No description
Find a file
2026-07-12 17:24:29 -06:00
config showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
samples/demo-gates showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
schema showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
tests showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
ward showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
.gitignore showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
conftest.py showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
LICENSE showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
pyproject.toml showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00
README.md showcase: ward — curated public snapshot (AGPL-3.0-only) 2026-07-12 17:24:29 -06:00

Ward — guardrail-integrity monitor

Ward answers one question, continuously and provably: are the enforcement guardrails that gate an AI agent's risky actions still exactly what the operator blessed — un-forged, un-drifted, and still wired in?

A capable AI agent runs behind a set of guardrails: pre-tool-use hooks that block a deploy without an operator approval, a planning-mode gate, shared classifier libraries, and the operator-only "write half" that mints an approval marker. Those guardrails are just files on a host. If one is silently edited, unwired, or deleted, the agent looks healthy while its safety net has a hole. Ward is the independent monitor that catches exactly that — without ever being able to weaken the thing it watches.

The core invariant — courier, never notary

Ward is read-only and holds no signing key. It enumerates and hashes the guardrail artifacts and compares live state against an operator-signed baseline — but it can neither change a guardrail nor mint a baseline. The operator, out-of-band and with a key the monitor never sees, is the notary; Ward is only the courier that recomputes live state and reports the diff.

That boundary is the whole design. A compromised monitor is a classic weak link: if the watcher can bless its own baseline, an attacker who owns the watcher owns the guardrails too. Ward closes that by construction — the private key lives off the box, the monitor verifies against a pinned public key, and a self-generated key + self-made signature is refused because it cannot match the operator-anchored fingerprint (TOFU, the same trust model cosign / TUF / SSH known_hosts accept).

How it works

Three stages, mapping to the modules below.

W1 — enumerate + canonically hash (ward/manifest.py). Walk a declared set of guardrail artifacts (enforcement hooks, gate libraries, marker writers, the operator write-half, and the settings hooks subtree that wires them in) and emit a stable JSON manifest. Each artifact carries its sha256, mode/uid/gid, symlink target, and a wired verdict (is it actually referenced by the enforcement wiring, or an orphan?). The signable manifest_digest is a sha256 over the canonical artifacts array only — volatile metadata (timestamp, host) is excluded, so an unchanged host yields a byte-identical digest every run. A missing artifact is recorded as a finding, never a silent skip.

W2 — the sign ceremony (ward/ceremony.py, baseline_sign.py, baseline_bless.py). The AI stages a baseline (builds the manifest, writes the detached digest, inserts an inactive, unsigned row) and prints the exact one-liner the operator runs in their own terminal to sign the digest. The AI then blesses it: it verifies the operator's detached signature against the pinned public key and, only on success, pins the key and flips the row to active. Every failure path — wrong key, corrupt signature, unconfirmed fingerprint, a whole guardrail set absent — refuses and leaves the trust anchor unpinned.

W3 — the fail-closed scan (ward/scan.py, comparator.py, verify.py). The load-bearing order is non-negotiable: verify the baseline signature before any comparison. A clean verdict is reachable only through a verified signature — an unverified scan with zero drift is still unverified, never clean. Silence is never clean. Once verified, the comparator diffs live vs baseline artifact-by-artifact; a byte change on an enforcement hook or the operator write-half is a CRITICAL, and a gate that vanished or an unblessed gate that appeared is itself a finding.

Layout

Path Role
ward/manifest.py W1 — gate enumerator + canonical, stable, signable hasher
ward/trust.py pinned public keys (TOFU anchors); the only place a key becomes authoritative
ward/verify.py signature verification with algorithm dispatch (ed25519 native; cosign wired)
ward/baseline.py load the single active + blessed baseline; resolve pin + TOFU cross-check
ward/comparator.py per-artifact live-vs-baseline byte/metadata diff
ward/findings.py finding model, stable dedup key, regressed transition
ward/scan.py the fail-closed orchestrator (verify-before-compare)
ward/ceremony.py the W2 stage → bless sign ceremony
ward/store.py DB-agnostic baseline row store (JSON-file stand-in for Postgres)
ward/cli.py read-only JSON manifest emitter
schema/*.sql illustrative Postgres schema (findings, baseline, per-run ledger) with the invariant encoded as CHECK constraints
samples/demo-gates/ a small, synthetic guardrail tree the tests run against
tests/ the W1/W2/W3 suite, including the fail-closed and forge-resistance proofs

Run it

python -m venv .venv && .venv/bin/pip install -e '.[dev]'
.venv/bin/python -m pytest -q

# point Ward at your own guardrail tree and print the manifest:
WARD_GATE_ROOT=samples/demo-gates .venv/bin/python -m ward.cli
WARD_GATE_ROOT=samples/demo-gates .venv/bin/python -m ward.cli --digest

The test suite is self-contained: it builds every manifest from the bundled samples/demo-gates tree and simulates drift by mutating a copy of the manifest — no real file is ever touched, and no host's actual guardrail topology is present in this repository.

About this repository

This is a curated, self-contained snapshot published as an engineering showcase. The gate topology here is deliberately generic (WARD_GATE_ROOT + a demo tree); a real deployment supplies its own set. The reusable, portfolio-relevant part is the architecture: read-only integrity monitoring, an operator-signed baseline the monitor cannot forge, canonical stable hashing, and a fail-closed verify-before-compare scan.

License

AGPL-3.0-only — see LICENSE for the full text.

A commercial license is available for use outside the AGPL-3.0 terms — contact Info@Seglamater.com. (Formal commercial-license terms are being finalized.)

Versions previously published under the Business Source License 1.1 remain available under BSL-1.1.

Copyright © 2026 Seglamater Services LLC.