Deterministic policy enforcement for AI agents. Control what your agent can do — spending limits, destructive command blocking, credential gating — without putting an LLM in the decision path.
cargo install signet-eval
1. Install: cargo install signet-eval
2. Hook into Claude Code:
{
"hooks": {
"PreToolUse": [{
"matcher": "",
"hooks": [{"type": "command", "command": "signet-eval", "timeout": 2000}]
}]
}
}
3. Done. Every tool call now passes through policy evaluation.
Self-protection rules are LOCKED — the agent cannot remove, edit, or reorder them, even through the MCP management server.
| Action | Decision |
|---|---|
Write/Edit/Bash to .signet/ LOCKED | DENY |
Tamper with signet-eval binary LOCKED | DENY |
Modify settings.json LOCKED | ASK |
| Kill signet processes LOCKED | DENY |
rm, rmdir | DENY |
git push --force | ASK |
mkfs, format, dd if= | DENY |
curl | sh, wget | sh | DENY |
| Everything else | ALLOW |
# ~/.signet/policy.yaml
version: 1
default_action: ALLOW
rules:
- name: books_limit
tool_pattern: ".*purchase.*"
conditions:
- "param_eq(category, 'books')"
- "spend_plus_amount_gt('books', amount, 200)"
action: DENY
reason: "Books spending limit ($200) exceeded"
- name: protect_prod
tool_pattern: ".*"
conditions: ["contains(parameters, 'production')"]
action: ASK
locked: true
reason: "Production access requires confirmation"
| Function | Description |
|---|---|
contains(parameters, 'X') | Tool input contains string |
any_of(parameters, 'X', 'Y') | Any string present |
param_eq(field, 'value') | Field equals value |
param_ne(field, 'value') | Field not equal |
param_gt(field, N) | Field > number |
param_lt(field, N) | Field < number |
param_contains(field, 'X') | Field contains substring |
matches(field, 'regex') | Field matches regex |
has_credential('name') | Credential exists in vault |
spend_gt('cat', N) | Session spend exceeds limit |
spend_plus_amount_gt('cat', field, N) | Cumulative spend + amount exceeds limit |
not(condition) | Negate any condition |
or(A || B) | Either condition true |
true / false | Literal boolean |
Four locked rules prevent the agent from disabling its own guardrails. Locked rules can't be removed or edited via MCP. Direct file edits break the HMAC signature, triggering fallback to hardcoded safe defaults.
Argon2id + AES-256-GCM. Three tiers: public ledger, session-encrypted state, compartment-encrypted credentials. Session key encrypted with device-specific key. Brute-force lockout after 5 failed attempts.
Track cumulative spend per category. "Don't spend more than $200 on books" just works. Vault ledger enforces across every tool call. Reset with signet-eval reset-session.
Store credentials with domain, purpose, amount caps, and one-time constraints. signet_use_credential enforces all constraints before releasing. One-time tokens auto-invalidate.
Manage policies by talking to Claude. "Add a $50 limit for amazon orders." 17 MCP tools: rules, credentials, testing, validation, reordering, signing, credential use.
Wrap upstream MCP servers with enforcement. Hot-reloads policy on every call. The agent connects to the proxy, not directly to servers.
HMAC-sign your policy file. Hook verifies integrity on every evaluation. Tampered files fall back to safe defaults automatically. MCP mutations auto-sign.
Mark rules as locked: true in YAML. MCP tools refuse to modify them. Unlocked rules can't be reordered above locked ones. Self-protection rules ship locked by default.
Part of the Signet personal sovereign agent stack. The core principle: the authorization layer must not be an LLM. It processes structured data only. No natural language, no context window, no persuasion surface. A rule either matches or it doesn't.
Agent proposes action -> signet-eval evaluates policy -> allow / deny / ask
(deterministic, 25ms, no NLP)
signet-eval is a seatbelt, not a cage. It enforces policy within a cooperative protocol — Claude Code calls signet-eval before every tool use and respects the response. This reliably blocks destructive commands, enforces spending limits, and gates credential access during normal operation.
It does not protect against an adversarial agent with shell access. Any process at the same privilege level can use shell indirection (alias, eval, base64 | sh) to evade string matching, read the session key, or replace the binary. No amount of pattern matching fixes this — it's an infinite regress.
True containment requires OS-level controls: separate user accounts, containers, stripped $PATH, seccomp/AppArmor, or sandboxed environments. signet-eval is the policy engine inside such a perimeter. The OS enforces the perimeter. Neither replaces the other.
| Command | Purpose |
|---|---|
signet-eval | Hook evaluation (default, 25ms) |
signet-eval init | Write default policy with locked self-protection |
signet-eval rules | Show current rules (locked rules tagged) |
signet-eval validate | Check policy for errors |
signet-eval test '<json>' | Test a tool call |
signet-eval setup | Create encrypted vault |
signet-eval unlock | Refresh vault session |
signet-eval status | Vault status and spending |
signet-eval store <n> <v> | Store Tier 3 credential |
signet-eval delete <n> | Delete credential |
signet-eval log | Recent action log |
signet-eval reset-session | Clear spending counters |
signet-eval sign | HMAC-sign policy file |
signet-eval serve | MCP management server (17 tools) |
signet-eval proxy | MCP proxy for upstream servers |