Skill: rafalzacher1/signet-guardian (version 0.1.0)

--- name: signet-guardian description: "Payment guard middleware for AI agents. Use whenever any skill is about to initiate a payment. Runs a preflight check against the user's policy (payments enabled, per-transaction limit, monthly cap). Returns ALLOW, DENY, or CONFIRM_REQUIRED. Other payment-capable skills must call signet-preflight before proceeding and signet-record after a successful payment." metadata: {"openclaw":{"emoji":"🛡️","homepage":"https://getsignet.xyz"}} --- # Signet Guardian — Payment Guard Middleware ## Overview Signet Guardian is a **policy firewall** for money actions. It does not intercept payments at runtime by itself; **payment-capable skills must route through it by contract**: 1. Before any payment: call **signet-preflight** (amount, currency, payee, purpose). 2. If result is **ALLOW** or **CONFIRM_REQUIRED** (and user has confirmed): the skill may proceed. 3. If result is **DENY**: do **not** proceed; tell the user the reason. 4. After a successful payment: call **signet-record** to append to the ledger. This gives one place to enforce: master switch (payments on/off), max per transaction (e.g. £20), max per month (e.g. £500), and optional confirmation above a threshold (e.g. £5). **Concurrency:** Preflight is advisory (no lock). **Record enforces the monthly cap under a file lock** (`{baseDir}/references/.ledger.lock`): it re-checks the cap before appending and refuses to record if the month would be exceeded. So the monthly limit is enforced at record time; idempotency and cap are both safe under concurrent calls. Preflight can still be used to fail fast; the definitive check is in record. **Currency:** No FX conversion. The request currency **must match** the policy currency; otherwise preflight returns DENY. Conversion source/rules are not defined. ## Policy (user configuration) **Source of truth:** OpenClaw config first (`signet.policy` in the main config, e.g. editable in the Control UI if the extension is installed), then fallback to `{baseDir}/references/policy.json`. OpenClaw sets `{baseDir}` via `OPENCLAW_SKILL_DIR` or `OPENCLAW_BASE_DIR`. | Field | Meaning | |-------|--------| | `paymentsEnabled` | Master switch. If `false`, all payments are denied. | | `maxPerTransaction` | Max amount allowed for a single transaction (e.g. 20). | | `maxPerMonth` | Max total spend in the current calendar month (e.g. 500). | | `currency` | ISO currency code (e.g. GBP, USD). Request currency must match. | | `requireConfirmationAbove` | Above this amount, return CONFIRM_REQUIRED so the user must explicitly confirm (e.g. 5). | | `blockedMerchants` | Optional list of substrings; payee matching any is denied. | | `allowedMerchants` | Optional; if non-empty, only payees matching one of these are allowed. | | `version` | Optional number for future policy migrations. | **Default behaviour:** If the policy file is missing or invalid, **preflight returns DENY** (default-deny). ## Commands ### `signet-preflight` Run **before** initiating any payment. Validates: payments enabled, currency match, amount > 0 and ≤ max per transaction, (current month spend + amount) ≤ max per month, and optional merchant rules. Optionally requires explicit confirmation above a threshold. Amount must be greater than zero. ```bash signet-preflight --amount 15 --currency GBP --payee "shop.example.com" --purpose "Subscription" ``` Optional: - `--idempotency-key "unique-key"` — Used when recording later to avoid duplicate ledger entries. - `--caller-skill "skill-name"` — Name of the skill invoking the guard (for audit). **Output (JSON):** - `{ "result": "ALLOW", "reason": "Within policy" }` — Proceed with the payment. - `{ "result": "CONFIRM_REQUIRED", "reason": "..." }` — Ask the user for explicit confirmation; if they agree, proceed then call signet-record. (Confirmation is the caller’s responsibility.) - `{ "result": "DENY", "reason": "..." }` — Do **not** proceed. Notify the user. Every DENY is logged to the audit trail. **Exit code:** 0 for ALLOW or CONFIRM_REQUIRED, 1 for DENY. ### `signet-record` Call **after** a payment has successfully been made. Appends one line to the ledger (append-only). If an idempotency key was used in preflight, pass the same key here to avoid double-counting. **Record validation scope:** `signet-record` re-checks only **currency** and **monthly cap** (under lock). It does **not** re-check `paymentsEnabled` or merchant allow/block lists. Policy enforcement (switch, merchants, per-tx limit) is done at **preflight** (and in an optional future authorize phase). Record is the post-success log; the cap check at record time prevents double-counting when concurrent preflights both allowed. ```bash signet-record --amount 15 --currency GBP --payee "shop.example.com" --purpose "Subscription" --idempotency-key "sub-123" ``` Optional: `--caller-skill "skill-name"` for audit. If the same `idempotency-key` was already recorded, the command is a no-op (idempotent). ### `signet-report` Shows spending and transaction history for the user. ```bash signet-report --period today signet-report --period month ``` ### `signet-policy` Show, edit, or configure policy via wizard. ```bash signet-policy --show # Print current policy (config, then file) signet-policy --edit # Open policy.json in $EDITOR signet-policy --wizard # Interactive step-by-step setup (no JSON) signet-policy --migrate-file-to-config # One-time: copy file policy into OpenClaw config ``` ## Audit (ledger and deny log) Ledger file: `{baseDir}/references/ledger.jsonl`. Format is **strict JSONL**: one JSON object per line, **newline-separated** (no space between entries). Each line contains: - **ts** — Timestamp UTC (ISO 8601). - **callerSkill** — Optional; skill that invoked preflight/record. - **idempotencyKey** — Optional; dedupe key for record. - **status** — `completed` or `denied`. - **reason** — Decision reason (especially for denials). - Plus: amount, currency, payee, purpose. All preflight denials are appended to the same ledger with `status: "denied"` and a reason. ## Critical Rules (for the agent) 1. **Never skip preflight** — Any payment from any skill must go through `signet-preflight` first. No exceptions. 2. **Respect DENY** — If preflight returns DENY, do not attempt the payment. Tell the user the reason. 3. **CONFIRM_REQUIRED** — If preflight returns CONFIRM_REQUIRED, ask the user explicitly (“Allow this payment of £X to Y?”). Only proceed if they confirm, then call `signet-record`. 4. **Always record success** — After a successful payment, call `signet-record` with the same amount, currency, payee, purpose, and idempotency key (if used). 5. **Idempotency** — For critical flows, use a stable `--idempotency-key` (e.g. order ID or request ID) so retries do not double-count in the monthly total. 6. **Default-deny** — If the policy file is missing or corrupt, the skill denies by default. 7. **Record is authoritative for cap only** — The monthly cap is enforced when recording (under lock). If `signet-record` fails with a cap error, the payment already happened; do not retry without user confirmation. For cap-safe flows before payment, a future **authorize** (reservation under lock) then **settle** (convert reservation to completed) pattern can reserve budget before the payment is made. ## First Run On first use, the user must have a valid `{baseDir}/references/policy.json`. Run `signet-policy --show` to see current policy; if missing, create it (e.g. via `signet-policy --edit`) with at least: - `paymentsEnabled`: true/false - `maxPerTransaction`: number - `maxPerMonth`: number - `currency`: e.g. "GBP" - `requireConfirmationAbove`: number (e.g. 5) Ledger lives at `{baseDir}/references/ledger.jsonl`; no extra setup required.

path: docs/DASHBOARD_TEST.md
content: # Manual test: Dashboard edit → CLI reads

Use this to confirm that policy edited in the OpenClaw Control UI is read by the CLI.

## Prerequisites

- OpenClaw extension installed (see [../openclaw-extension/README.md](../openclaw-extension/README.md)).
- Config path: `OPENCLAW_CONFIG_PATH` or `~/.openclaw/openclaw.json`.

## Steps

1. **Open Control UI** (Gateway dashboard) and go to **Settings**.
2. Find the **Signet Guardian** / **signet.policy** section.
3. **Change a value** (e.g. set "Max per transaction" to `25`) and save.
4. In a terminal, run:
   ```bash
   cd /path/to/signet-guardian
   export OPENCLAW_SKILL_DIR="$PWD"   # optional if config has policy
   npx tsx scripts/signet-cli.ts signet-policy --show
   ```
5. **Expected:** The printed JSON shows your updated value (e.g. `"maxPerTransaction": 25`).

## Optional: migrate file → config then verify

1. Ensure `references/policy.json` exists and is valid.
2. Run: `npx tsx scripts/signet-cli.ts signet-policy --migrate-file-to-config`
3. Run: `npx tsx scripts/signet-cli.ts signet-policy --show`
4. **Expected:** Output matches the previous file content; source is now config.


path: package.json
content: {
  "name": "signet-guardian",
  "version": "0.1.0",
  "description": "Transaction security skill for OpenClaw",
  "type": "module",
  "scripts": {
    "build": "tsc scripts/signet-cli.ts --outDir dist --module esnext --target es2020 --moduleResolution node"
  },
  "dependencies": {
    "prompts": "^2.4.2"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0",
    "tsx": "^4.7.0"
  }
}


path: references/policy.json
content: {
  "paymentsEnabled": true,
  "maxPerTransaction": 20,
  "maxPerMonth": 500,
  "currency": "GBP",
  "requireConfirmationAbove": 5,
  "blockedMerchants": [],
  "allowedMerchants": []
}


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (HIGH)