feat(workspace): remote shared backend — run the shared stack on a remote Docker host (spec 21) - #107
Merged
Merged
Conversation
…mote Docker host (spec 21) Generalize the shared stack to run on a remote Docker host (an SSH/TCP `docker context` or a DOCKER_HOST endpoint) instead of only the local daemon. Ref-counting, provisioning, and DNS/alias are backend-agnostic; this swaps WHERE containers run. Default is local, byte-for-byte unchanged. - internal/docker: a Backend seam (ComposeEnv/Reachability/NewClient/ RemoteReachable). Local = zero value (unchanged). Remote binds moby to the resolved endpoint and keys the ledger by the context name/host so remote rows never bleed into local counts, like the WSL2 Desktop-vs-dockerd split. - internal/docker: Compose gains ContextEnv, appended after the secret Env, pinning every compose verb to the endpoint (DOCKER_HOST/DOCKER_CONTEXT). - internal/config: a workspace `backend:` selector (+ store global default); validated (dockerhost scheme; context XOR host). - orchestrate/up + cli/up: thread the backend into the client, ledger key, and every compose driver. Remote host-side Postgres provisioning is guarded with a clear error (scoped follow-up — needs an SSH tunnel). - doctor: a backend.remote reachability probe with remediation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gustavobertoi
force-pushed
the
feat/remote-backend
branch
from
July 1, 2026 16:49
82a85f0 to
07f6010
Compare
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.
What & why
Generalizes the shared stack (spec 21) so it can run on a remote Docker host — an SSH/TCP
docker contextor aDOCKER_HOSTendpoint — instead of only the local daemon. The ref-counting, provisioning, and DNS/alias model are backend-agnostic; this only swaps where the containers run and how host-side consumers reach them. The local backend is the default and byte-for-byte unchanged.Delivered (tested core)
backend:block (context:orhost:), plus a machine-global default on the storeconfig.yaml. Validated: adockerhostscheme rule (ssh|tcp|unix|npipe|fd://) and a cross-field check thatcontextandhostare mutually exclusive.nil= local. (internal/config,internal/store)docker.Backend(ComposeEnv/Reachability/NewClient/RemoteReachable). The local backend is the zero value and defers to the existingNewClient(so context-name resolution / ledger keying is unchanged). A remote backend binds moby to the resolved endpoint (WithHost+ version negotiation; adocker contextendpoint is resolved viadocker context inspect) and keys the ledger by the context name / host, so remote rows never bleed into the local counts — exactly like the WSL2 Desktop-vs-dockerd split.ComposegainsContextEnv, appended after the resolved-secretEnv, pinning every compose verb to the endpoint viaDOCKER_HOST/DOCKER_CONTEXT(never written to disk).cli/up.go buildUpDepsresolves the backend, builds the read-only client from it, keysstate.Openby the backend context, and threads it throughUpDeps.Backendinto the shared-phase, project, and down composes.doctortargets the selected endpoint too.--no-provision, or provision from the remote host). Remoteupof the non-provisioned shared stack (redis/minio, or postgres with no host-provisioned consumer) works.backend.remotereachability probe (Ping + ServerVersion) with a remediation; only emitted for a remote backend (the local daemon is already covered by the existing preflight).Scoped out (flagged follow-ups — this is a frontier spec)
flockcannot serialize two developers on two machines against the same remote ledger. TheDistLock(recommended: a Postgres advisory lock on the cluster DB) is not implemented; this pass is the single-user, local-flock, all-remote-topology sketch the spec calls ~3–4w.u_<user>_<project>roles,REVOKE … FROM PUBLIC, atenantledger column).These are documented in code doc-comments (
internal/docker/backend.go) and gate on a daemon decision (Q-DAEMON).How tested
mock docker.Client+ a fake compose runner, no real remote:internal/docker—IsRemote/Reachability/ComposeEnv/String;ContextEnvthreads through the runner after secrets; local adds nothing;RemoteReachableok/fail/nil-client.internal/config— backend default-local, context, host, bad scheme, mutual exclusion, valid schemes.internal/orchestrate— remoteuppins every compose verb (shared + project) toDOCKER_CONTEXT; local leaks no backend env; the remote host-provision guard fails the shared phase with an actionable message.internal/cli— thedoctorbackend.remoteprobe (reachable / unreachable / local-absent).CGO_ENABLED=0 go build ./...,CGO_ENABLED=1 go test ./internal/...,gofmt -l(clean),go vet ./..., and a darwin/arm64 cross-build all pass. No new dependencies.🤖 Generated with Claude Code