Originally published at HOL
The Release
HOL Guard 2.1 changes 575 files, adds 107,000 lines, and ships a 51,000-case command classification corpus that assigns every AI-generated shell command a security floor before the tool runs it. Every layer of the stack changed — the local daemon, the Codex hook pipeline, the dashboard UI, CI, the test harness.
Three things define this release:
A six-level action lattice (allow, warn, review, require-reapproval, sandbox-required, block) backed by a 51,000-case decision-diff corpus. Zero known gaps. Zero lowered decisions. The corpus ships with the code.
HMAC-backed dashboard reconnect that survives daemon restarts without dropping the trust relationship between the dashboard and its local server. Mutual challenge-response with replay protection.
Codex hook manifest signing that cryptographically binds hook identity so a compromised workspace cannot silently swap which hooks Guard trusts.
Behind those headlines: restricted archive downloads with DNS pinning, structured GitHub command capability modeling across shell/REST/GraphQL, cross-platform CI (Linux, macOS, Windows), a dashboard migration from npm to bun, and a TestPyPI canary pipeline that verifies installed-origin proof before any stable tag.
Command Classification: 51,000 Cases, Six Floors
Guard 2.1 replaces the previous allow/block binary with a six-floor action lattice. Every tool invocation gets exactly one floor. No configuration file can lower it.
allow — safe, no interaction
warn — notify but proceed
review — surface in the approval center
require-reapproval — block until developer approves in the dashboard
sandbox-required — isolate before touching the host
block — refuse execution
The corpus lives in tests/fixtures/guard-command-corpus/. Each of the 51,000 cases maps a command to its expected floor. CI runs a decision-diff report against every case on every push. A merge always requires review. A credential read always requires reapproval. A Guard configuration modification always blocks.
Classification rules sit in command_critical_floors.py (490 lines): merge, release, history, delete, secret, access, workflow, content, and dynamic mutations each get a fixed floor.
Dashboard Reconnect: HMAC Challenge-Response
Guard's dashboard connects to a local daemon over localhost. When the daemon restarts — after an update, a crash, a reboot — the old connection model had no way to confirm it was talking to the same installation.
The 2.1 protocol (dashboard_reconnect.py, 416 lines):
POST /v1/update/reconnect/prepare— the dashboard asks the current daemon for a signed authorization. The daemon persists areconnect_id, installation fingerprint, and HMAC-bound challenge to Guard's home directory.The daemon restarts. The dashboard probes candidate ports until it finds a
/healthzendpoint.POST /v1/update/reconnect/challenge— the dashboard challenges the recovered daemon. The daemon checks that the authorization is still fresh, then returns a server-side HMAC proof.The dashboard verifies the server proof, then sends
POST /v1/update/reconnect/verifywith its own client proof. The daemon checks the replay cache.Only after mutual verification does the dashboard resume status polling.
Every challenge binds to a specific installation ID, guard home ID, and compatibility version. Replays fail. Cross-installation challenges fail. Port mismatches fail. The dashboard test suite backs this with 302 lines of security-specific e2e tests (guard-update-reconnect-security.test.ts).
Hook Manifest Integrity and Archive Safety
Codex hooks intercept tool calls inside the developer's own process. Before 2.1, hook configuration lived in plain workspace files — an attacker with filesystem access could modify the identity record and redirect Guard's inspection pipeline.
Guard 2.1 introduces signed hook manifests (codex_hook_manifest.py, 495 lines). Each manifest carries the hook's identity, expected payload shape, and an HMAC signature from a private key stored in Guard's home directory — outside the workspace, with restrictive permissions and atomic writes. Guard verifies the signature at load time. A failed check logs a tamper event and refuses the hook.
Archive safety follows the same trust-nothing principle. Agents download archives frequently — model weights, package tarballs, training data. 2.1 routes every archive download through a five-module pipeline that enforces HTTPS-only transfers, DNS pinning, redirect limits (max 3, same-origin after the first), and bounded streaming with a size cap — then scans the result in a sandbox with no network access and dropped capabilities before the host filesystem ever sees it. Incomplete downloads land in temp; only verified completes get an atomic rename into the workspace.
GitHub Capability Modeling
gh pr merge, gh release create, gh secret set — GitHub CLI commands are among the most common things an AI agent runs and among the riskiest. Before 2.1, Guard pattern-matched against command strings. A reformulated command could slip past.
2.1 ships a typed capability model spanning six modules:
github\_capability\_contract.py (313 lines) — maps every GitHub operation to its required capability and security floor
github\_rest\_capabilities.py (304 lines) — REST API path classification
github\_graphql\_capabilities.py — GraphQL operation classification
github\_shell\_capabilities.py (497 lines) and github\_shell\_bindings.py (489 lines) — shell binding model mapping
ghcommands, flags, and arguments to capabilitiesgithub\_command\_capabilities.py — unified classification entry point
verified\_github\_reads.py (362 lines) — proven-read pattern allowing safe
ghreads through--jsonformatters
gh repo delete is blocked regardless of spelling. gh pr view --json number,title is allowed. gh issue create requires review. Classification follows what the command _does_ — not what it looks like.
CI, Canary Pipeline, Dashboard
Guard's build and release infrastructure grew substantially in 2.1:
Cross-platform CI across Linux, macOS, and Windows (Python 3.10–3.13). uv 0.9.26 pinned with its SHA-256 in the CycloneDX SBOM.
TestPyPI canary pipeline (
publish.yml, now ~930 lines): every release-branch push produces a unique per-PR canary wheel, verifies byte equality against the build artifact, installs on a clean environment, runs the full 51K-case corpus, and proves installed origin on all three OS targets.Dashboard migrated from npm to bun 1.3.14. Four new Playwright e2e specs cover reconnect, command activity, approval scope, and protection health — all targeting the installed wheel, not a dev checkout.
Command activity API (
command_activity_api.py, 474 lines) with privacy controls, authenticated totals, filters, detail views, and execution-proof copy.
Installing It
The 2.1 alpha ships to TestPyPI for canary testing. Stable releases, Docker images, and Homebrew updates follow.
pip install --index-url https://test.pypi.org/simple/ hol-guard
The repository is github.com/hashgraph-online/hol-guard. Apache 2.0.
Top comments (0)