Docs/Engineering/Auto-Signer — Bounded Automation in the Multisig
proposed

Auto-Signer — Bounded Automation in the Multisig

Operational pattern for reducing required human signatures on routine per-request settlements. An automated signer participates in the multisig and signs only when the proposed settlement price is within NAV tolerance. Pairs with Per-Request Flows.

Auto-Signer — Bounded Automation in the Multisig

Pairs with: Per-Request Flows · Updates: Deployment Ceremony

Why

The per-request architecture puts each AP settlement behind a multisig approval. If the multisig is pure-human, routine MM flows (dozens of subscriptions and redemptions per day, all within a few bps of NAV) pile up waiting on coordinating 3 humans to sign. SLAs slip. APs see friction. Secondary-market peg tracking degrades — which is the whole thing the per-request model was meant to solve.

The auto-signer pattern compresses this: one slot in the multisig is held by an automated service that signs iff pre-defined NAV-adjacency rules are satisfied. Normal flow drops from 3 human signatures to 2. Emergency/override path stays unchanged (pure human quorum).

This doc specifies the rules, the infrastructure, and the failure modes.

Not on-chain

The contract does not know about the auto-signer. From the contract's perspective this is just a Gnosis Safe with N signers and a threshold. The auto-signer is off-chain infrastructure that happens to hold one of those signer keys. No contract changes required.

Safe configuration

Recommended: 3-of-5 Gnosis Safe.

Signers:

  • Founder (hardware wallet)
  • CTO (hardware wallet)
  • Ops Lead (hardware wallet)
  • Counsel (hardware wallet)
  • AutoSigner (cloud HSM-backed)

Normal flow: Ops proposes tx → AutoSigner signs within seconds if in policy → 1 human countersigns → 3-of-5 met → execute.

Override flow: AutoSigner refused (price out of tolerance, source stale, or paused) → 3 humans sign → 3-of-5 met → execute. AutoSigner is bypassed, not blocking.

Emergency flow: AutoSigner key rotated out, or 4 humans reach quorum without it. System stays live.

Scope of AutoSigner's signing authority

AutoSigner only signs specific function selectors on specific contracts. Anything else, it refuses.

Allowed targets:

ARUSubscriptionManager.approveSubscription(bytes32, uint256)
ARUSubscriptionManager.approveRedemption(bytes32, uint256)

Refused:

  • Any admin config change: setTreasuryAddress, setMinimumMintAmount, pauseSubscriptions, freeze, addAP, removeAP, overwriteRequest, etc.
  • Any call on ARU directly (no minting or ownership ops)
  • Any call on ARUPriceOracle (no price updates)
  • Any tx with unexpected value > 0
  • Any Safe management call (changeThreshold, addOwner, removeOwner, swapOwner, setGuard)

This is enforced at two layers:

  1. AutoSigner code — parses calldata, refuses anything not in the allowlist
  2. Safe Guard contract (optional) — on-chain guard rejects certain selectors from being pre-signed by AutoSigner's key; redundant defense

Approval rules

AutoSigner evaluates each approval tx against three hard checks before signing:

1. Function + decoded parameters match policy

Target contract:   ARUSubscriptionManager
Function:          approveSubscription | approveRedemption
Parameters:        (id, pricePerARU) — both non-zero
Request state:     not fulfilled, not expired, priceBound satisfied

Fetched from on-chain state at evaluation time. Stale data → refuse.

2. Price within NAV tolerance

nav            = median(Redstone, BlackRock IAU ref, internal model)
deviation_bps  = |pricePerARU − nav| * 10000 / nav
allowed_bps    = 100    // 1%, tunable by multisig-signed config tx

Tolerance defaults to 100 bps (1%). Wider for stablecoin-like assets, tighter for hard-commodity pegs where NAV is nearly deterministic.

Three NAV sources, median of three. All three must be:

  • Fresh (less than 12h old, or source-specific threshold)
  • Non-zero
  • Within 500 bps of each other (inter-source sanity — if oracles disagree massively, don't sign)

If any source is stale or disagreeing, AutoSigner refuses. Falls to human quorum.

3. Global rate limit

max_approvals_per_hour = 50
max_approvals_per_day  = 500
max_total_usdc_per_day = $50M       // sum of subscription USDC + redemption USDC

Beyond these, AutoSigner auto-pauses. Prevents a scenario where a subtle oracle bug routes many small-deviation-but-adversarial approvals through. Manual human unpause required.

Configuration storage

Rules live in two places:

On-chain (immutable per deploy): the Safe signers, the Safe threshold, optionally a Safe Guard contract enforcing function-allowlist.

Off-chain (versioned, auditable): autosigner.yaml with:

yamlversion: 1
active: true
nav_sources:
  - name: redstone
    type: onchain_oracle
    address: "0x..."
    max_staleness_seconds: 43200
  - name: blackrock_iau_ref
    type: offchain_api
    url: "https://..."
    secret_env: BR_IAU_TOKEN
  - name: internal_model
    type: service
    url: "https://nav.internal.aru/v1/price"
tolerance:
  deviation_bps: 100
  inter_source_max_divergence_bps: 500
rate_limits:
  approvals_per_hour: 50
  approvals_per_day: 500
  total_usdc_per_day: 50000000
emergency:
  paused: false
  pause_reason: ""

Config changes require:

  • Git commit by authorized engineer
  • Reviewed + approved in PR by non-author
  • Deployed only by ops with separate IAM role
  • Emitted to observability channel on apply

Infrastructure

Runtime

Stateless event-driven service, preferred deployment shape:

Cloud provider:  AWS / GCP / Azure, OR OpenZeppelin Defender (see below)
Compute:         Serverless (Lambda / Cloud Functions / Defender Autotasks)
Key storage:     KMS HSM-backed; key never leaves HSM
Observability:   Structured logs to SIEM + metrics to Prometheus + Slack alerts
Event source:    Safe Transaction Service webhook
Evaluation:      Pure function of (tx, nav_sources, config); no local state

Worth evaluating as a first-class option once the v2 contract ships. Defender provides the exact primitives AutoSigner needs, pre-audited by OZ:

  • Autotasks — serverless functions for rule evaluation. Replaces the Lambda/Cloud Function layer.
  • Relayers — managed signer keys with HSM backing, rate limiting, and meta-transaction support. Replaces self-managed KMS + signing infra.
  • Sentinels — on-chain event monitoring that triggers Autotasks when a Safe approval is proposed. Replaces Safe Transaction Service webhook polling.
  • Admin — Safe integration with approval workflows. Can fit the human-in- the-loop override pattern without custom UI.

Why Defender over self-hosted:

  • Audit surface for the AutoSigner runtime is vendor-owned, reducing our custom infra code
  • Relayer key infrastructure is production-tested at scale
  • Well-integrated with Safe, which we already use
  • The same platform handles multiple future automation needs (e.g., oracle keeper, pause bots, emergency response), so amortizes integration cost

Why not Defender:

  • Vendor dependency — if OZ deprecates Defender or changes pricing, we migrate
  • Less control over specific runtime behavior vs. self-hosted
  • Requires trusting OZ's opsec on the Relayer key storage

Recommendation: use Defender for v1 AutoSigner rollout, revisit self-host if we outgrow the service or need specific features it doesn't support. The stateless/pure-function design of AutoSigner means the Autotask can be lifted to self-hosted infra later with bounded migration cost.

Ops upgrade — schedule for after v2 contracts land and per-request flows stabilize. Not in the initial v2 launch path.

Flow

  1. Ops proposes approval tx in Safe UI
  2. Safe Transaction Service webhook hits AutoSigner endpoint
  3. AutoSigner fetches:
    • Proposed tx details (target, calldata, value)
    • On-chain request state (is it actually unfulfilled + non-expired + priceBound-compliant?)
    • Current NAV from all sources
  4. Evaluation runs all three checks
  5. Log decision with full inputs (regardless of outcome)
  6. If all checks pass: sign via Safe API
  7. If any check fails: log refusal reason, alert Slack #multisig-ops, do nothing else

Key custody

Hardware-backed KMS with:

  • No plaintext key export allowed
  • Audit log of every signing operation
  • Alerting on unexpected signing patterns
  • Quarterly rotation — new key added to Safe, old key removed, keep 1 Safe tx of transition period for continuity

Deployment pipeline

  • CI tests against a mainnet fork: proposes N adversarial txs, verifies AutoSigner refuses each
  • Staging Safe on testnet runs the same AutoSigner code for a week before production
  • Production deploys require two-person review (same-org, separate IAM roles)

Security model

Attack Capability Mitigation
AutoSigner key compromise Attacker now holds 1-of-5 Needs 2 more human signatures. Humans should apply extra scrutiny when AutoSigner signed first (cultural discipline)
NAV source poisoning Attacker can't bypass multi-source median unless ≥2 sources are compromised Multi-source + divergence check; admin rotates compromised source immediately
Replay / race Same approval tx submitted twice Safe's nonce + contract's fulfilled flag prevent effect; AutoSigner's rate limit also blocks
Slow oracle + NAV deviates Stale source evaluates, AutoSigner signs at adversarial price Strict staleness check + inter-source divergence; either triggers refusal
Admin-config tx disguised as approval Calldata crafted to look like approval but target is unexpected Function-selector allowlist at AutoSigner; optional Safe Guard as second layer
AutoSigner stuck / dead New requests can't get automated sign Fallback to 3 humans. AutoSigner is never a required dependency
Config drift (wrong tolerance) AutoSigner signs too liberally Config is git-committed, reviewed. Anomaly detection on signed-tx metadata. Humans notice bad patterns

What AutoSigner cannot do

  • Cannot mint ARU directly. The only mint path goes through the manager contract's approve* functions, which AutoSigner can sign but not execute alone.
  • Cannot authorize itself a larger share of the multisig. Changing threshold or adding signers requires human-signed Safe owner management, which AutoSigner refuses to sign.
  • Cannot approve outside AP's priceBound. The contract rejects that regardless of AutoSigner's signature.
  • Cannot trigger approval while the request is expired or fulfilled. Contract rejects.
  • Cannot extend request TTL. That's a MANAGER_ADMIN config change AutoSigner refuses.

Operational SLAs

Commit publicly to APs:

Condition SLA
Request in policy (AutoSigner signs) + 1 human online ≤ 30 minutes typical, ≤ 2 hours committed
Request requires human override ≤ 4 business hours
Request during AutoSigner outage ≤ 4 business hours (pure human quorum)
Emergency pause (any path) Immediate on-chain via pause*, announced within 15 minutes

Monitoring + alerts

Page on-call for any of:

  • AutoSigner refused > 5% of proposals in the last hour (policy drift or incident)
  • Two NAV sources disagree by > 250 bps (oracle trouble)
  • AutoSigner offline (no heartbeat) > 15 minutes
  • Rate limit hit (unusual demand or attack surface)
  • Safe owner-change tx pending (unusual; attackers would try this first)

Implementation checklist

  • Deploy Safe with 5 signers, threshold 3
  • Build AutoSigner service (stateless, HSM-backed signing)
  • Build NAV aggregator with 3 sources + median + staleness checks
  • Publish autosigner.yaml config schema, version 1
  • Deploy optional Safe Guard contract with function-selector allowlist
  • Integrate Safe Transaction Service webhook
  • SIEM integration + Slack alerts
  • Runbook: AutoSigner refusal investigation
  • Runbook: AutoSigner key rotation (quarterly)
  • Test: 100+ adversarial tx types, all refused
  • Test: legitimate flow end-to-end in < 5 min wall-clock
  • Staging deploy on testnet, 7 days observation
  • Production deploy with first AP only, expand after 2 weeks clean ops

Why this beats pure-human multisig

  • Speed — routine approvals finish in minutes, not hours
  • Consistency — price tolerance is enforced uniformly, not dependent on the human signer's mood or market conditions they've seen
  • Auditability — every decision (sign or refuse) is logged with full inputs; easier for auditors than reconstructing human reasoning
  • Humans do higher-value work — reserved for edge cases, not routine 2 bps approvals

Why this beats pure-automation

  • Humans stay in quorum — 3-of-5 with AutoSigner = 1-of-5 of the sigs; humans are still in the loop
  • Override is always possible — pure human quorum any time; AutoSigner never blocks
  • Attacker needs 3 factors — AutoSigner key AND NAV source AND human signers; compromising only AutoSigner buys them nothing alone
  • Legal story is clean — programmatic pre-approval within bounds is a well-understood TradFi concept; courts and regulators understand it