Skip to content

Tailnet join follows the provider, not the service - #9

Merged
czpython merged 1 commit into
mainfrom
commonzenpython/eng-857-tailnet-per-provider
Aug 17, 2026
Merged

Tailnet join follows the provider, not the service#9
czpython merged 1 commit into
mainfrom
commonzenpython/eng-857-tailnet-per-provider

Conversation

@czpython

Copy link
Copy Markdown
Owner

TAILSCALE_ENABLED is service-global, and provision() read it as "every host joins the tailnet": join credentials, bootstrap script, internal_ssh_host, device discovery. A docker host is a local container with no path onto the tailnet, so the docker provider failed fast on the script — a tailnet-mode drukbox could not serve docker hosts at all. One box serving remote agent VMs over the tailnet plus local browser containers (the druks browser-session flow requests provider=docker per borrow) needs exactly that.

Providers now declare supports_tailnet beside the other per-provider traits (supports_instance_type, supports_disk_gb); docker leaves it False, the VM providers inherit True. provision() binds the tailscale client once — tailscale = self.tailscale if get_vm_provider(host.provider).supports_tailnet else None — and its three tailnet blocks key on that local, so a docker host under a tailnet-mode service skips the join entirely: no credentials minted, no setup script, no internal_ssh_host, no discovery wait. The keyscan and teardown paths already follow the host's own fields and need no change. The docker provider's fail-fast on a setup script stays as the backstop for a mis-flagged provider.

Nothing widens: docker containers still publish sshd on 127.0.0.1 only, reachable from the box alone. Docs updated (deploy.md docker section, networking.md two-modes intro). One service-level test beside the tailscale-off tests: tailnet-mode service + docker host stays local end to end.

ENG-857

TAILSCALE_ENABLED is service-global, and provision() read it as every
host joins: join credentials, bootstrap script, internal_ssh_host,
device discovery. A docker host is a local container with no path onto
the tailnet, so the docker provider failed fast — a tailnet-mode drukbox
could not serve docker hosts at all, and one box serving remote agent
VMs over the tailnet plus local browser containers needs exactly that.

Providers now declare supports_tailnet beside the other per-provider
traits; docker leaves it False. provision() binds the tailscale client
only for providers whose hosts can join, so a docker host under a
tailnet-mode service skips the join entirely and keeps its published
127.0.0.1 port as the only path. The docker provider's fail-fast on a
setup script stays as the backstop.

ENG-857
@czpython
czpython force-pushed the commonzenpython/eng-857-tailnet-per-provider branch from 4f0ccc8 to 94fe4fc Compare August 17, 2026 10:29
@czpython
czpython merged commit b37173b into main Aug 17, 2026
6 checks passed
czpython added a commit that referenced this pull request Aug 18, 2026
Review fixes: declare supports_tailnet=False (base gained the flag in #9
after this branch was drafted — without it a tailnet-mode service minted
and leaked a Tailscale auth key per create), stop the template entrypoint
truncating authorized_keys on restart, anchor the not-found stderr match
to the CLI's own message so an auth failure can't be read as sandbox-gone
and destroy live state, translate workspace mkdir errors at the provider
boundary, attempt sandbox removal when create fails partway, bracket IPv6
publish specs, honor ssh_username in the bootstrap script, bound sbx
calls at 600s, and move workspace rmtree off the event loop.

Test prunes: drop config-mirroring and implementation-detail tests, merge
the port-parse and binary-launch variants, move diagnose coverage to
test_diagnose.py per docs/add-a-provider.md. Docs: networking.md and
security.md now name the provider; deploy.md notes the missing
SERVICE_LABEL mechanism.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant