KeyTurn handover record
Generated 2026-09-27T04:50:25.006224Z · proofs run 2026-09-27T04:50:18.670655Z · method: same user · mastodon → mastodon · changes made by: scripted operator (demo, not Bob) (as stated) · KeyTurn 0.2.1, code 3e7422489d937075
Current state: VERIFIED
The three checks
1. Customers work on the new credential — PASS
- ✓ a login role holds the new password role: mastodon (method: same user)
- ✓ sidekiq runs on the new credential status=running user=mastodon password=new
- ✓ streaming runs on the new credential status=running user=mastodon password=new
- ✓ web runs on the new credential status=running user=mastodon password=new
- ✓ customer journeys pass 3/3
2. The old credential is refused at Postgres and PgBouncer; no old sessions — PASS
- ✓ old credential refused at Postgres AUTH_REJECTED
- ✓ old credential refused at PgBouncer CLIENT_AUTH_REJECTED
- ✓ no session authenticated with the old credential is left postgres: old 0, unclear 0; pgbouncer_clients: old 0, unclear 0; pgbouncer_servers: old 0, unclear 0
3. App recreate, PgBouncer reconnect and restart, migration path and a delayed job all passed — PASS
- ✓ recreate_apps apps recreated from workspace files; journeys 3/3; running config matches files: True
- ✓ reconnect PgBouncer RECONNECT rc=0; journeys 3/3
- ✓ restart_pgbouncer restart pgbouncer rc=0; journeys 3/3
- ✓ migration_path rc=0 up-migrations=615
- ✓ delayed_job queued before the handover, published at 2026-09-27 04:49:18.55748; old credential retired in between: True
Customer journeys sampled during the change (observation, not a proof)
10 customer journeys sampled from 2026-09-27T04:43:11.109894Z to 2026-09-27T04:43:57.795284Z (watch run w20260927044310-dd5aef); 3 failed during the change, 0 during KeyTurn's own restart tests. Failures during the change at: 2026-09-27T04:43:36.468563Z, 2026-09-27T04:43:41.488475Z, 2026-09-27T04:43:46.490000Z. customer journeys sampled about every 5 s while the watch ran; gaps between samples are not observed, so this is evidence, not a zero-downtime guarantee. Failures inside KeyTurn's own restart tests (proof 3 deliberately restarts things) are counted separately.
Where the credential lived
no inventory was supplied in this run (keyturn/inventory.json is absent)
Operations KeyTurn ran
| step | ok | detail | at |
|---|
| recreate_apps | True | apps recreated from workspace files; journeys 3/3; running config matches files: True | 2026-09-27T04:44:35.411031Z |
| reconnect | True | PgBouncer RECONNECT rc=0; journeys 3/3 | 2026-09-27T04:44:52.861376Z |
| restart_pgbouncer | True | restart pgbouncer rc=0; journeys 3/3 | 2026-09-27T04:45:21.241458Z |
| migration_path | True | rc=0 up-migrations=615 | 2026-09-27T04:45:29.649846Z |
| delayed_job | True | queued before the handover, published at 2026-09-27 04:49:18.55748; old credential retired in between: True | 2026-09-27T04:49:21.989968Z |
Steps KeyTurn's gate refused
| at | kind | reason |
|---|
| 2026-09-27T04:42:55.579029Z | activate | restarts or recreates services |
| 2026-09-27T04:43:01.182866Z | change_old_password | changes the old user's password in place |
Coverage gaps
not stated (no gaps field in the inventory)
Known limits of KeyTurn
- One supported deployment shape: apps -> PgBouncer (auth_file) -> Postgres, in Docker.
- The gate covers listed command forms only; script files, Python/Ruby file writes, podman and interactive psql are not gated (the proofs still judge the end state).
- The transition watch samples about every 5 s; gaps between samples are not observed. No zero-downtime claim.
- The Stop-hook done-check records a verdict after the agent's final message; it cannot block the word 'done'.
No passwords are stored in this record. Raw logs stay private in KeyTurn's state folder.