Harden the agent guardrail: default-deny on every real-value chain (#76) - #104
Merged
Conversation
Invert the signer daemon's auto-approval guardrail from "armed only on
chain_id == 1" (fail-open) to "armed on every chain except an explicit
exempt testnet/dev allowlist {Sepolia 11155111, anvil 31337}" (fail-safe).
An unknown chain-id is treated as real-value and guarded, so configuring a
new real chain (Base, OP, ...) can never silently turn the brake off.
- daemon.rs: module-scope TESTNET_FORK_CHAIN_IDS + is_testnet_or_fork;
mainnet_guardrail_active -> guardrail_active (chain- and origin-agnostic).
- config.rs: mainnet_override -> autonomy_override; env var
DECKARD_I_KNOW_THIS_IS_MAINNET kept, now disarms on any real-value chain
(documented in THREAT-MODEL.md only, never echoed to the agent).
- tests: pin within-cap on Base 8453 and an unknown id -> NeedsApproval; an
exec-level test that execute is refused (not_approved) on Base before any
human resolve; a structural pin that 1/8453/10/42161 are never exempt.
- rename the now-inaccurate "mainnet guardrail" strings (client-visible
failure.rs, rpc/app comments) to "auto-approval guardrail".
Guard-shape only: no policy fields, no new deps.
README/THREAT-MODEL/CONTRIBUTING/STATUS + agent docs said the guardrail downgrades auto-allow "on chain 1 / on mainnet". After #76 it is default-deny on every real-value chain, so update the claim. Keep it honest: on the exempt testnet/dev ids within-cap auto-allow still happens by design (the demo runs hands-free), the default policy is still OverCap, and THREAT-MODEL residual risk #3 is narrowed, not resolved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #76.
What
Inverts the signer daemon's auto-approval guardrail from fail-open ("armed only on
chain_id == 1") to fail-safe default-deny ("armed on every chain EXCEPT an explicit exempt testnet/dev allowlist{Sepolia 11155111, anvil 31337}"). An unknown chain-id is treated as real-value and guarded, so configuring a new real chain (Base 8453, OP 10, …) can never silently turn the brake off. The safety property no longer depends on remembering to special-case each new chain.Guard-shape change only: no policy fields, no new deps.
Changes
daemon.rs— module-scopeTESTNET_FORK_CHAIN_IDS+is_testnet_or_fork;mainnet_guardrail_active→guardrail_active(chain- and origin-agnostic —ProposalOriginis display-only, so it guards app + agent proposals equally).config.rs—mainnet_override→autonomy_override. The env varDECKARD_I_KNOW_THIS_IS_MAINNETis kept unchanged (fewer env vars), but now disarms the guardrail on any real-value chain; its broadened meaning is documented inTHREAT-MODEL.mdonly and never echoed to the agent.tests/guardrail.rs+ a daemon unit module):NeedsApprovalin everyApprovalMode(acceptance pin)executeis refused (not_approved) on Base before any human resolve — pins the actual brake, not just the label1 / 8453 / 10 / 42161are never on the exempt list (catches a future fat-finger)deckard-wallet-client/src/failure.rs, to "auto-approval guardrail".OverCap, and THREAT-MODEL residual-risk feat(contract): deckard-contract crate + virtual Cargo workspace (freeze-first wire) #3 is marked narrowed, not resolved.Review
Produced via
/autoplan(CEO + Eng dual-voice: Claude subagent + Codex). Both phases converged; their findings are folded in (dropped a speculative1337exempt id, fixed a broken intra-doc link that would have been a-D warningsCI red, added the exec + structural tests, widened the doc sweep, doc-honesty about the OverCap default).Two items deferred to follow-up issues (out of #76's guard-shape scope): making
ApprovalMode::Alwaysthe default policy, and reconsidering the override env var's name/necessity.Definition of done
cargo fmt --all --checkcleanjust checkgreen (clippy-D warnings, default +--features tray)cargo test --workspacegreen — 282 passed, 0 failed (2 ignored =shield_e2e, needs a live Sepolia RPC)