Problem
drukbox's contract assumes each host runs an SSH server that callers dial directly. Docker Sandboxes (sbx) does not work that way: its data plane is a session API, not an open port.
Findings from the docker-sbx deployment tests (#13):
- The daemon stops a sandbox approximately 30 seconds after its last
sbx session ends.
- Connections to published ports do not count as sessions, and a caller cannot wake a stopped sandbox.
- Published port bindings change across a stop/wake cycle, thus stored port coordinates go stale.
Decision: the drukbox-gateway process
A new process in this repo, run the same way as the janitor and the pool: same codebase, same settings, direct database access, own systemd unit.
- Callers connect with normal SSH and the per-host key:
ssh <host-name>@<gateway>.
- The gateway authenticates the presented key against the stored per-host public key, resolves the host, and asks the host's provider to dial a session.
- For
docker-sbx, the dial is an sbx exec session. The connection is then a real daemon session: a stopped sandbox wakes on connect (~6 s, measured), stays awake while connected, and sleeps after disconnect. The auto-stop becomes scale-to-zero.
- Classic VM providers (
exe, aws, hetzner, exoscale, docker) keep the direct path. No gateway.
Prior art that shaped the decision: ContainerSSH (terminate SSH once, authenticate externally, dispatch to a per-connection backend), and the vendor gateways that clients such as Crabbox consume (Tenki ssh-proxy, Morph's shared gateway). Session-API products (e2b, Modal) expose no SSH at all — the service in front of them must provide the SSH face.
Design
- Auth: a new
hosts.public_key column stores the public half of the per-host keypair (the private half stays return-once, never persisted). The key is the identity; the username must match the host name as a second check.
- Provider seam: a
SessionCapability ABC in providers/capabilities.py, the same pattern as HttpProxyCapability. A provider that implements it can be dialed by the gateway. docker-sbx is the first implementation.
- Contract: for hosts of a session provider, the API response carries the gateway address and port in the existing fields (
external_ssh_host, external_ssh_port, ssh_username = host name). scan_known_hosts works unchanged and pins the gateway's host key.
- Server: asyncssh,
python -m gateway.server. Interactive shell and command execution only. SFTP and port forwarding are refused.
- Placement: for
docker-sbx the gateway runs on the sandboxd machine.
Scope
- M1: migration,
SessionCapability, the gateway process, the docker-sbx dialer, tests.
- M2: deploy documentation, systemd unit, end-to-end verification on a real host.
- Not in v1: SFTP, scp, port forwarding, multiple gateways.
Related
Problem
drukbox's contract assumes each host runs an SSH server that callers dial directly. Docker Sandboxes (
sbx) does not work that way: its data plane is a session API, not an open port.Findings from the
docker-sbxdeployment tests (#13):sbxsession ends.Decision: the drukbox-gateway process
A new process in this repo, run the same way as the janitor and the pool: same codebase, same settings, direct database access, own systemd unit.
ssh <host-name>@<gateway>.docker-sbx, the dial is ansbx execsession. The connection is then a real daemon session: a stopped sandbox wakes on connect (~6 s, measured), stays awake while connected, and sleeps after disconnect. The auto-stop becomes scale-to-zero.exe,aws,hetzner,exoscale,docker) keep the direct path. No gateway.Prior art that shaped the decision: ContainerSSH (terminate SSH once, authenticate externally, dispatch to a per-connection backend), and the vendor gateways that clients such as Crabbox consume (Tenki
ssh-proxy, Morph's shared gateway). Session-API products (e2b, Modal) expose no SSH at all — the service in front of them must provide the SSH face.Design
hosts.public_keycolumn stores the public half of the per-host keypair (the private half stays return-once, never persisted). The key is the identity; the username must match the host name as a second check.SessionCapabilityABC inproviders/capabilities.py, the same pattern asHttpProxyCapability. A provider that implements it can be dialed by the gateway.docker-sbxis the first implementation.external_ssh_host,external_ssh_port,ssh_username= host name).scan_known_hostsworks unchanged and pins the gateway's host key.python -m gateway.server. Interactive shell and command execution only. SFTP and port forwarding are refused.docker-sbxthe gateway runs on the sandboxd machine.Scope
SessionCapability, the gateway process, thedocker-sbxdialer, tests.Related