You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix audit finding #2 (critical) properly: implement ADR-0014 (Accepted 2026-07-09) verbatim. Supersedes the interim hard-reject ticket, which it is blocked by (same code path; the interim gate becomes the no-key branch of the full gate).
Scope, per the ADR's six decisions:
approval_token: Option<String> added to Request::Rollback (additive wire change, crates/agentic-proto).
HMAC-SHA256 verification over the domain-prefixed (commit_hash, peer_uid, timestamp) tuple; constant-time compare; token wire format "<unix_ts>:<hex_hmac>".
APPROVAL_TOKEN_TTL_SECONDS = 300 in agentic-core/src/approval.rs; symmetric window.
--approval-key-file <path> daemon flag; exactly-32-byte key file; fail-closed when absent/unreadable/empty; zeroize on drop.
RollbackForcedDataLoss structured audit event on EVERY accept_data_loss=true attempt, all decision variants.
agentic rollback --approval <commit-hash> --uid <worker-uid> --key-file <path> token issuance in agentic-cli, daemon not required.
Tests: forged/expired/wrong-target/wrong-uid/malformed token rejection, no-key rejection, happy path, audit-event emission on every branch; failure-injection where it touches rollback orchestration.
Standing rule from the map: if the ADR underspecifies something load-bearing, STOP and surface a decision — don't invent policy.
Resolution records: PR link, test names, confirmation the interim gate was cleanly superseded.
Regression found & re-established, not "superseded": PR #114's interim gate was silently deleted by PR #115's merge-conflict resolution, so main was unprotected on this finding. This PR restores protection and implements ADR-0014 in full. Verified the other five audit fixes are still present in main HEAD.
What landed:agentic-core::approval (HMAC-SHA256 over domain-prefixed (commit_hash, peer_uid, timestamp), constant-time verify_slice on the hex-decoded tag, 300s symmetric TTL, zeroizing 32-byte key); additive approval_token on Request::Rollback + v0 shim → None; DaemonState key + --approval-key-file startup loading (narrow-abort reconciliation of Decisions 4/6); the gate before object-store/Postgres and before dry-run with all six typed rejections + RollbackForcedDataLoss audit on every branch; agentic rollback --approval/--uid/--key-file issuance + --approval-token forwarding.
Standing-rule call: the Decision 4/6 ambiguity (empty/short key file) was reconciled, not escalated — both readings are security-equivalent (neither permits unauthorized destructive rollback). Documented at the load site. The ADR-0012 × ADR-0014 interaction (peer_uid == None in insecure mode) is an explicit NoPeerUid rejection, never a UID substitution.
Tests: full approval matrix; daemon gate tests for every rejection variant (zero side effects), accepted happy path end-to-end, dry-run-doesn't-bypass; wire classification for all six codes. Workspace tests + clippy + fmt green. CLI issuance smoke-tested.
Also: flipped ADR-0014 Status → Accepted (its natural home) and restored the thiserror agenticd dep that #115 dropped alongside the interim gate.
Activity
tonibergholm commentedon Jul 10, 2026
Resolution
Shipped as PR #121 (pending Toni's review/merge).
Regression found & re-established, not "superseded": PR #114's interim gate was silently deleted by PR #115's merge-conflict resolution, so
mainwas unprotected on this finding. This PR restores protection and implements ADR-0014 in full. Verified the other five audit fixes are still present inmainHEAD.What landed:
agentic-core::approval(HMAC-SHA256 over domain-prefixed(commit_hash, peer_uid, timestamp), constant-timeverify_sliceon the hex-decoded tag, 300s symmetric TTL, zeroizing 32-byte key); additiveapproval_tokenonRequest::Rollback+ v0 shim →None;DaemonStatekey +--approval-key-filestartup loading (narrow-abort reconciliation of Decisions 4/6); the gate before object-store/Postgres and before dry-run with all six typed rejections +RollbackForcedDataLossaudit on every branch;agentic rollback --approval/--uid/--key-fileissuance +--approval-tokenforwarding.Standing-rule call: the Decision 4/6 ambiguity (empty/short key file) was reconciled, not escalated — both readings are security-equivalent (neither permits unauthorized destructive rollback). Documented at the load site. The ADR-0012 × ADR-0014 interaction (
peer_uid == Nonein insecure mode) is an explicitNoPeerUidrejection, never a UID substitution.Tests: full approval matrix; daemon gate tests for every rejection variant (zero side effects), accepted happy path end-to-end, dry-run-doesn't-bypass; wire classification for all six codes. Workspace tests + clippy + fmt green. CLI issuance smoke-tested.
Also: flipped ADR-0014 Status → Accepted (its natural home) and restored the
thiserroragenticd dep that #115 dropped alongside the interim gate.Implement ADR-0014 destructive-rollback approval gate (#121)