Repository navigation
Add a process sidecar provisioner as the default backend (CL-6534) - #637
Merged
Merged
Conversation
…L-6534) Covers the provisioner's spawn env, idempotent ensure, generation fencing, destroy cleanup, and allocation-id validation against an injected process runner, plus the hub config's new default: an unconfigured install registers the process backend alone.
Adds @corbits/process-provisioner: a SidecarProvisioner whose ensure spawns one apps/sidecar process per allocation on the hub host, with the allocation's own token, hub WebSocket URL, and a per-generation data directory under HUB_DATA_DIR; destroy signals the process and removes that directory. Idempotence, generation fencing, and obsolete-unit sweeping come from @corbits/sandbox-sidecar's shared core, as they do for the docker and e2b backends; a unit is scoped by generation so a superseded process is still found and stopped after a hub restart. The hub registers it as the sole backend when SIDECAR_PROVISIONERS is unset, so an install that configures nothing provisions exclusive sidecars on a single server. An explicit list behaves exactly as before, with 'process' accepted as an id. Also wires the sidecar identity validator the router leaves defaulted to always-true: one createSidecarCredentialResolver now serves both seams, resolving a presented token at the handshake and revalidating that identity at the registration, readiness, and routing boundaries. Without it a provisioner-issued token stays accepted after its allocation was superseded or destroyed.
Adds docs/sidecar-provisioners.md as the one page comparing the shipped backends — process (default, same host), docker, e2b — with what each requires, where each keeps its hub-side state, and the three steps to add another. .env.example now documents the same set against SIDECAR_PROVISIONERS, and IMPLEMENTATION.md's deployment section points at it.
The workbench hub now always has a backend registered; the guard stays because this package is a library a host with none can still consume.
Contributor
Author
Self-reviewNo defects found; this is ready as-is. The process backend, its idempotence/generation-fencing tests, the identity wiring, and the docs all hold up under a close read.
Targeted tests:
Not run: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Step 1 of CL-6534.
Why
Workbench is going fully native on Interchange: the UI becomes an
Interchange client and the backend becomes mostly stock Interchange, with
custom backend machinery removed. Two constraints shape this step:
rig up and how to structure them.
on a single server — many chats and workflows on the same VPS.
Today an unconfigured hub registers no sidecar provisioner, so exclusive
sidecar placement fails closed until an operator sets up Docker or an E2B
account. That is the wrong default for a single-server install, and it is
also the shape the later steps (retiring folded runs) need in place first:
every run becomes an allocation, and an allocation needs a backend that
exists out of the box.
What this does
Adds
packages/process-provisioner— aSidecarProvisionerwhoseensurespawns oneapps/sidecarprocess per allocation on the hub's ownhost, mirroring the env
bun run devgives the local sidecar: the hubWebSocket URL, the allocation's provisioner-issued bearer token, its
sidecar id, and a per-allocation data directory under
HUB_DATA_DIR.destroysends SIGTERM (escalating to SIGKILL after a grace period) andremoves that directory.
It supplies only the OS-level unit. Idempotence, generation fencing,
destroy tombstones, and obsolete-unit sweeping come from
@corbits/sandbox-sidecar's shared core — the same coredockerande2buse. A unit is scoped by generation on disk
(
allocations/<id>/gen-<n>/sidecar.pid), which is what keeps the sweephonest across a hub restart: a new generation's pid never overwrites the
record of the unit it replaces, so a superseded process is still found and
stopped. Binding fingerprint is
process:v1:<sidecar entry>:<hub ws url>.Every OS failure path reports through
reportErrorwith the allocation idand returns a classified rejection carrying the
refId.Registers it as the default. With
SIDECAR_PROVISIONERSunset the hubregisters
processas its sole backend and default. With it set, behaviouris exactly as before — explicit ids, the
SIDECAR_DEFAULT_PROVISIONERrules, loud boot failures for an unknown/duplicate id or a missing backend
setting — with
processaccepted as an id like any other.Wires the sidecar identity validator.
createSidecarRouter'svalidateSidecarIdentitywas never supplied, so it fell back to therouter's always-true default. It is now the same
createSidecarCredentialResolver({ db })that authenticates the handshake:resolvefor the token,isCurrentfor the registration, readiness, androuting boundaries — exactly how upstream's own
apps/hub/src/server.tswires it. Without it a provisioner-issued token stays accepted after its
allocation was superseded or destroyed, which a process-provisioned sidecar
reaches easily: a child that outlives its allocation keeps reconnecting to
the same hub.
How to configure
docs/sidecar-provisioners.mdis the one page an operator reads: the threeshipped backends side by side (isolation, requirements, where each keeps
its hub-side state) and the three steps to add another.
.env.examplecarries the same set against
SIDECAR_PROVISIONERS, andIMPLEMENTATION.md's deployment section points at the doc.
Verification
packages/process-provisionerunit suite: spawn injected, so no realprocess starts. Asserts the spawned env, idempotent ensure, stale
generation rejection, a newer generation superseding and stopping the
old process, spawn failure classification, destroy cleanup and
tombstone fencing, and allocation-id validation.
apps/hub/test/config.test.tscovers the new default,processas anexplicit id, its overrides, and that an explicit list without
processdoes not get it.
bun run typecheck,bun run lint,bun run check:structural: green.apps/hub/test/composition.test.ts,packages/sidecar-placement): green — the hub boots with the new defaultbackend and the identity validator wired.
sidecar back, so the live check is the browser walkthrough:
bun run dev, thenbun run scripts/e2e/browser/walkthrough.ts.Notes
Upstream's
SidecarProvisionerat the #632 target pin (692c3106) adds acapabilities: readonly SidecarCapabilityDeclaration[]member and anoptional-
externalRefclarification that main's vendored contract does nothave. This provisioner is built against what main has; declaring
capabilities lands with the #632 re-pin, alongside the same addition for
the docker and e2b backends. Nothing is shimmed for it here.
Part of CL-6534