deploy/solver-tunnel/README.md

Persistent Hetzner → Graviton5 relay transport

Deployed on 2026-09-09. The enabled attested-relay-origin-tunnel.service runs on Hetzner 178.105.23.35, forwarding 127.0.0.1:29081 to the Graviton5 parent's 127.0.0.1:8080 over SSH to 34.220.57.13. The mirror and solver fleet use http://127.0.0.1:29081; their operation no longer depends on the operator laptop. Public Cloudflare HTTPS remains a separate pending deployment.

The enclave was not restarted. The operator's existing SSH access and security group rule were retained. The only new ingress is TCP22 from 178.105.23.35/32, rule sgr-0c25d74e286c1c662 in sg-0ffa617e635151935 (instance i-0de9795ee9d3ce090, us-west-2).

Key and account boundaries

The new Ed25519 private key was generated on Hetzner at /var/lib/relay-origin-tunnel/id_ed25519, owned by the new unprivileged relay-origin-tunnel user, mode0600 under a mode0700 home. The private key never left Hetzner. Only its public key was installed on AWS and retained as deployment evidence. Neither the operator AWS SSH key nor any account credential was copied to Hetzner for this connection.

The AWS host key was copied from the already established local .local/graviton5-known-hosts into the dedicated Hetzner configuration directory. StrictHostKeyChecking=yes, UpdateHostKeys=no, and an empty global host-key file prevent implicit trust changes. A parent host-key change requires explicit verification and a reviewed configuration edit.

On AWS, a new relay-forward system account has a nologin shell and exactly one authorized key. Its key options restrict the source to the Hetzner /32, force /bin/false, disable PTY/agent/X11/user-rc, and permit only the local forwarding destination 127.0.0.1:8080.

The dedicated sshd Match User policy independently permits local TCP forwarding only, restricts its destination, disables all remote listeners and Unix-socket forwarding, and sets MaxSessions 0 to prohibit shell/login/SFTP channels. It also disables EC2 Instance Connect key lookup for this account. Key options alone would not sufficiently restrict reverse forwards after re-enabling port-forwarding; the server policy supplies that restriction.

The policy is included at the end of /etc/ssh/sshd_config, rather than through its early global include directory. This prevents a Match block from accidentally encompassing later global directives. Installation retained a candidate snapshot, ran sshd -t, compared the complete effective ec2-user policy before/after (identical), checked every tunnel restriction using sshd -T -C, and then reloaded sshd. Existing sessions were retained.

OpenSSH's primary documentation describes authorized-key restrictions and server forwarding/session controls.

Installed files and replay

install-client.py runs as root on Hetzner with a JSON stdin object containing known_hosts (the established public host-key file), ssh_config and unit (the corresponding files in this directory). It creates the new local account, generates the key only if absent, installs files only when new or byte-identical, validates systemd/OpenSSH syntax, and returns only the public key. It does not start the service.

install-parent.py runs as root on AWS with a JSON stdin object containing public_key and policy (the public key and aws-sshd-match.conf). It creates the dedicated account and checked policy, appends only its own include, and validates before reloading sshd. Existing differing deployment files cause an error. Scripts do not delete files. Do not invoke them with secret private-key data or mix an unrelated account into this deployment.

After authorization and the narrow SG rule were installed, the client was enabled using:

systemctl daemon-reload
systemctl enable --now attested-relay-origin-tunnel.service

The systemd unit restarts dropped SSH connections automatically. SSH has strict batch authentication, a 10-second connect timeout, keepalive failure detection, and ExitOnForwardFailure=yes. It binds only loopback. It cannot request a shell or forward an agent. The separate mirror/solver units are documented in ../solver-fleet/.

To stop this transport without deleting files, stop and disable only attested-relay-origin-tunnel.service. This leaves existing enclave and operator services running. Mirror/solver polls will then fail until transport is restored or their configuration is deliberately changed.

Evidence and health

verify-hetzner.py can run as root on Hetzner. Its negative checks create only short-lived dedicated SSH connections and do not interrupt the persistent tunnel. The Unix-socket channel reports a generic rejection; effective sshd policy validation separately establishes that Unix-socket forwarding is disabled.

Useful read-only checks on Hetzner:

systemctl show attested-relay-origin-tunnel.service -p ActiveState -p NRestarts -p MainPID
ss -ltn 'sport = :29081'

The fleet independently verified fresh production warming attestation through this connection, including the actual inner TLS key, hardware policy and PCR0:

c743aa2259fa59575de56c0c1e11f8eb5c40e95af991d433af87dc4b04797fc77b73e1ac8777d2b1ba98c900ae1820aa

At first verification the artifact index was empty because the production enclave was warming. The fleet mirror/solver were enabled after that verified attestation, with mirror polling healthy and the solver idle awaiting a puzzle.