Repository navigation
v0.21.0 — login state in Postgres, graceful relay drain
Relay scale-out, round two: login state in Postgres, graceful drain, honest edge metrics
Three children of the scale-out epic (#524) land together. api.<apex> is no longer pinned to one relay, a relay restart no longer yanks its tunnels mid-request, and the edge's connection counters now add up.
What's new
- Login-flow state in Postgres (#522, PR #529) — dashboard web login, brokered CLI login, the GitHub device flow and the login rate limit each live in a shared table (
login_web_states,login_cli_handles,login_device_flows,login_rate). Any relay can answer any step, sopiper-edgenow round-robinsapi.<apex>across live relays in start order. The device flow has no background poller any more: the CLI poll makes the upstream call, so a relay dying mid-login strands nothing. The rate limit is one budget across the pool (30/min per client), not one per process. No secret is at rest in a login table. Wire shapes of/v1/login/*are unchanged. - Graceful drain on SIGTERM (#523, PR #533) — a stopping relay marks its pool row
draining, which the edge reads from the heartbeat: no new tunnel placement and noapi.<apex>traffic go there, while hostnames it owns keep routing to it. It refuses new:7000connections (the listener stays open so the edge does not evict it), closes each agent session the moment it has no open streams, force-closes the rest after 20 s, then leaves the pool and parks in-flight webhooks for the new owner. Worst case 60 s from signal to exit. Agents are untouched; they still see one reconnect. Zero-drop rollouts come with #530 (two sessions per agent). - Edge metrics decompose (#526, PR #528) — new
piper_edge_conns_dropped_total{listener}counts connections that close without a usable SNI or Host (background scanners), and a dial to a dead owner on:443/:80now counts as unrouted.accepted = routed + unrouted + droppedholds on every listener.
Upgrading
This release changes the relay schema in place (pre-1.x, no migrations). Agents need nothing; the tunnel wire protocol is unchanged.
- Drop the two pool tables before starting the new binaries — mandatory:
Both are ephemeral (heartbeats and ownership; refilled within seconds).
DROP TABLE agent_owners, relay_instances;
relay_instancesgained adrainingcolumn, and bothpiper-relayandpiper-edgeapply the sameschema.sqlwithCREATE TABLE IF NOT EXISTS, so whichever new process starts first re-creates them. Skipping the drop breaks every new relay's heartbeat. The fourlogin_*tables are new and need nothing. - Roll relays first, edge last. An upgraded edge round-robins
api.<apex>; a relay still on v0.20.0 holds login state in memory, so logins would fail until every relay is on v0.21.0. Stagger relay restarts on this first roll so two relays don't race the sameCREATE TABLE. - Give relays a 60 s stop timeout. Compose:
stop_grace_period: 60son the relay service (the repo'sdeploy/compose/relay/docker-compose.ymlhas it). ECSstopTimeout, KubernetesterminationGracePeriodSeconds. systemd's default 90 s already covers it. - Relays, then the edge. Compose recreates both relay replicas together (
COMPOSE_PARALLEL_LIMIT=1does not serialise them, see #535), so every agent redials once:docker compose up -d --no-deps --scale relay=2 relay docker compose up -d --no-deps edge
Expect this first roll to drop tunnels once anyway: the relays being stopped are v0.20.0 and do not drain. From here on a relay restart drains. Verified on the hosted relay on 2026-09-05.
Runbook: docs/runbooks/relay-deploy.md — "Upgrade across a schema change", "Scale out" and "Single host with compose".
Known limits
- A drained relay's agents still reconnect once (~1 s backoff). Redundant sessions for zero-drop rollouts are #530; zone-aware placement is #531.
piper-edgeitself stops abruptly on SIGTERM; readiness flip and splice grace are #534.- Only one SIGTERM is honoured; a second during the drain is ignored (the orchestrator's SIGKILL at the stop timeout still applies).
Specs: docs/superpowers/specs/2026-09-04-login-state-postgres-design.md, docs/superpowers/specs/2026-09-05-relay-graceful-drain-design.md, prior art docs/superpowers/specs/2026-09-05-tunnel-placement-prior-art.md.