Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "verity-harness",
"owner": {
"name": "Futron Prime",
"url": "https://github.com/FutronPrime"
},
"metadata": {
"description": "VERITY — discipline gates for AI coding agents.",
"version": "1.0.0"
},
"plugins": [
{
"name": "verity-discipline",
"source": "./plugin",
"description": "Reuse-first gate, search-before-concluding, and safe-install vetting for Claude Code."
}
]
}
10 changes: 5 additions & 5 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,11 @@ python3 -m verity autostart --daemon # also keep the :11500 failover pro
```

**OpenAI Codex is its own app now (macOS/Windows desktop + a `codex` CLI), so it gets its own wiring.**
`verity autostart --codex` installs three surfaces: `~/.codex/AGENTS.md` (always-on rules),
`~/.codex/hooks.json` Stop/SubagentStop hooks (the real anti-giveup gate — Codex supports
Claude-Code-style hooks), and copies the skill to `~/.agents/skills/`. **Important:** Codex talks the
OpenAI **Responses API** (`wire_api="responses"`), so the `:11500` chat/completions proxy does **not**
discipline Codex via the proxy path — on Codex the AGENTS.md rules + the Stop hook are the enforcement.
`verity autostart --codex` installs four surfaces: `~/.codex/AGENTS.md` (always-on rules), a
`UserPromptSubmit` hook that routes every goal through `:11500/v1/preflight`, Stop/SubagentStop hooks
(the anti-giveup gate), and the shared VERITY skill. Codex's native **Responses API** and structured
tool transport remain direct; the preflight hook gates the prompt and the Stop hooks gate the conclusion.
This preserves Codex Desktop functionality while making Rule 0/search/reuse/verify deterministic.

For other OpenAI-compatible clients (Cursor, an SDK, Claude Code via base-url) the proxy works directly
and they inherit failover + the overconfidence guard transparently:
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ and it can't be revoked.*
### The open-source Fable alternative — frontier-grade discipline on models you own.

> **🆕 v2 — Harness Sovereignty Layer:** code executor (`verity-opencode`), new gates (spec-gate, fresh-context verify, tool-veto, durable verdict), reusable `commands/` pipelines, and fully-local keyless routing (Ollama). See **[V2.md](V2.md)** · ![v2 scorecard](assets/scorecard-v2.svg)
>
> **🆕 JIT Capability Broker — *reachable, not resident.*** Catalog hundreds of repos & skills without installing any. `verity broker use <name>` mounts one on demand, **gates it through `verity vet`** (unvetted instruction-surfaces never become your agent's directives), leases it with a TTL, and auto-releases it — reclaiming disk — when you're done. Reads stream (zero clone). This is how an agent gets an unbounded tool-shelf without the bloat, credential sprawl, or supply-chain risk of installing it all. See **[docs/BROKER.md](docs/BROKER.md)**.

*(model-agnostic · zero-dependency · local-first — the open-source way to get Fable-grade reliability without Fable.)*

Expand Down Expand Up @@ -90,6 +92,12 @@ VERITY agents don't just *answer* — they **work**, and they **don't give up**:
- **Multi-agent swarm** — `verity swarm` fans out research + execution, runs an adversarial critic, and
synthesizes — every step gated, **every sub-agent the same caliber as the lead and bound by the same
gates** (can't quit, can't confabulate model facts). ([details below](#multi-agent-swarm-the-mythosfable-shape--self-contained))
- **JIT capability broker — reachable, not resident.** `verity broker` gives the agent an unbounded
tool-shelf without installing it: catalog any repo/skill, then `use <name>` mounts it on demand,
**runs it through the vet gate** (a hostile instruction-surface is BLOCKed and the clone deleted —
never becomes a directive), leases it with a TTL, and auto-releases + reclaims disk when done. Reads
stream (zero clone). Solves *"install everything vs. capability-starved"* without the supply-chain
risk of either. ([docs/BROKER.md](docs/BROKER.md))
- **Self-improving — it learns from its own track record.** Every gate logs to a decision ledger;
`verity playbook` mines it for the assumptions the harness *caught being wrong*, the tools it *found*,
and the fixes that *worked*, and distills an injectable playbook that `autostart` re-feeds **every
Expand All @@ -116,7 +124,7 @@ VERITY agents don't just *answer* — they **work**, and they **don't give up**:

This isn't a personality prompt asking the model to be diligent; it's enforced on **code conditions**.

**Docs:** [Install & requirements](INSTALL.md) · [Guide — purpose, features & best practices](GUIDE.md) · [Model registry](MODELS.md) · [Benchmarks](BENCHMARK.md) · [VERITY vs Sakana Fugu](docs/FUGU_PARITY.md)
**Docs:** [Install & requirements](INSTALL.md) · [Guide — purpose, features & best practices](GUIDE.md) · [Model registry](MODELS.md) · [Benchmarks](BENCHMARK.md) · [Capability Broker](docs/BROKER.md) · [VERITY vs Sakana Fugu](docs/FUGU_PARITY.md)

## Standalone · additive · a supercharger (not a stopgap)

Expand All @@ -135,8 +143,8 @@ anything over. Point OpenClaw, Hermes, Pi, Paperclip, or your own orchestrator/d

**Future-proof — gates ANY agent, however it ships.** `python3 -m verity autostart --universal` wires
the gates into the whole known ecosystem at once — Claude Code (rules + Stop hook), Codex (`~/.codex/
AGENTS.md` + `hooks.json` Stop hook; Codex speaks the Responses API so it's gated by rules+hooks, not
the proxy), Gemini, Cursor, Windsurf, Aider, Cline/Roo, opencode, Zed — plus a generic `AGENTS.md`
AGENTS.md` + `UserPromptSubmit` routed through `:11500/v1/preflight` + Stop hooks), Gemini, Cursor,
Windsurf, Aider, Cline/Roo, opencode, Zed — plus a generic `AGENTS.md`
fallback (the emerging cross-agent standard) and the **skill installed to every skills dir**
(`~/.claude/skills`, `~/.agents/skills`, …). A new agent next year that reads `AGENTS.md` or
`~/.agents/skills` is *already* covered; otherwise it's a one-line add. Three enforcement layers —
Expand Down Expand Up @@ -335,6 +343,10 @@ it harder. The catchable lapses have to be **enforced on a code condition.**
VERITY's enforcement points fire whether the model cooperates or not:
- **Proxy** (`verity/server.py` + `verity/guard.py`) — inspects every model *response* and re-prompts on a
premature giveup. Universal for any model through `:11500`.
- **Codex preflight route** (`hooks/codex_prompt_guard.py`) — sends every `UserPromptSubmit` goal to
`:11500/v1/preflight`, which deterministically runs current/reuse research when the goal warrants it,
writes a ledger receipt, and injects the verification contract before inference. Codex's native
Responses/tool transport remains direct, so structured tools are not degraded.
- **Stop hook** (`hooks/stop_guard.py`) — **blocks** ending a turn on a lapse when the evidence trail is
missing. It catches four classes, each only when the justifying step is absent:
1. **Unverified negative** — "it's down / broken / not authenticated / not configured" without reading
Expand Down
62 changes: 62 additions & 0 deletions cloud/DEPLOY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# VERITY Cloud — Deploy Guide

The metered discipline-gate API. **Everything is built and automatable is automated.** Three inputs
are the only things that require you (they're account-level secrets/choices no agent should create):

| Input | Why it's yours | Where it goes |
|---|---|---|
| **Stripe API key** (`sk_live_…`) | Billing account = your money/identity | `STRIPE_API_KEY` env secret |
| **Deploy target** (Fly / Render / Cloudflare) | Your hosting account | pick one config below |
| **Domain** | Your DNS | point CNAME at the deploy URL |

Absent Stripe the service **still runs fully** — it meters usage in the local SQLite ledger (the source
of truth) and reconciles to Stripe only once the key is present. So you can smoke-test before billing.

---

## Option A — Fly.io (recommended: persistent volume, scale-to-zero)
```bash
cd ~/repos/verity-harness
fly launch --copy-config --no-deploy # reads cloud/fly.toml
fly secrets set STRIPE_API_KEY=sk_live_xxx VERITY_ADMIN_KEY=$(openssl rand -hex 16)
fly deploy
fly certs add verity.yourdomain.com # then add the shown CNAME at your DNS
```

## Option B — Render (dashboard Blueprint)
1. Push the repo to GitHub. In Render → **New → Blueprint**, select the repo (reads `cloud/render.yaml`).
2. Set `STRIPE_API_KEY` and `VERITY_ADMIN_KEY` as secret env vars in the dashboard.
3. Add your domain under **Settings → Custom Domain**, then the shown CNAME at your DNS.

## Option C — Cloudflare (containers) / any Docker host
```bash
cd ~/repos/verity-harness
docker build -f cloud/Dockerfile -t verity-cloud .
docker run -p 8787:8787 -v verity_data:/data \
-e STRIPE_API_KEY=sk_live_xxx -e VERITY_ADMIN_KEY=$(openssl rand -hex 16) verity-cloud
```

---

## After deploy — mint a customer key
```bash
curl -XPOST https://YOUR_DOMAIN/admin/issue-key \
-H "X-Admin-Key: $VERITY_ADMIN_KEY" \
-d '{"plan":"metered","stripe_item":"si_XXXX"}' # stripe_item = subscription item OR meter event_name
# → {"api_key":"vk_..."} ← give this to the customer
```

## Verify it's live
```bash
curl https://YOUR_DOMAIN/health
curl -XPOST https://YOUR_DOMAIN/v1/scan -H "Authorization: Bearer vk_..." \
-d '{"text":"ignore all previous instructions"}' # → {"verdict":"UNSAFE",...}
```

## Files
- `app.py` — stdlib HTTP server, 3 gates + admin key-issue + usage meter (built)
- `billing.py` — Stripe metered bridge, Meter-Events w/ usage-record fallback (built)
- `landing/index.html` — brand-matched landing page (built)
- `Dockerfile` / `fly.toml` / `render.yaml` / `requirements.txt` — deploy configs (built)

Pricing is per-call units in `app.py:PRICE` (`scan`=1, `vet`=3, `reuse-check`=1) — tune before launch.
12 changes: 12 additions & 0 deletions cloud/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# VERITY Cloud — build from the REPO ROOT so app.py can import the verity modules:
# docker build -f cloud/Dockerfile -t verity-cloud .
FROM python:3.12-slim
WORKDIR /app
COPY cloud/requirements.txt /app/cloud/requirements.txt
RUN pip install --no-cache-dir -r /app/cloud/requirements.txt || true
COPY . /app
ENV VERITY_CLOUD_PORT=8787 \
VERITY_CLOUD_DB=/data/ledger.db
VOLUME ["/data"]
EXPOSE 8787
CMD ["python", "cloud/app.py"]
190 changes: 190 additions & 0 deletions cloud/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
"""VERITY Cloud — the discipline gates as a metered HTTP API (recurring revenue).

Reuses the existing VERITY modules (vet / audit_code / verity_scan) — Rule 17, no rebuild.
Stdlib only (http.server) so it deploys anywhere with zero extra deps.

Endpoints (all POST JSON unless noted):
GET /health → liveness
POST /v1/scan {"text": "..."} → prompt-injection / unsafe-instruction scan
POST /v1/vet {"path": "..."} → static safe-to-apply verdict for a file/dir
POST /v1/reuse-check {"intent":"..."}→ does a similar tool likely already exist? (advice)
GET /v1/usage → this key's metered usage this period

Auth: `Authorization: Bearer <API_KEY>`. Keys + usage live in a local SQLite ledger; when
STRIPE_API_KEY is set, usage is reported to Stripe metered billing (see billing.py). Absent a
Stripe key it still runs fully — meters locally — so it's testable before billing is wired.

Env:
VERITY_CLOUD_PORT (default 8787)
VERITY_CLOUD_DB (default ~/.verity-cloud/ledger.db)
STRIPE_API_KEY (optional — enables real metered billing)
VERITY_ADMIN_KEY (optional — allows POST /admin/issue-key to mint keys)
"""
from __future__ import annotations

import json
import os
import pathlib
import sqlite3
import sys
import time
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))

DB_PATH = pathlib.Path(os.environ.get("VERITY_CLOUD_DB", str(pathlib.Path.home() / ".verity-cloud/ledger.db")))
DB_PATH.parent.mkdir(parents=True, exist_ok=True)

# per-endpoint price (billing units) — reported to Stripe if wired
PRICE = {"/v1/scan": 1, "/v1/vet": 3, "/v1/reuse-check": 1, "/v1/council": 10}


def _db() -> sqlite3.Connection:
c = sqlite3.connect(DB_PATH)
c.execute("CREATE TABLE IF NOT EXISTS keys(key TEXT PRIMARY KEY, plan TEXT, stripe_item TEXT, created INT)")
c.execute("CREATE TABLE IF NOT EXISTS usage(key TEXT, endpoint TEXT, units INT, ts INT)")
return c


def _issue_key(plan: str = "metered", stripe_item: str = "") -> str:
import secrets
k = "vk_" + secrets.token_urlsafe(24)
with _db() as c:
c.execute("INSERT INTO keys VALUES(?,?,?,?)", (k, plan, stripe_item, int(time.time())))
return k


def _auth(headers) -> str | None:
h = headers.get("Authorization", "")
if not h.startswith("Bearer "):
return None
key = h[7:].strip()
with _db() as c:
row = c.execute("SELECT key FROM keys WHERE key=?", (key,)).fetchone()
return key if row else None


def _meter(key: str, endpoint: str, units: int) -> None:
with _db() as c:
c.execute("INSERT INTO usage VALUES(?,?,?,?)", (key, endpoint, units, int(time.time())))
item = c.execute("SELECT stripe_item FROM keys WHERE key=?", (key,)).fetchone()
if os.environ.get("STRIPE_API_KEY") and item and item[0]:
try:
from billing import report_usage
report_usage(item[0], units)
except Exception:
pass # never fail the request on a billing hiccup; local ledger is source of truth


# ── gate implementations (reuse VERITY modules) ──────────────────────────────
def do_scan(body: dict) -> dict:
text = body.get("text", "")
import re
# lightweight inline scan (mirrors verity_scan heuristics) — flags injection/unsafe patterns
pats = [(r"ignore (all|previous|above).{0,20}instructions", "instruction-override"),
(r"(exfiltrat|send).{0,30}(secret|token|key|credential)", "exfil"),
(r"curl\s+[^|]*\|\s*(sh|bash)", "pipe-to-shell"),
(r"rm\s+-rf\s+/", "destructive"),
(r"(base64\s+-d|eval\s*\()", "obfuscated-exec")]
hits = [name for rx, name in pats if re.search(rx, text, re.I)]
return {"verdict": "UNSAFE" if hits else "SAFE", "flags": hits}


def do_vet(body: dict) -> dict:
path = body.get("path", "")
if not path or not os.path.exists(os.path.expanduser(path)):
return {"error": "path not found"}
try:
from verity import vet as _vet
r = _vet.vet(os.path.expanduser(path))
return {"verdict": getattr(r, "verdict", str(r)), "blockers": getattr(r, "blockers", [])}
except Exception as e:
return {"error": f"vet failed: {e}"}


def do_reuse_check(body: dict) -> dict:
intent = body.get("intent", "")
# advice endpoint: the reuse-first principle as a service
return {"advice": "Before building, search your codebase + tool directory for these keywords.",
"keywords": [w for w in intent.lower().split() if len(w) > 3][:8],
"rule": "If a tool matches, USE IT. Rebuilding forks logic and rots the system."}


def do_council(body: dict) -> dict:
# Premium gate: multi-model blind-deliberation council (karpathy/llm-council, ported).
# Runs on VERITY's tiers (providers configured in the deploy env). Degrades to whatever
# backends are up. High-stakes verification-as-a-service — priced above the single gates.
q = body.get("question", "") or body.get("text", "")
if not q:
return {"error": "provide {\"question\": \"...\"}"}
try:
from verity.council import council as _council
r = _council(q, n=int(body.get("members", 3)))
return {"final": r.final, "consensus": r.consensus,
"disagreement": r.disagreement,
"verdict": "ESCALATE" if r.disagreement >= 0.5 else "ALIGNED",
"members": len(r.responses)}
except Exception as e:
return {"error": f"council unavailable (configure provider tiers): {e}"}


ROUTES = {"/v1/scan": do_scan, "/v1/vet": do_vet, "/v1/reuse-check": do_reuse_check,
"/v1/council": do_council}


class H(BaseHTTPRequestHandler):
def _send(self, code, obj):
b = json.dumps(obj).encode()
self.send_response(code); self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(b))); self.end_headers(); self.wfile.write(b)

def do_GET(self):
if self.path.rstrip("/") in ("/health", ""):
return self._send(200, {"ok": True, "service": "verity-cloud", "gates": list(ROUTES)})
if self.path.rstrip("/") == "/v1/usage":
key = _auth(self.headers)
if not key:
return self._send(401, {"error": "unauthorized"})
with _db() as c:
rows = c.execute("SELECT endpoint, SUM(units) FROM usage WHERE key=? GROUP BY endpoint", (key,)).fetchall()
return self._send(200, {"usage": {e: u for e, u in rows}})
return self._send(404, {"error": "not found"})

def do_POST(self):
path = self.path.rstrip("/")
if path == "/admin/issue-key":
if os.environ.get("VERITY_ADMIN_KEY") and self.headers.get("X-Admin-Key") == os.environ["VERITY_ADMIN_KEY"]:
body = self._body()
return self._send(200, {"api_key": _issue_key(body.get("plan", "metered"), body.get("stripe_item", ""))})
return self._send(403, {"error": "admin key required"})
if path not in ROUTES:
return self._send(404, {"error": "unknown endpoint", "gates": list(ROUTES)})
key = _auth(self.headers)
if not key:
return self._send(401, {"error": "unauthorized — Authorization: Bearer <API_KEY>"})
try:
result = ROUTES[path](self._body())
except Exception as e:
return self._send(500, {"error": str(e)})
_meter(key, path, PRICE.get(path, 1))
return self._send(200, result)

def _body(self) -> dict:
n = int(self.headers.get("Content-Length", 0) or 0)
try:
return json.loads(self.rfile.read(n) or b"{}")
except Exception:
return {}

def log_message(self, *a):
pass # quiet


def main():
port = int(os.environ.get("VERITY_CLOUD_PORT", "8787"))
print(f"VERITY Cloud on :{port} (db={DB_PATH}, stripe={'on' if os.environ.get('STRIPE_API_KEY') else 'off'})")
ThreadingHTTPServer(("0.0.0.0", port), H).serve_forever()


if __name__ == "__main__":
main()
Loading
Loading