Scope Enforcement

Scopes let you restrict what a runtime key is allowed to do. When you delegate a runtime key with a scope, that key can only sign messages that claim the same scope. This prevents a key delegated for “messaging” from being used to sign “deploy” actions.

What Is a Scope?

A scope is a plain text label — like "messaging", "deploy", or "read-only" — that gets hashed into a bytes32 value and stored on-chain as part of the delegation record.

terminal
# Delegate a key that can only sign "messaging" actions
vouch delegate --expiry 24h --scope messaging
# Sign with an explicit scope
vouch sign --payload '{"msg": "hello"}' --scope messaging

Under the hood, scopes are keccak256 hashes. A special value — the zero scope (32 zero bytes) — means “unrestricted.” The key can sign anything.

scope string: "messaging"
keccak256("messaging")
bytes32: 0x7a9c3e... (stored on-chain in the delegation record)

Scope Matching Rules

When vouch verify checks a delegated key (signer ≠ agent_id), it compares two scopes:

  • Delegation scope — what the key was authorized to do (from the on-chain record)
  • Envelope scope — what the message claims to be doing (from the signed envelope)
Delegation ScopeEnvelope ScopeResult
Zero (unrestricted)AnythingValid — unrestricted keys can sign anything
Any valueZero (not claiming a scope)Valid — not claiming a scope is always allowed
keccak256("messaging")keccak256("messaging")Valid — scopes match exactly
keccak256("messaging")keccak256("deploy")Rejected — scope mismatch

Owner wallets skip scope checks. If the signer is the wallet itself (signer == agent_id), scope checks are skipped entirely. Your own wallet always has full authority — scopes only apply to delegated runtime keys.

When to Use Scopes

Use scopes when you delegate keys to different agents or processes that should have limited authority.

ScopeUse case
messagingA chatbot agent that can only send messages
deployA deployment agent that can authorize deploys
read-onlyA monitoring agent that can read but not take actions
billingA billing agent that handles payment-related operations
(empty / unrestricted)A general-purpose agent with full authority

Don’t use scopes when: you only have one agent per wallet, your agent needs full authority, or you’re just getting started (you can add scopes later).

Practical Example

Say you run two agents from the same wallet: a chat agent and a deploy agent. You want to make sure the chat agent can never sign deploy-related messages.

terminal
# Delegate a key for the chat agent — scoped to messaging
vouch delegate --expiry 24h --scope messaging
# Delegate a separate key for the deploy agent — scoped to deploy
vouch delegate --expiry 24h --scope deploy

When the chat agent signs a message:

terminal
# This works — scope matches the delegation
vouch sign --key 0xCHAT_KEY --payload '{"msg":"hello"}' --scope messaging
# This would fail verification — scope mismatch
vouch sign --key 0xCHAT_KEY --payload '{"action":"deploy"}' --scope deploy

Even if a chat key is compromised, it can’t be used to authorize deploys.

Scopes in the Config File

After you delegate a key with a scope, the scope is saved in your config so you don’t have to specify it every time:

# ~/.vouch/config.toml (saved automatically after delegate)
runtime_key_address = "0xDEF..."
delegation_scope = "messaging"
delegation_duration = "24h"
delegation_expires_at = 1760003600

When you run vouch sign without --scope, it uses the delegation_scope from your config. When you run vouch delegate --renew, it reuses the same scope.

Enforcement Step by Step

Here’s exactly what happens during step 8b of verification when a delegated key is used:

1. Recover delegation scope from on-chain record via RPC (bytes32)
2. Decode envelope scope from the signed envelope (bytes32)
3. Apply rules:
delegation == ZeroScope?
→ Yes: key is unrestricted, PASS
→ No: continue
envelope == ZeroScope?
→ Yes: not claiming a scope, PASS
→ No: continue
delegation == envelope?
→ Yes: PASS (scope matched)
→ No: FAIL ("envelope scope does not match delegation scope")

This means:

  • An unrestricted key can sign anything (backwards compatible with pre-scope delegations)
  • A message that doesn’t claim a scope is always allowed (no scope = no restriction to check)
  • A scoped key signing a scoped message must have an exact match