Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 5 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ sandboxd](docs/deploy.md) covers release binaries, the full config and systemd.
newline-JSON frames over vsock 2048 ([reference](docs/silkd.md))
- `sandboxd/` — per-node control plane (Go): warm pools, the claim API and the
relays to silkd ([deploy](docs/deploy.md), [API](docs/sandboxd-api.md))
- `sdk/go/` — [Go SDK](docs/sdk.md); `silkd/` is its connection layer and
`silkd/silkdtest/` a test fake
- `sdk/go/` — [Go SDK](docs/sdk.md); `sdk/go/silkd/` is its connection layer
and `sdk/go/silkd/silkdtest/` a test fake
- `sdk/python/` — [Python SDK](docs/sdk-python.md), stdlib-only and synchronous
- `mcp/` — `sandbox-mcp`, an [MCP](docs/mcp.md) stdio server
- `sdk/openai/` — `cocoonstack-sandbox-openai`, a sandbox provider for the
Expand Down Expand Up @@ -112,7 +112,8 @@ sandboxd](docs/deploy.md) covers release binaries, the full config and systemd.
changed carriers (via `build-boot.yml` / `build-silkd.yml`,
`workflow_call`) then os-images, in order, so the chain is deterministic
- `build-os-images.yml` — bakes base + flavors FROM the sha-pinned carriers
- `release.yml` — on a version tag, builds the release binaries and archives
- `release.yml` — on a version tag, checks the tag against silkd's version,
then builds the release binaries and archives
- `publish-pypi.yml` — on an `sdk-*-v*` tag, builds and publishes the
matching package via PyPI Trusted Publishing (OIDC, per-package
environment)
Expand Down Expand Up @@ -142,20 +143,12 @@ make go-lint # Go: protocol/wire + sandboxd + sdk/go + e2e + mcp, GOOS lin
make go-test # Go: go test -race across the Go modules
make sh-lint # shellcheck every tracked shell script
make sandboxd # build dist/sandboxd
make bench # claim-tier + data-plane benchmarks on this node
make bench # claim-tier + data-plane benchmarks; removes every sbx-* VM
make boot # kernel + initramfs artifact image (docker)
# KERNEL_MIRROR=… if kernel.org tarball paths 404 locally
make silkd-image # silkd release binary in a scratch carrier image
make images # base + python images against the local boot + silkd images
```

The parent workspace's `go.work` includes these modules, which is why the
Makefile forces `GOWORK=off`: CI has no workspace, and a workspace build
resolves sibling checkouts instead of the pinned versions. silkd's integration
tests spawn real processes — run them in a Linux container too (`docker run
rust:1 … cargo test`) before touching platform-sensitive paths; macOS green
alone has hidden Linux-only breakage before.

## License

The server stack — sandboxd, silkd, the boot chain, the OS images, and the
Expand Down
16 changes: 9 additions & 7 deletions docs/cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,17 +175,18 @@ An archived claim keeps only a pointer on the node; with
`meta_store` with a `cell`, an `s3` `checkpoint_store` and a `mesh`), the
archive also writes one row to the shared PostgreSQL, scoped by the cell: id,
tenant, token SHA-256, owner node, epoch, a consumed flag, checkpoint id,
retention deadline and the claim record without its token. Only archives and
replication rounds write it, so the claim path is unchanged.
retention deadline and the claim record without its token. Archives,
replication rounds, and env writes and renews of a claim whose row must follow
it while it sleeps write it, so the claim path is unchanged.

- **Adoption.** `Lookup` asks the entry node, then every peer. A node that does
not hold the claim reads the row; when the token hash matches, it takes the
row with one compare-and-set on the epoch. The same statement requires the
owner's [node lease](#node-lease) to be expired, unless the owner is this
node (with its record gone). The node records the claim as archived and
answers as the owner. Two nodes adopting at once leave one owner; the other
answers 404. A tombstone is never adopted. The next exec or file call wakes
the claim from the store as usual. An owner with a live lease answers for
answers 404. A tombstone is never adopted. The next call that reaches the
guest wakes the claim from the store as usual. An owner with a live lease answers for
itself, and a node that tries to take its row is refused with a 404.
A draining node never adopts, nor does a fenced node or one that lifted its
fence less than 8 seconds ago (one renewal interval plus one registry query),
Expand Down Expand Up @@ -470,8 +471,8 @@ at a node that does not hold the record runs a tier order:
`Checkpoint.Delete` (`DELETE /v1/checkpoints/{id}`) removes the local
record, then best-effort broadcasts the delete to every peer this node
currently sees, so a replica a heal pulled earlier is cleaned up too, not
just the original. A peer that is offline or partitioned during that
broadcast keeps its copy until the checkpoint TTL ages it out. A healed
just the original. A peer that is offline, partitioned or not yet joined during
that broadcast keeps its copy until the checkpoint TTL ages it out. A healed
replica carries the source checkpoint's original `CreatedAt`, so it becomes
eligible for expiry at the same instant everywhere; each node then removes
it on its own hourly sweep, which is independently phased and retries on
Expand All @@ -482,7 +483,8 @@ sweep failure extends retention until one succeeds — the TTL is the eligibilit
point, not a hard ceiling. This is why heal *requires* a nonzero, fleet-matching
TTL (see [cluster-invariant config](#cluster-invariant-config)).
With TTL disabled it could never close at all, so that combination is
rejected at config load.
rejected at config load. A node later run with healing off and that TTL back at
0 keeps such a replica until an explicit delete.

## State ownership

Expand Down
Loading
Loading