Delegation sweep drops a pending begin, so the key rotates without a restart #114

Closed
opened 2026-09-02 08:16:01 +00:00 by a73x · 1 comment
a73x commented 2026-09-02 08:16:01 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Observed on the local plane: delegate_begin returned key A, the human signed it ~15 minutes later, delegate_complete refused with "that certificate is over A, but this tenant's current delegation key is B — the key changes when the control plane restarts". The server had been up 2.5 hours. A second begin+sign completed within a minute worked.

Cause: delegation.Keyring.Sweep deletes every entry with e.cert == nil ("not doing anything"), and boot runs it every delegationSweepInterval (10 min). Its own comment says a caller "who takes a few minutes between Begin and Complete is not raced" — false when Begin lands just before a tick; the window is 0–10 minutes depending on phase, and the docs (docs/mcp.md, Begin's doc, and Complete's refusal text) all promise the key is stable for the life of the process.

Fix direction: Sweep must not drop a pending entry on the next tick. Either keep pending entries for a bounded grace (e.g. an hour from Begin, tracked on the entry) or never sweep pending entries at all (one keypair, ~100 bytes, bounded by tenants that ever called Begin — the comment already argues that bound is fine). Test: Begin, Sweep, Begin returns the same key. The refusal text is right once the promise is true.


Migrated from git-collab issue 652e88443c3cd3d10974f1fc2eb806ecf1197560.

Original author: a73x dev@a73x.sh.

Close reason: merged in patch 1d5ab576

[claude 2026-09-02] Observed on the local plane: delegate_begin returned key A, the human signed it ~15 minutes later, delegate_complete refused with "that certificate is over A, but this tenant's current delegation key is B — the key changes when the control plane restarts". The server had been up 2.5 hours. A second begin+sign completed within a minute worked. Cause: delegation.Keyring.Sweep deletes every entry with e.cert == nil ("not doing anything"), and boot runs it every delegationSweepInterval (10 min). Its own comment says a caller "who takes a few minutes between Begin and Complete is not raced" — false when Begin lands just before a tick; the window is 0–10 minutes depending on phase, and the docs (docs/mcp.md, Begin's doc, and Complete's refusal text) all promise the key is stable for the life of the process. Fix direction: Sweep must not drop a pending entry on the next tick. Either keep pending entries for a bounded grace (e.g. an hour from Begin, tracked on the entry) or never sweep pending entries at all (one keypair, ~100 bytes, bounded by tenants that ever called Begin — the comment already argues that bound is fine). Test: Begin, Sweep, Begin returns the same key. The refusal text is right once the promise is true. --- Migrated from git-collab issue `652e88443c3cd3d10974f1fc2eb806ecf1197560`. Original author: a73x <dev@a73x.sh>. Close reason: merged in patch 1d5ab576
a73x commented 2026-09-02 08:21:43 +00:00 (Migrated from git.a73x.sh)

[claude 2026-09-02] Picking this up on branch fix/* in a worktree; Opus subagent implements under TDD, boot gate before merge.

[claude 2026-09-02] Picking this up on branch fix/* in a worktree; Opus subagent implements under TDD, boot gate before merge.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
a73x/eitri#114
No description provided.