Skip to content

Implement ADR-0014: destructive-rollback approval gate #111

Closed

Description

@tonibergholm

Question

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.

Activity

  1. added
    ready-for-agentScoped and unambiguous enough for an agent to pick up
    wayfinder:taskWayfinder ticket: work that unblocks a decision or lands a fix
    on Jul 9, 2026
  2. self-assigned this
    on Jul 10, 2026
  3. tonibergholm commented on Jul 10, 2026

    @tonibergholm
    MemberAuthor

    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 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.

  4. added a commit that references this issue on Jul 10, 2026
    beacd65
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

ready-for-agentScoped and unambiguous enough for an agent to pick upwayfinder:taskWayfinder ticket: work that unblocks a decision or lands a fix

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions