Overview
Awarranty.md file is a signed, operator-defined policy that tells Sigil Sign what your agent is and isn’t allowed to do. It is the contract between you and your agent’s execution layer.
Sigil Sign evaluates every agent intent against this file before allowing any action to proceed. If the action violates policy, it is denied before it executes, not audited after.
Generate Your Policy
Use Sigil Warrant at sigilcore.com/tools/warrant to generate, sign, and download yourwarranty.md. Two paths are available:
- Warrant Builder: guided step-by-step flow. No policy syntax required. Recommended for first-time operators.
- Manual Warrant: write your policy directly in the
warranty.mdformat. Full control over every field. For developers familiar with the warranty.md schema.
warranty.md that Sigil Sign accepts at boot.
File Format
warranty.md uses a plain-text, typed-block format. Blocks are defined by ## headers. At least one of ## evm, ## tool_calls, or ## custom is required.
Policy Sections
## evm
Controls EVM transaction execution, including spend limits, allowed chains, allowed actions, and consensus hold thresholds.
| Field | Description |
|---|---|
max_transaction_eth | Maximum ETH value per transaction |
allowed_chains | Comma-separated chain IDs |
allowed_actions | Permitted EVM actions |
chain_actions | Optional per-chain action overrides (takes precedence over allowed_actions) |
consensus_threshold_eth | Transactions above this require human approval |
consensus_require_hold | Set true to enable the hold |
token.<SYM>.max_transaction | Maximum per-transaction amount for that ERC-20 token, in human units (e.g. 10000 = 10,000 USDC) |
token.<SYM>.decimals | Required. Token decimals (USDC/USDT are 6, most ERC-20s are 18). There is no default: a wrong implicit default would mis-scale the limit by orders of magnitude |
token.<SYM>.addresses | Optional pinned contract addresses; repeat the line to add addresses across chains (entries merge) |
token is governed only by the matching token.<SYM>.* rule. max_transaction_eth and consensus_threshold_eth are ETH-denominated and never apply to token amounts. A token intent with no matching rule, including a policy with no token rules at all, is DENIED fail-closed with SIGIL_POLICY_VIOLATION_TOKEN_NOT_ALLOWED. Symbols match case-insensitively; address-form intents match only pinned addresses. When a rule pins addresses, the intent’s targetAddress must be one of them. An ERC-20 transfer’s transaction target is the token contract, and this binding prevents a native ETH transfer labelled with a token symbol from skipping the ETH limit. Pinning addresses is strongly recommended; rules without them accept the declared symbol at face value. Amount comparisons are exact: all-digit amounts are base units compared via BigInt at the rule’s decimals, decimal amounts are scaled exactly via string math with no float rounding, and the limit itself is kept as the decimal string you wrote. A token intent whose amount is missing or unparseable is DENIED fail-closed with SIGIL_POLICY_VIOLATION_TOKEN_AMOUNT_INVALID.
## tool_calls
Controls non-EVM agent tool execution.
| Field | Description |
|---|---|
allowed | Permitted tool types |
bash.blocked_commands | Substrings that trigger denial in bash |
web_fetch.blocked_domains | Hostnames blocked for web requests |
file_write.blocked_paths | Path prefixes blocked for file writes |
email.require_approval | Hold all email.send for human approval |
email.allowed_recipients | Recipients permitted for email.send, using exact addresses or *@domain wildcards |
email.blocked_recipients | Recipients always denied for email.send, using the same entry forms |
email.send intents carry recipients in intent.to (string or array). Checks run in order: denylist, allowlist, approval hold. A blocked recipient is DENIED (SIGIL_POLICY_VIOLATION_BLOCKED_RECIPIENT) before any hold is created, and an off-allowlist recipient returns SIGIL_POLICY_VIOLATION_RECIPIENT_NOT_ALLOWED. Every recipient in an array must pass. A missing to while either list is declared is DENIED fail-closed. Each recipient list must contain at least one entry; empty recipient lists reject the policy. *@domain matches that exact domain only; subdomains do not match. Matching is case-insensitive.
## custom
Operator-defined rules evaluated before all other checks. Three rule types:
contains, starts_with, ends_with, equals, not_equals, matches (regex)
Allowlist semantics: allow_only is an affirmative allowlist with exact, case-sensitive matching (no operators). The rule applies to every intent the policy evaluates: if the field is missing, non-string, or its value is not listed, the intent is DENIED fail-closed with SIGIL_POLICY_VIOLATION_NOT_ON_ALLOWLIST. Each line must include at least one value; empty allow_only lists reject the policy. Repeat the line for the same field path to extend the value set (entries merge). Deny rules win: deny_if/deny_string are evaluated first, so a value matching both a deny rule and the allowlist is denied with the deny rule’s code.
## soft_limits
Informational limits flagged for audit but never hard-enforced. Included so the policyHash reflects the operator’s stated intent.
## signature
Ed25519 signature over all content above this block. Generated by Sigil Warrant. A missing or invalid signature causes Sigil Sign to reject the policy unconditionally at startup.
Deployment
Place your signedwarranty.md at config/warranty.md relative to process.cwd(), or set WARRANTY_PATH to its location:
Security
- The policy file is signed with your Ed25519 operator key
- The SHA-256 hash of the policy content is embedded in every Intent Attestation JWT (
policyHashclaim) - If the file is modified after signing, Sigil Sign detects it at next startup and refuses to start
- Never commit your live
warranty.mdto version control; it exposes your agent’s security policy and operational limits
config/warranty.md is gitignored by default in the sigil-sign repo.