DEV Community

Cover image for HOL Guard 3.0: Rust authority, extension-first controls, honest harness contracts
Michael Kantor for HOL (Hashgraph Online)

Posted on Originally published at hol.org

HOL Guard 3.0: Rust authority, extension-first controls, honest harness contracts

Originally published at HOL

Your agent proposes aws s3 rb s3://prod-artifacts --force in the same session where it was helpful five minutes ago. You are mid-review. YOLO is on somewhere. The bucket name looks right enough that a tired operator might hit allow. That is the failure mode Guard 3.0 is built around: not abstract model risk, but a concrete shell string that empties production before anyone finishes reading the diff.

Stable v3.0.0 cut on August 27, 2026. Current tip to pin is 3.0.113 (Production/Stable). PyPI yanked 3.0.0, so do not put that cut tag in a lockfile, Dockerfile, or CI pin. Use a living 3.0.x tip.

What broke in the 2.x mental model

Pre-3.0 Guard already intercepted harnesses, paused on new artifacts, routed approvals, and wrote receipts. Cloud stayed optional. Teams still bounced between three half-answers when something destructive showed up: which editor owned the detector, which surface owned the allow memory, and which harness hook folklore was currently true.

We kept Local Extensions and Cloud policy language looking like parallel systems. Capability identity drifted from governance objects. Operators could not tell whether a deny came from a floor, a remembered approval, or a workspace rule. That ambiguity is what we killed.

Extensions define what HOL Guard can protect. Guard Cloud Control Sets define how that protection is governed across people, devices, agents, environments, and time.

That is the ADR 0011 product sentence, quoted once. Local owns capability identity and detector facts. Cloud owns Control Sets, Deployments, acknowledgement, drift, pause, rollback, and audit. ADR also rejected paywalling Local Extensions and rejected giving every Cloud rule absolute precedence in the 3.0 release. Contextual policy precedence stays unless a separately negotiated contract says otherwise.

Rust owns the authority path

Every supported harness routes PreToolUse security semantics through a bundled, version-matched Rust runtime. Supported PostToolUse output review does too. There is no Python semantic fallback on that path in 3.x. That is the hard boundary, not a performance footnote.

The flow is boring on purpose:

harness tool call
  -> Guard hook (PreToolUse)
  -> version-matched Rust runtime
  -> CanonicalCommand parse-once
  -> Extension permission detectors (evidence only)
  -> local floors + device settings + remembered rules
  -> optional Cloud Control Set (managed-restrictive)
  -> allow | ask | deny (fail closed on authority faults)
Enter fullscreen mode Exit fullscreen mode

Shell text becomes a CanonicalCommand once: segments, wrappers, pipelines, redirects, security identity. Detectors emit evidence. They do not grant authority. A safe preview segment such as git clean -ndx does not wash away a later destructive segment on the same line. Help and describe forms suppress only their owning rule and segment. Cloud matrices match executable and subcommand structure rather than English words like delete. Reordered global flags do not change recognition, which matters when an agent rewrites aws s3 rb --force s3://... into a flag order you have not memorized.

Identity, protocol, rule-digest, policy-snapshot, overload, timeout, transport, and response failures fail closed on the authority path. Soft-allow on those faults is not a degraded mode we keep. The v3.0.0 notes put the resident Rust runtime at roughly 29,000 lines shipped in-wheel. Version-matched wheels live inside the Guard package so the native binary and the Python package cannot silently drift.

Python still handles authenticated transport, harness-specific rendering, approval coordination, the dashboard control plane, and bounded non-authoritative evidence. The ownership gate exists specifically to stop someone from reintroducing Python command evaluation as PreToolUse authority when Rust is inconvenient.

Extension-first controls and the cloud matrices

An Extension is a stable Local capability boundary for a tool or domain. A Permission is an independently configurable capability inside that Extension. Detector rules recognize evidence for a Permission. Local settings and remembered rules may tighten protection. They cannot lower required floors or weaken managed restrictions.

Authority modes are personal-shared, workspace-shared, and managed-restrictive. The product-decision contract sets local_registry_authoritative and cloud_must_not_redefine_detector_matchers. Cloud targets Extension and permission identities published by the local registry. It does not become a second matcher editor.

In release 3.0, managed-restrictive actions are only disable-extension, disable-permission, and global-lockdown. Exceptions are time-bounded and cannot weaken those blocks. Composition is monotonic for non-weakenable authority. That is narrower than a full Cloud policy language, and that narrowness is intentional.

Built-in cloud destructive-operation matrices at the v3.0.0 cut: 200 AWS, 100 gcloud, and 100 Azure matrix-reviewed operations. The AWS matrix ships as two non-overlapping 100-operation batches. Directory UIs may show 1 rule on a command.cloud.* Extension. That column is not the operation count. The Extension wraps a matrix. Confirm on your tip with hol-guard command extensions.

Required core Extensions cover filesystem, git, system, windows, and guard-self-protection. They are built-in capability boundaries over the same parser used by harness hooks, not downloadable regex bundles.

External community contribs (blitcp, Noodle, Probe, repo2nb, Skill Sunset) stay off until a local-admin enable. Cloud cannot flip External on by itself.

Delegated package-manager protection points at the package-firewall contract rather than duplicated command-policy rules.

Install intent, package metadata, and the final pre-execution Guard result ride the shared runtime envelope before an approval is queued. Saved artifact-level package blocks stay sticky until a new allow rule or exception exists.

hol-guard command test 'aws s3 rb s3://prod-bucket --force'
hol-guard command explain 'gcloud sql instances delete my-db --quiet'
hol-guard command extensions
Enter fullscreen mode Exit fullscreen mode

command test and command explain are side-effect-free. They do not execute the command, create an approval, or write a receipt. Use them after every upgrade before you trust muscle memory on az group delete or php artisan migrate:fresh.

Protection Center, Test Lab, Core vs Desktop

Protection Center is the operator control plane for command protection modules. Open it from Modules in the local dashboard. Compatibility routes for /extensions remain. Posture choices are Protected, Extra careful, or Watch. Default is Protected.

Protected stops theft, wipes, and Guard bypass, then remembers ordinary first-seen decisions. Extra careful also asks on first new site or tool install. Watch records without stopping. Use Watch only while debugging false positives. If Watch becomes your standing posture, you are collecting receipts while the blast radius stays live.

hol-guard settings set protection protected
hol-guard command controls status
hol-guard command controls list
hol-guard command controls show command.git
hol-guard command controls patterns --tool command.git
hol-guard command controls preview command.git --target-kind extension --state disabled
Enter fullscreen mode Exit fullscreen mode

Those inspect or preview local state. They do not contact Cloud. Writes still go through preview, proof, approval, and apply. A device setting can preserve or tighten protection. It cannot lower an immutable floor, override Emergency Lockdown, or weaken a workspace managed restriction.

Test Lab rehearses a bounded command against current local protection without executing it. No subprocess. No activity row. No approval. No Cloud upload. No raw command in the response payload. Same contract as command explain: explanatory, not a sandbox.

We moved the menu-bar tray to hashgraph-online/hol-guard-desktop. Guard Core stays headless and owns enforcement: policy, approvals, receipts, daemon, browser dashboard, updates, repair, diagnostics. Desktop is optional. If the tray vanished after upgrade, you lost a UI process, not the authority path. Desktop is not required for blocking.

Protection Center is presentation over the same extension-control catalog, effective state, preview, proof, apply, and runtime evaluation contracts used by the CLI. It must not invent separate enforcement semantics. Simple mode is the default; Advanced and Developer modes reveal implementation detail. Organization-managed settings stay read-only on the device. Settings history comes from the authenticated local authority transition chain: picking an earlier version prepares only the historical device layer as a draft against the current revision, with the organization layer still in force, and still requires the normal proof-bound approval flow.

Local decision integrity and evidence privacy

Saved local decisions are verified against Guard-maintained integrity key material before they become authoritative again. Coming from older local memory, migrate deliberately:

hol-guard policies integrity-status
hol-guard policies migrate-local-integrity --preserve-all-local
hol-guard policies verify
Enter fullscreen mode Exit fullscreen mode

Tampered or recovery-required authority state goes through the approval-bound Settings integrity repair flow in Protection Center. Do not hand-edit the authority database. Unsigned, unknown-key, or tampered local decision rows should fail closed until an operator repairs them.

Command activity is a local security-evidence domain. It records bounded facts about Guard decisions and proof metadata, not command content. Opaque activity references, harness, phase, status, proof level, bounded reason classes, extension and rule identities: yes. Raw commands, arguments, paths, workspace names, package names, environment values, credentials, matcher free-text with user input: no. Positive field allowlists enforce that. Redaction alone is not the contract.

Correlation handles use a local HMAC-SHA-256 over length-framed harness identifiers with an installation-local random key. Session-only correlation never proves execution. Default egress is local-only. Optional Cloud egress, when opted in, accepts only rare-cell-suppressed daily aggregate counts, not activity rows or receipt payloads. Catalog and control status stay bounded security metadata for the same reason: support evidence must not become a side channel for shell text.

Harness contracts, stated honestly

Support is not identical across harnesses. Treat the matrix as contracts, not slogans. Guard's own authority path fails closed on the faults listed above. Harness adapters still differ when Guard itself is unreachable or a hook crashes.

  • Codex: Guard-owned PreToolUse and PermissionRequest hooks are authoritative, including deny in YOLO mode. Wrapper launch refuses when native hooks are missing or disabled. Thread resume works when a live binding exists.
  • Grok: waits on the original PreToolUse after queuing approval, then returns allow so Grok resumes the same tool call. Fails open if a hook crashes or times out.
  • Kimi: fails open on crash or timeout so Kimi Code keeps working when Guard is unreachable.
  • ZCode: fails open on crash or timeout.
  • Cline: evaluates PreToolUse synchronously and fails closed when Guard is unavailable or payloads are contradictory.
Harness     | Guard crash / unavail          | Resume note
Codex       | refuses missing native hooks   | thread resume when bound
Grok        | fail-open on crash/timeout     | same-call resume
Kimi        | fail-open on crash/timeout     | keeps working
ZCode       | fail-open on crash/timeout     | keeps working
Cline       | fail-closed on unavail/bad payload | sync PreToolUse
Enter fullscreen mode Exit fullscreen mode

Codex refusing a launch without native hooks is a feature. Grok, Kimi, and ZCode failing open means Guard unreachable is not the same as Guard deny. Cline failing closed means an unavailable Guard stops action-bearing tools. Read harness-support.md for the harness you actually run, then prove it with status and command explain on that machine.

No VS Code Copilot extension-host claim. A VS Code inline permission sheet alone is not Guard proof. No Goose adapter in this repository. No Cisco AIBOM runtime policy in this pass.

Local floor stays up when Cloud pauses

Local Guard without sign-in still intercepts, blocks on supported actions, writes receipts, and resolves approvals. Cloud outage, trial end, past due, or never connected must not be translated into protection expired. That mistranslation is a product bug.

Sync paused is not unprotected. Codes like feature_not_in_plan, device_limit_reached, and cloud_sync_paused_plan_limit describe Cloud state only. Solo's two-device limit is a Cloud-sync limit. A third machine still installs and protects locally.

Team Cloud is where managed-restrictive Control Sets become interesting: shared workspaces, simulation, staged rollout, acknowledgement, drift, rollback, RBAC, and evidence exports. Deployment is a signed Control Set version delivered to an eligible runtime cohort. Stages fail closed independently. Compatibility evaluation and atomic application have to land before managed restrictions are enabled on a cohort; unsupported clients must not receive a fallback that silently drops Extension enforcement semantics.

Cloud adds sync, history, Control Sets, simulation, staged rollout, acknowledgement, drift, rollback, and shared review. A paused sync stage does not mean Local Guard stopped protecting the shell. A catalog mismatch excludes a device from that Cloud rollout; local enforcement continues. Invalid signed bundles follow the incident runbook; do not describe local protection as disabled merely because Cloud sync failed.

Day one on a laptop still looks familiar: install Guard Core, run init or install for the harness you use, confirm status, then rehearse a destructive class with command explain before you trust muscle memory. Decision-integrity tooling and receipts continue offline.

Upgrade once, prove four green bars

Prefer the current tip. Do not pin yanked 3.0.0. Repair the harnesses you use. Prove recognition with command explain. Migrate local decision integrity if you are coming from older local memory.

uv tool install --force "hol-guard[cisco]==3.0.113"
hol-guard --version
hol-guard init
hol-guard install codex
hol-guard apps repair grok
hol-guard status
hol-guard command explain 'aws s3 rb s3://prod-bucket --force'
hol-guard command test 'az vm delete -g rg -n vm1 --yes'
hol-guard command controls status
hol-guard policies integrity-status
hol-guard policies migrate-local-integrity --preserve-all-local
Enter fullscreen mode Exit fullscreen mode
  • Package reports a 3.0.x version at or above 3.0.113.
  • status shows the harness protected with hooks present.
  • command explain names a built-in Extension and permission evidence for a destructive class you care about.
  • policies integrity-status is clean or successfully migrated.

Then open Protection Center once and set posture deliberately. Do not inherit 2.x muscle memory for Watch. After that, the product model is simple enough to hold in one head: Extensions name what can be protected, Rust decides before the shell starts, Control Sets govern those identities across people and devices, and Cloud sync state never gets to redefine whether the local floor is still standing.

If marketing surfaces or an old README still talk early 3.x language, ignore them for pins. Treat the installed package version, the v3.0.0 release notes, ADR 0011, and product-decision.json as the operator sources of truth. After the four green bars, skip changelog side quests until you have actually watched one destructive class land as Extension permission evidence on your harness.

References

Top comments (0)