INTRODUCTION
Guardrail is a local-first control layer for trusted CLI and agent-assisted automation. It records an execution boundary, reuses it while unchanged, and stops drift before process spawn.
THE PROBLEM
You approve npm test. A later automation run adds --silent. Nobody reviewed the extra argument. The command surface changed, and ordinary shell execution would not tell you that the approved contract no longer matches.
This is scope drift. It happens in build scripts, CI pipelines, AI-assisted workflows, and iterative repair loops. It's rarely malicious. It's almost always invisible.
terminal — drift blocked# what the agent tried to run guardrail run --non-interactive --approved-manifest .guardrail/approved.json -- npm test --silent # guardrail output ▶ Execution paused. Drift detected. approved args: ["test"] current args: ["test", "--silent"] DRIFT Exit 12. Re-run with explicit approval to widen scope.
WHAT GUARDRAIL IS NOT
- Not a sandbox. It does not isolate processes or restrict syscalls.
- Not a container replacement. It does not limit filesystem or network access.
- Not safe for untrusted binaries. It verifies what runs, not what it does once running.
- Not a security boundary. It's a contract layer. The host environment handles containment.
HOW IT WORKS IN 30 SECONDS
APPROVE to acknowledge it.INSTALLATION
Guardrail 1.0.0 requires Node.js 20 or newer. Until a verified package name is published, install from the source checkout.
SOURCE INSTALL
bashgit clone https://github.com/justguy/guardrail.git cd guardrail npm install npm link
Verify the install:
bashguardrail --version 1.0.0
WITHOUT LINKING
bash — from the checkoutnode src/cli.js --help node src/cli.js run -- npm test
guardrail is unrelated. Do not use npm install -g guardrail until this project publishes a verified distribution name.SYSTEM REQUIREMENTS
| Platform | Status | Notes |
|---|---|---|
| Runtime | Node.js 20+ | Uses ESM and Node's built-in test/runtime APIs. |
| Interactive approval | Real TTY | The first run prompts for the literal word APPROVE. |
| CI | Non-interactive | Pass an acknowledged manifest explicitly; missing or changed state fails closed. |
QUICK START
Run one command interactively, acknowledge its contract, then reuse that exact manifest in CI.
YOUR FIRST APPROVED COMMAND
Step 1 — Run and review
bash — interactive TTYguardrail run -- npm test Review the candidate contract and risk assessment. Type APPROVE to continue: APPROVE ✓ Acknowledged manifest stored. Executing.
Step 2 — Reuse the acknowledged manifest in CI
bash — non-interactiveguardrail run \ --non-interactive \ --approved-manifest .guardrail/approved.json \ -- npm test
Step 3 — Watch drift get caught
bashguardrail run -- npm test --silent ✕ Drift detected. Exit 12. Changes from approved manifest: + args[1]: --silent BLOCKED — not in approved scope Re-run interactively to review and acknowledge the changed contract.
CORE CONCEPTS
Five things to understand before everything else clicks.
THE MANIFEST
A manifest is the stored record of an acknowledged contract. It contains the normalized contract, contract hash, project root, risk assessment, acknowledgement fields, and workflow settings.
Command manifests use .guardrail/approved.json by default. Decide deliberately whether to version-control them; they include absolute project context and are not a cryptographic identity signature.
THE CONTRACT FIELDS
The contract is structured data: command, args, working directory, mode, readable/writable paths, environment policy, child-process policy, network policy, timeout, retry policy, and update policy. The hash binds those normalized fields.
RISK CLASSIFICATION
Every command gets a risk level computed by the engine:
Risk is computed from the contract and provenance. RED requires strong confirmation; all interactive approvals currently require typing APPROVE.
TRUST CLASSES
| Class | Meaning |
|---|---|
| reviewed_internal | First-party, committed to version control, manually reviewed |
| pinned_external | External source, pinned to an immutable commit SHA |
| generated | Produced by an LLM, script, or automated process |
| unknown | Provenance cannot be determined |
generated and unknown trust classes evaluate to RED. Reviewed or hash-pinned provenance is required before a contract can qualify for GREEN.FAIL CLOSED
When an approved contract is required but missing, changed, or unreadable, Guardrail does not silently execute. Non-interactive mode returns a distinct failure status instead of prompting or guessing.
MANIFESTS
The locally stored record of the contract and risk you acknowledged.
MANIFEST STRUCTURE
.guardrail/approved.json{ "version": 1, "tool": "guardrail", "approvedAt": "2026-08-24T14:01:22Z", "projectRoot": "/absolute/path/to/project", "contractHash": "a3f9c12e...", "contract": { "command": "npm", "args": ["test"], "mode": "structured", "cwd": "/absolute/path/to/project", "envPolicy": { "inherit": false, "allow": ["PATH"] } }, "riskAssessment": { "trustClass": "reviewed_internal", "riskLevel": "green", "requiresStrongConfirmation": false, "acknowledgedBy": "interactive_user" }, "workflow": { "validator": "exit_code", "updateSource": "none" } }
MANIFEST PATHS
| Type | Default path |
|---|---|
| Command manifest | .guardrail/approved.json |
| Workflow manifest | .guardrail/workflows/default.approved.json |
| Audit log | .guardrail/audit.jsonl (repo-local by default) |
RISK CLASSIFICATION
Computed by the engine. Never trusted from the manifest alone.
THE THREE LEVELS
| Level | Triggers | Examples |
|---|---|---|
| GREEN | Reviewed or pinned source, structured argv, safe binaries, local/temp writes, no env inheritance, and no destructive traits | Bounded local checks and status commands |
| YELLOW | Fallback when work is not RED but misses at least one GREEN condition | Package installs, scoped local writes, or shell mode without RED traits |
| RED | Generated/unknown source, production/system targets, outside-repo writes, sudo/admin commands, or destructive system-path work | Production deploys, system mutation, database/cloud administration |
RED ESCALATION TRIGGERS
generatedorunknownworkflow provenance- System paths, production-like targets, or writes outside the project boundary
- Elevated privileges, admin binaries, or database administration
- Destructive behavior aimed at system paths
- Secret-shaped env var with a production-like target
- Shell mode combined with install, download, or destructive behavior
requiresStrongConfirmation. In interactive mode, the operator must type APPROVE; delegated tools cannot self-approve through normal arguments.DRIFT DETECTION
Every execution is re-hashed and compared against the approved manifest. No silent pass-through.
WHAT COUNTS AS DRIFT
| Change | Result | Self-resolvable |
|---|---|---|
| New flag added | BLOCKED — Exit 12 | No |
| New env var accessed | BLOCKED — Exit 12 | No |
| Binary name changed | BLOCKED — Exit 12 | No |
| New target or host | BLOCKED — Exit 12 | No |
| Mode changed to shell | BLOCKED — Exit 12 | No |
| Risk escalation | BLOCKED — human required | No |
| Argument removed | DRIFT — Exit 12 | No |
| Exact normalized contract | ALLOWED | N/A |
STRUCTURED DRIFT OUTPUT
bash — CI-safe JSONguardrail run \ --json \ --non-interactive \ --approved-manifest .guardrail/approved.json \ -- npm test --silent # status: drift_detected · exitCode: 12 # drift.diffs contains the changed manifest fields
APPROVAL FLOW
Interactive acknowledgement, reusable manifests, and a fail-closed delegated approval path.
INTERACTIVE COMMANDS
The first guardrail run shows the candidate contract and risk in a real TTY. Typing APPROVE acknowledges and stores the manifest. Exact later runs can reuse it; changed bound fields require a fresh review.
PENDING DELEGATED REQUESTS
bash — compatibility queueguardrail approve list guardrail approve <request-id>
These commands handle queued MCP approval requests. They do not approve an arbitrary command line or attach a signature to a manifest.
MCP HOST APPROVAL
Unpinned delegated recipe/template runs require MCP host form elicitation or an already approved approval_request_id. Hosts without elicitation support return host_approval_unavailable. Normal tool arguments cannot self-approve.
REPLACING AN APPROVAL
There is no generic revoke command. To change command scope, run the desired contract interactively and acknowledge the replacement manifest, or use a different explicit manifest path.
WORKFLOW DEFINITIONS
Multi-step execution contracts with explicit transitions, validators, and a bounded iteration count.
FORMAT
workflows/deploy-staging.json{ "version": 1, "kind": "workflow_definition", "name": "test-and-build", "projectRoot": ".", "entryStep": "test", "maxIterations": 2, "services": [], "steps": [ { "id": "test", "type": "task", "run": { "command": "npm", "args": ["test"], "cwd": ".", "mode": "structured", "envPolicy": { "inherit": true } }, "validator": "exit_code", "on": { "success": "build", "validation_failed": "abort" } }, { "id": "build", "type": "task", "run": { "command": "npm", "args": ["run", "build"], "cwd": ".", "mode": "structured", "envPolicy": { "inherit": true } }, "validator": "exit_code", "on": { "success": "done", "validation_failed": "abort" } } ] }
LINT BEFORE APPROVING
bashguardrail workflow lint --definition workflows/deploy-staging.json ✓ No issues found.
guardrail workflow run --definition workflows/deploy-staging.json reviews and stores a workflow-specific approved manifest before execution.EXIT CODES
Every Guardrail exit is deterministic and machine-readable.
USING EXIT CODES IN CI
.github/workflows/guardrail.yml- name: Run with Guardrail run: | guardrail run \ --non-interactive \ --approved-manifest .guardrail/approved.json \ -- npm test # Exit 12 = drift = build failure. No special handling needed.
CI / GITHUB ACTIONS
Create the approved manifest interactively, then reuse that exact contract non-interactively in CI.
THE CI PATTERN
GITHUB ACTIONS EXAMPLE
.github/workflows/guardrail.ymlname: Guardrail CI on: [push, pull_request] jobs: guardrail: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Use Node.js 20 uses: actions/setup-node@v4 with: node-version: 20 - name: Install this source checkout run: npm install && npm link - name: Lint workflow definitions run: | for f in workflows/*.json; do guardrail workflow lint --definition "$f" || exit 1 done - name: Run tests (contract-locked) run: | guardrail run \ --non-interactive \ --approved-manifest .guardrail/approved.json \ -- npm test - name: JSON output for structured logging run: | guardrail run \ --json \ --non-interactive \ --approved-manifest .guardrail/approved.json \ -- npm test
OPENCLAW RECIPES
Run the bundled, fixed OpenClaw workflows through the same contract review and drift checks as other recipes.
RUN A BUNDLED FLOW
bashguardrail run --recipe openclaw-fix-tests --dry-run guardrail run --recipe openclaw-debug-ci --dry-run
WHAT CHANGES
| Without Guardrail | With Guardrail adapter |
|---|---|
| Ad hoc command | Named recipe with an inspectable manifest |
| Implicit working scope | Declared read/write paths and environment policy |
| Silent recipe change | Contract drift and a new review |
| Unstructured history | Repo-local structured audit events |
OPENCLAW RECIPES
The bundled catalog currently includes:
INVARIANTS
The behavioral boundaries implemented by the current local-first runtime.
CURRENT GUARANTEES
I-1 — Explicit approval records
An interactive command run stores the normalized contract and its hash after the operator types APPROVE. A manifest is an acknowledgement record, not a cryptographic identity signature.
I-2 — Risk-aware confirmation
Generated or unknown provenance, system paths, production targets, destructive operations, and sensitive combinations can raise a contract to RED and require strong interactive confirmation.
I-3 — State Machine Default-Deny
A failed validation must not result in a success state. validation_failed → done is fatally rejected at lint time. Steps default to idempotent: false.
I-4 — Normalized paths
Working, readable, and writable paths are resolved into normalized absolute contract fields before hashing and comparison.
I-5 — Bounded workflow execution
Workflow definitions declare an entry step, transitions, validators, and a positive maxIterations limit.
I-6 — Deterministic status codes
Approval, denial, drift, validation, timeout, policy, protocol, and audit-chain failures have distinct machine-readable statuses.
I-7 — Delegated work is grant-owned
The MCP server uses an operator-owned grant and fail-closed describe, prepare, and approval stages. A missing capability is not silently replaced with broader shell access.
ROLLBACK
Model failure paths explicitly with workflow transitions and validators.
A workflow step maps outcomes through its on object—for example, success to the next step and validation_failed to abort or a declared cleanup step. Lint the full definition before running it.
bashguardrail workflow lint --definition workflows/release.json guardrail workflow run --definition workflows/release.json
NEGOTIATION LOOP
Worker proposals can suggest bounded workflow updates; Guardrail evaluates the resulting contract before reuse.
Set --update-source worker_proposal only for a workflow designed to accept structured proposals. Scope widening, higher risk, invalid transitions, and exhausted iteration limits remain explicit failure states.
USING RECIPES
Bundled parameterized contracts for common Git, package, infrastructure, agent, and OpenClaw workflows.
bashguardrail list --category infra guardrail run --recipe terraform-plan-only --input config_path=infra --dry-run
Bundled recipes run directly by ID. Browse all 25 recipes and check the current input names before execution.
AUTHORING RECIPES
Start with a generated skeleton, validate it, then package the exact artifact you intend to distribute.
bashguardrail create --name safe-task --category local --output safe-task.recipe.json guardrail recipe validate safe-task.recipe.json guardrail pack safe-task.recipe.json --output safe-task.packed.json
REMOTE RECIPES
Install a recipe from a local path, URL, pinned GitHub source, or an exact registry coordinate.
bashguardrail recipe install ./safe-task.recipe.json guardrail recipe install github://owner/repo/path/to/recipe.json@<commit-sha> guardrail recipe install local/safe-task@1.0.0 --registry ./registry
guardrail run --recipe <id> for a bundled recipe. Installation requires one of the explicit sources above.guardrail run
Review and execute a command, recipe, template, or shell script as a normalized contract.
usageguardrail run [options] -- <command> [args...] Options: --shell <text> Run shell-mode script text --recipe <id[@version]> Run a bundled or installed recipe --template <path> Run a template file --input <key=value> Supply a recipe/template input; repeatable --env-allow <name> Allow an environment variable; repeatable --manifest <path> Use a custom manifest path --approved-manifest <path> Reuse an approved manifest --non-interactive Never prompt; fail if approval is needed --json Emit a structured result --json-stream Emit progress events plus the result
--non-interactive with the exact approved manifest.guardrail approve
Inspect and resolve queued MCP compatibility approvals.
bashguardrail approve list guardrail approve <request-id>
This does not approve an arbitrary command line. Direct command approval happens during an interactive guardrail run.
template diff
Compare a rendered template contract with its approved hash.
bashguardrail template diff --template ./templates/npm-publish.json --input package_dir=packages/lib --input tag=beta
There is no generic guardrail diff command. Command drift is reported by guardrail run.
guardrail audit
Verify or query the repo-local hash-linked audit log.
bashguardrail audit verify --path .guardrail/audit.jsonl guardrail audit query --status drift_detected guardrail export --format csv
guardrail recipe
Validate, inspect, install, compose, publish, and version recipes.
bashguardrail list --search git guardrail recipe validate ./safe-task.recipe.json guardrail recipe inspect ./safe-task.packed.json
guardrail workflow
Lint or run a workflow definition.
bashguardrail workflow lint --definition workflows/ci.json guardrail workflow run --definition workflows/ci.json --non-interactive
AUDIT LOG
Command and workflow events are stored as structured JSONL in .guardrail/audit.jsonl by default.
Each entry carries previous, payload, and entry hashes. Use guardrail audit query for filters, guardrail audit verify for chain integrity, and the top-level guardrail export command for JSON or CSV output.
TAMPER EVIDENCE
Hash linking makes local log edits detectable when the chain is verified.
AGENT INTEGRATION
Expose only the delegated capabilities in an operator-owned MCP grant.
bashguardrail mcp serve --grant ~/.guardrail/mcp-grants/codex.json --agent codex
The server uses stdio. Agents call grant status, then describe or prepare an action; execution requiring approval fails closed if host elicitation or an approved request is unavailable.
TRUST MODEL
Guardrail trusts the local operator and the tools the operating system allows it to launch.
- It is a command-scope and approval layer, not a security sandbox.
- It does not isolate processes, restrict syscalls, or contain an untrusted binary.
- Approved manifests are local acknowledgement records, not identity signatures.
- Delegated MCP capabilities are bounded by an operator-owned grant.
- Audit-chain verification detects edits; external retention is your responsibility.