Skip to content

Amara-ops/policy-runtime-ts

Repository files navigation

Policy Runtime TS

CI

What this is

  • An HTTP sidecar that enforces explicit, auditable guardrails for any agent or wallet.
  • You send an Intent; it replies allow/deny and logs every decision. This reduces blast radius, prevents runaway spend, and creates a clear audit trail.
  • Designed to be simple to reason about and easy to gate in CI: policies are just JSON; decisions are deterministic and logged.

Core concepts

  • Allowlist: Which contract (to) + function (selector) on which chainId are permitted.
  • Caps: Limits on outflow amounts and call counts over rolling windows (hour/day), globally, per target, or per function selector.
  • Denominations: Registry to convert human amounts (e.g., "1.25" USDC) into base units using decimals.

Quick start

  1. Build and run the HTTP sidecar
  1. Evaluate an intent (allow)
  • curl -sS -X POST http://127.0.0.1:8787/evaluate -H 'content-type: application/json' -d '{"intent":{"chainId":8453,"to":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","selector":"0xa9059cbb","denomination":"USDC","amount_human":"1"}}' | jq .
  1. Send your transaction (with your wallet client)

  2. Record the execution

  • curl -sS -X POST http://127.0.0.1:8787/record -H 'content-type: application/json' -d '{"intent":{"chainId":8453,"to":"0x833589fcd6edb6e08f4c7c32d4f71b54bda02913","selector":"0xa9059cbb","denomination":"USDC","amount_human":"1"},"txHash":"0xREPLACE_WITH_YOUR_TX_HASH"}'
  1. Observe

Define a policy (explanatory)

  • allowlist: Array of { chainId, to, selector }. Requests must match one entry.
  • caps:
    • max_outflow_h1 / max_outflow_d1: Monetary limits per denomination per hour/day. Use per‑denomination maps to write human amounts:
    {
      "max_outflow_h1": { "USDC": "100" },
      "max_outflow_d1": { "USDC": "500" }
    }
    
    Runtime converts to base units using the token registry decimals for each symbol.
    • per_target.h1 / per_target.d1: Monetary limits scoped to a target key:
      • Key formats: "" (aggregate per contract) or "|" (per function on that contract).
      • Values: base‑units string or per‑denomination map using human strings (preferred).
    • max_calls_per_function_h1 / max_calls_per_function_d1: Call‑count limits per 4‑byte selector (integers).
  • pause: If true, all requests are denied with PAUSED.
  • meta:
    • tokens_registry_path: Path to a JSON token registry used to resolve decimals (or set env TOKENS_CONFIG_PATH). Legacy meta.denominations is still accepted but deprecated.
    • defaultDenomination: Used when an intent omits denomination.
    • nonce_max_gap / slippage_max_bps: Optional guardrails used with the corresponding intent fields.

Intent shape

  • chainId, to (lowercase), selector (0x + 8 hex), denomination.
  • amount or amount_human. If both are provided, they must match or you get AMOUNT_MISMATCH.
  • Optional: deadline_ms, nonce/prev_nonce, slippage_bps.

Per‑target resolution

  • Given (to, selector): first check per_target["to|selector"], else fall back to per_target["to"].

Function meaning

  • "Function" means a 4‑byte selector across all contracts. max_calls_per_function_h1 or max_calls_per_function_d1 limits total requests for that selector per hour/day, globally.

Human amounts and denominations

  • Any monetary cap written as a per‑denomination map uses human strings. The runtime converts using registry decimals; top‑level cap strings (without a map) are treated as base units for backward compatibility.
  • Intent.amount_human follows the same decimals; precision beyond decimals is rejected.

Use with the Policy Linter

  • See: https://github.com/Amara-ops/agent-guardrails-policy-linter
  • Validate your policy.json with the linter before loading it into the runtime to catch schema and safety issues early.
  • Recommended flow: edit policy → run the linter → commit/CI gate → (re)start the runtime (or POST /reload).
  • GitHub Action: agent-guardrails-policy-linter provides a composite action to block merges on errors.
  • CLI example (from the linter repo):
    • npm run build
    • node dist/cli.js path/to/policy.json --report report.json [--sarif report.sarif]

Auth

  • If AUTH_TOKEN is set when starting the server, all HTTP endpoints require header: authorization: Bearer .
  • If AUTH_TOKEN is not set, auth is disabled for local development only. Do not run without auth in shared or production environments.

HTTP endpoints

  • POST /evaluate { intent }
  • POST /record { intent, txHash, amount? }
  • POST /execute { intent, txHash? }
  • POST /reload { policy }
  • POST /pause { paused }
  • GET /status
  • GET /metrics

Troubleshooting

  • 401/403: Missing or wrong Bearer token when AUTH_TOKEN is enabled.
  • NOT_ALLOWLISTED: chainId/to/selector mismatch.
  • CAP_*_EXCEEDED: lower amount, increase caps, or wait for window to roll.
  • AMOUNT_*: fix amount or denomination.
  • DEADLINE_/NONCE_/SLIPPAGE_: adjust intent or meta.*.
  • PAUSED: unpause via /pause or reload with pause=false.

Naming note

  • This repository is the TypeScript implementation of the runtime (hence "TS"). In docs and conversations, "Policy Runtime Engine" is the preferred product name. Other language implementations may exist in the future.

About

Minimal policy engine for agent treasury guardrails

Resources

License

Stars

Watchers

Forks

Packages

No packages published