fix(cli): cotal mint reuses the existing identity unless --force#7
fix(cli): cotal mint reuses the existing identity unless --force#7sealedsecurity-bot wants to merge 4 commits into
Conversation
mint.ts unconditionally called newIdentity() on every mint, so re-minting an agent (e.g. to refresh its channels from the persona file) rotated the mesh id. The durable ACL row and the dm/dlv delivery durables are all keyed by the nkey public key, so rotation orphaned them → the agent went @mention-wake-blind with 'consumer not found' until re-provisioned (hit live 6x during a fleet relaunch). Option B (maintainer ruling on design PR #3, cubic P1): re-mint reuses the same identity. New core helper identityFromCreds(creds): Identity — the id-preserving sibling of idFromCreds (which it reuses for the id + JWT-subject cross-check), returning { id, seed } from the creds' seed block. mint computes the out path before minting; if a creds file exists there and --force is not passed, it re-signs that SAME id+seed with fresh ACLs; otherwise it mints a new identity. --force keeps the rotation escape hatch (compromised key / deliberate new id). Orthogonal to the still-open mint-strategy fork (#3): changes WHICH id mint uses, not WHERE the ACL write triggers. Pairs with the PR #4 DLV-durable fix. Refs #3. Co-Authored-By: seal <noreply@sealedsecurity.com>
|
Warning Review limit reached
Next review available in: 35 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe mint CLI now reuses an identity from an existing output creds file for the agent profile when ChangesMint identity reuse
Estimated code review effort: 2 (Simple) | ~10 minutes Sequence Diagram(s)sequenceDiagram
participant CLI as mint command
participant FS as Filesystem
participant Core as identity.ts
CLI->>FS: check if creds file exists at output path
alt creds exist and not --force
CLI->>FS: readFileSync(existing creds)
CLI->>Core: identityFromCreds(creds)
Core-->>CLI: reused Identity {id, seed}
else no creds or --force
CLI->>Core: newIdentity()
Core-->>CLI: new Identity
end
CLI->>CLI: mint creds with selected identity
CLI->>CLI: log id (reused vs new)
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR makes agent re-minting keep the same identity by default.
Confidence Score: 5/5This looks safe to merge.
Important Files Changed
Reviews (4): Last reviewed commit: "test(cli): assert corrupt-creds mint lea..." | Re-trigger Greptile |
There was a problem hiding this comment.
All reported issues were addressed across 2 files
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
Fix all with cubic | Re-trigger cubic
…rseable creds Two review findings on the reuse logic (cubic P1 + greptile P2 on #7): - Reuse now requires profile === "agent". The durable-ACL-orphan rationale is agent-specific (persona-refresh workflow + dm/dlv durables keyed to the id); observer/admin creds have neither, and silently preserving a privileged admin key across re-mints would extend its lifetime unexpectedly. Observer/admin now always rotate, as before. - A present-but-unparseable creds file (empty, truncated, not a user creds file) now fails loud with an actionable error naming --force, instead of letting identityFromCreds throw a raw parse exception. Silently rotating there would orphan the durable row the existing id may still own, so fresh-mint is not a safe fallback — the operator must opt in via --force or remove the stale file. Verified: agent re-mint keeps id; admin/observer re-mint rotate; corrupt creds at the out path errors naming --force (no raw crash, no silent rotate). Refs #3. Co-Authored-By: seal <noreply@sealedsecurity.com>
Layer 1 — packages/core/smoke/identity.smoke.ts (offline, 4 asserts): identityFromCreds
round-trips {id, seed} unchanged; agrees with idFromCreds (one-id-everywhere); rejects
spliced creds (seed vs foreign JWT subject) and a missing seed block.
Layer 2 — implementations/cli/smoke/mint-reuse.smoke.ts (hermetic, exercises the real
mint() against a tmp .cotal root, 8 checks): re-minting an agent reuses the SAME id;
--force rotates; a persona ACL change refreshes the baked sub.allow WITHOUT rotating the
id; first mint of a new name mints fresh (absent-creds path, no crash); observer + admin
re-mint rotate (reuse is agent-only); an unparseable existing creds file fails loud naming
--force (no silent rotate, no raw crash).
Red-green verified: reverting the reuse block to unconditional newIdentity() fails checks
1, 3, and 6 (two mints → two ids; the exact #3 cubic-P1 durable-orphan bug); the fix turns
them green. Registered both as smoke:identity + smoke:mint-reuse and wired into smoke:ci.
Refs #3.
Co-Authored-By: seal <noreply@sealedsecurity.com>
There was a problem hiding this comment.
All reported issues were addressed across 3 files (changes from recent commits).
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
cubic P2 on #7: the corrupt-creds check only asserted the error message, so a future mint() refactor that wrote fresh creds *before* surfacing the parse error would still pass while silently rotating the id — the exact regression the test guards. Add a filesystem-state assertion: after the failed mint, the corrupt file must be byte-unchanged (still empty), proving no silent fresh-mint. Refs #3. Co-Authored-By: seal <noreply@sealedsecurity.com>
Problem
cotal mint <name>callednewIdentity()unconditionally on every invocation, so re-minting an agent rotated its mesh id. Because the durable ACL row and the per-agent dm/dlv delivery durables are all keyed by the nkey public key, rotation orphaned them: the re-minted agent came up with a fresh id the broker had no ACL row or consumer for, and went @mention-wake-blind (consumer not found) until re-provisioned. This reproduced live 6× during a fleet relaunch.Re-minting is the normal way to refresh an agent's channels (mint's read/post ACLs are read from the persona file), so an operation that's supposed to be routine was silently breaking delivery.
Fix (option B — maintainer ruling on design PR #3, cubic P1)
Re-mint reuses the same identity by default:
packages/core/src/identity.ts— newidentityFromCreds(creds: string): Identity, the id-preserving sibling ofidFromCreds. It returns{ id, seed }from a creds file, reusingidFromCredsfor the id + JWT-subject cross-check (no duplicated parse, no new inline cast) and extracting the seed block.implementations/cli/src/commands/mint.ts— the agent path now computes theoutcreds path before minting. If a creds file already exists there and--forceis not passed, it re-signs that same id+seed with the fresh ACLs from the persona file; otherwise it mints a new identity.--forcekeeps the rotation escape hatch (compromised key / deliberate new identity). The output line now reports(reused — re-mint kept the identity)vs(new).Review refinements (agent-only + fail-loud)
profile === "agent"— observer/admin creds always rotate (they carry no persona-refresh workflow or durable footprint, and silently extending a privileged admin key's lifetime would be surprising).--force, rather than crashing on a raw parse exception or silently rotating (which would orphan the durable row the existing id may still own).Closes the #3 cubic-P1
This directly resolves the re-mint-orphans-the-ACL-row P1 that cubic raised on design PR #3 (
durable-delivery-acl-provisioning.md) — it is cubic's option (b) verbatim, per Matt's ruling. One fix retires both: #3's design note that re-mint orphans are "out of scope" no longer applies, because the canonicalcotal up+cotal mintrecipe is now safe under re-mint.Scope
Orthogonal to the still-open mint-strategy fork in #3: this changes which id mint uses, not where the ACL write triggers. Pairs with the PR #4 DLV-durable fix (that one makes fresh mints land their durables; this one stops re-mints from needing to).
Verification
@cotal-ai/corebuild +core/clitypecheck clean.newIdentity()→mintCreds→identityFromCredsreturns the same{id, seed};identityFromCreds(creds).id === idFromCreds(creds); re-mint of a reused identity stays STABLE.mint(): agent re-mint keeps the id; admin + observer re-mint each rotate; a corrupt creds file at the out path errors naming--force(no raw crash, no silent rotate).newIdentity()and pass after) is being added on this branch.Closes the #3 cubic-P1 (re-mint orphans ACL row). Refs #3.