Skip to content

lhy0718/Guardplane

Guardplane

Guardplane wordmark

Security kernel + sandboxed runner for AI agents
Run any agent safely.

CI Release License Python Status Validated

Start Here · 5-Minute Quickstart · Use Cases · 1-Minute Demo · Architecture · Validation · Operations · Security

Guardplane is a security runtime for AI agents.

If you already use Codex, Claude Code, Ollama-based local models, or CI bots, Guardplane lets you keep your existing agent and add three things:

  • deny-by-default policy
  • approval for risky actions
  • replayable audit logs

It sits between the agent and the operating system, adding workspace sandboxing, MCP tool routing, and audited execution without requiring you to adopt a new agent framework.

Think: Docker for AI coding agents, but focused on policy enforcement, approvals, and auditability rather than packaging alone.

Validated with real-agent runs for Codex, Claude, and Llama/Ollama, and designed to support other agents through the same Guardplane action contract.

Fastest Path

If you want to try Guardplane immediately:

pipx install guardplane
git clone https://github.com/lhy0718/Guardplane.git
cd Guardplane/docs/tutorials/pipx-quickstart
cat README.md

That lab gives you a reproducible pipx-installed quickstart with:

  • one local daemon
  • one policy file
  • one guarded job
  • one approval
  • one replay

Why Guardplane

  • Keep your existing agent or wrapper instead of migrating to a new orchestration stack
  • Start with one local agent and one policy file, then add approvals, MCP control, and audit as needed
  • Move from demos to production with the same action contract, replay model, and policy surface

Who It Is For

  • Platform teams that need guardrails around coding agents and internal automation
  • Security teams that need approvals, replay, and auditability for agent actions
  • Developers who want to wrap an existing agent CLI without rewriting it
  • CI and tooling owners who want the same policy model for human and automated runs

Start Here

Pick the path that matches your use case:

Common Use Cases

  • Local guarded job execution: run a deterministic job that writes a report, calls an MCP browser tool, and spawns a local process with policy checks around each step.
  • Existing coding agent with guardrails: keep Codex, Claude Code, Ollama-hosted local models, or another planner as the planner, convert its action plan into Guardplane JSONL, and enforce filesystem, network, and tool policy on execution.
  • CI with the same policy model: run automation under a narrower policy so safe report generation is allowed by default while risky package installs, network access, or repo writes need approval.

Install

Option A: pipx from PyPI

pipx install guardplane

If you want the latest unreleased source instead:

pipx install "git+https://github.com/lhy0718/Guardplane.git@main"

You can also install from a local checkout:

pipx install .

pipx install guardplane installs the CLI and daemon only. The repository's examples/, scripts/, and validation assets are not bundled into the wheel, so the demo and validation commands below assume either a source checkout of this repository or your own policy/job/MCP assets.

Option B: editable dev install

python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .[dev]

5-Minute Quickstart

Bootstrap token -> managed token. If you want the shortest path, use docs/tutorials/pipx-quickstart/. The manual flow below uses ./examples/policy.yaml, ./examples/job.yaml, and examples/mock_mcp_server.py, so run it from a repository checkout.

Fastest reproducible path:

pipx install guardplane
git clone https://github.com/lhy0718/Guardplane.git
cd Guardplane/docs/tutorials/pipx-quickstart
cat README.md

Manual flow:

  1. Set auth token
export GUARDPLANE_AUTH_TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(24))')"
  1. Start daemon
guardplane daemon start
  1. Initialize state
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" init
  1. Mint a managed admin token and switch over
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" auth create --role admin --label ops-admin
# save the returned `token` value, then:
export GUARDPLANE_AUTH_TOKEN="<managed-token>"
  1. Create workspace and agent
mkdir -p workspace/reports workspace/data
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" agent create researcher --workspace ./workspace --policy ./examples/policy.yaml
  1. Register mock MCP server
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" mcp add browser --cmd "python3.11 examples/mock_mcp_server.py"
  1. Run deterministic job
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" run researcher --job ./examples/job.yaml

1-Minute Guardrail Demo

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" run researcher --job ./examples/job.yaml
# status=WAITING_APPROVAL request_id=<id>

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" approve <request_id> --reason "reviewed"
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" ps
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" replay <run_id>

Agent Sandbox Demo

bridge runs the wrapper command on the host and only guards the emitted JSONL actions. Use it for interoperability with existing agent CLIs.

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" run researcher --sandbox-cmd "python3.11 examples/sandbox_demo_agent.py" --sandbox-cmd-mode bridge

contained runs the wrapper command itself inside Docker with a read-only workspace mount and --network none.

Build the sandbox image once:

docker build -f Dockerfile.sandbox -t guardplane-sandbox:local .

Then run a contained wrapper that emits one guarded write action:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" run researcher --sandbox-cmd 'python3 -c "import json; print(json.dumps({\"syscall\":\"fs.write\",\"args\":{\"path\":\"reports/contained-demo.txt\",\"content\":\"contained demo\"}}))"' --sandbox-cmd-mode contained

Use contained when the wrapper command and its inputs are available inside the sandbox image or workspace.

What Guardplane Is

  • A runtime kernel for agent syscalls
  • A sandboxed runner for agent wrappers and deterministic jobs
  • A control plane for approvals, replay, audit, policy, MCP, and worker routing

What Guardplane Is Not

  • Not an LLM orchestration framework
  • Not a replacement for your existing coding agent
  • Not a hard kernel-isolation claim
  • Not a generic MCP proxy without policy and audit semantics

Feature Matrix

Surface What it covers Status
Policy engine Capability allow/deny, approval-required rules, policy simulation, rollout governance Available
Workspace sandbox Path traversal block, absolute path block, symlink escape block Available
Approval gate Pause/resume runs, approval queue, operator UI, approval SLA metrics Available
Audit and replay JSONL event log, event integrity chain, replay, archive, evidence packs Available
MCP control plane MCP registry, tool routing, persistent sessions, tool metadata Available
Wrapper execution bridge, contained, local, Docker, and bwrap backends Available
SDKs and adapters Python SDK, TypeScript SDK, Codex/Claude/CI examples Available
Scale-out Remote workers, worker pools, tenancy, quota, admission control Available
Operator plane Trace UI, executive dashboard, saved queries, scheduled reports, alerts Available

What Guardplane Enforces

  • Capability policy (default: deny)
  • Workspace filesystem sandbox (traversal/symlink/absolute-path escape blocked)
  • Approval gate for risky actions
  • JSONL event audit log + retention/compression policy
  • Replay (guardplane replay <run_id>)
  • Agent Sandbox mode (--sandbox-cmd) with bridge and contained wrapper modes
  • Python/TypeScript adapter SDKs with versioned action contract
  • Managed auth tokens with RBAC (admin, approver, auditor, run_submitter)
  • Enterprise identity federation (OIDC JWT), directory sync, delegated admin, and break-glass tokens
  • Daemon auth (Bearer token) + UDS transport option + optional TLS/mTLS
  • Health/Readiness/Metrics endpoints
  • Run cancel + idempotent approval resume
  • Local/Docker sandbox backend switch
  • Tamper-evident audit log (sequence, prev_hash, event_hash) + integrity verify
  • External audit sinks (object-style mirror + SIEM HTTP sink)
  • Persistent MCP sessions, tool metadata, and webhook notifications
  • Multi-tenant org/business-unit/project/workspace control plane
  • Remote workers and per-run executor selection
  • Worker-pool scheduling by class/labels with queue-aware dispatch
  • Run provenance and project quota/budget controls
  • API admission control and rate limiting
  • Trace UI (/ui, /ui/approvals, /ui/executive, /ui/runs/<run_id>)

Operator CLI

guardplane [--daemon-url URL] [--daemon-socket /tmp/guardplaned.sock] [--auth-token TOKEN] init
guardplane auth whoami
guardplane auth list [--active-only]
guardplane auth create --role <role> --label <label> [--principal-type service_account|human] [--owner-subject <subject>] [--expires-at <iso8601>]
guardplane auth rotate <token_id> [--label <label>] [--owner-subject <subject>] [--expires-at <iso8601>]
guardplane auth revoke <token_id>
guardplane auth break-glass --label <label> --reason "..." [--owner-subject <subject>] [--expires-at <iso8601>]
guardplane auth directory-sync --file ./directory.yaml
guardplane auth directory-users
guardplane auth directory-groups
guardplane agent create <agent_id> --workspace <path> --policy <file> [--org ORG] [--project PROJECT] [--workspace-id WS] [--worker NAME]
guardplane run <agent_id> --job <job.yaml> [--worker NAME] [--worker-class CLASS] [--worker-label key=value] [--adapter-name NAME] [--agent-version VER] [--image-version VER]
guardplane run <agent_id> --sandbox-cmd "<command string>" [--sandbox-cmd-mode bridge|contained] [--worker NAME] [--worker-class CLASS] [--worker-label key=value]
guardplane cancel <run_id>
guardplane ps
guardplane logs <run_id>
guardplane replay <run_id>
guardplane approvals list [--status PENDING] [--agent-id <id>] [--run-id <run_id>]
guardplane approvals show <request_id>
guardplane approve <request_id> [--by <actor>] [--reason "..."]
guardplane audit events [--run-id <run_id>] [--agent-id <id>] [--event-type EVENT] [--capability CAP] [--status STATUS]
guardplane audit verify <run_id>
guardplane audit export-run <run_id> [--output bundle.json] [--object-dir out/] [--siem-url https://siem.example/ingest]
guardplane audit archive-run <run_id> [--archive-dir ./audit-archives] [--tier hot|warm|cold]
guardplane audit verify-archive ./audit-archives/hot/2026-03-07/<run_id>
guardplane audit restore-archive ./audit-archives/hot/2026-03-07/<run_id> --output-dir ./restored-audit
guardplane audit apply-retention [--archive-dir ./audit-archives] [--hot-days 7] [--warm-days 30] [--cold-days 180]
guardplane audit evidence-pack [--case-id INC-1] [--run-id <id>] [--org-id <id>] [--project-id <id>] [--redact-external] [--create-hold] [--output evidence.json]
guardplane audit verify-evidence --input evidence.json
guardplane audit deliver-evidence --input evidence.json --connector splunk|opensearch|datadog|ticket --url https://...
guardplane audit hold create --case-id INC-1 --run-id <id> [--reason "..."] [--expires-at <iso8601>]
guardplane audit hold list [--case-id INC-1] [--status ACTIVE]
guardplane audit hold release <hold_id> [--reason "..."]
guardplane report dashboard [--scope-type global|org|business_unit|project] [--scope-id <id>] [--horizon-hours 24] [--top-n 5]
guardplane report query create <name> [--scope-type project] [--scope-id <id>] [--event-type POLICY_DECISION] [--capability net.fetch] [--text denied]
guardplane report query list [--scope-type project] [--scope-id <id>]
guardplane report query show <query_id>
guardplane report query run <query_id> [--limit 100]
guardplane report schedule create <name> [--scope-type project] [--scope-id <id>] [--saved-query-id <query_id>] [--interval-minutes 60] [--webhook <url>]
guardplane report schedule list [--scope-type project] [--scope-id <id>]
guardplane report schedule run <report_id>
guardplane report alert create <name> --rule-type pending_approval_overdue|worker_down|denied_capability_spike|failed_runs_spike [--scope-type project] [--scope-id <id>] [--threshold 1] [--webhook <url>]
guardplane report alert list [--scope-type project] [--scope-id <id>]
guardplane report alert evaluate <alert_id>
guardplane policy validate <policy.yaml>
guardplane policy test <policy.yaml> --cases <policy_cases.yaml>
guardplane policy dry-run --policy <policy.yaml> --capability fs.write --resource workspace/reports/out.txt
guardplane policy effective --agent <agent_id>
guardplane policy explain --agent <agent_id> --capability fs.write --resource workspace/reports/out.txt
guardplane policy diff <old-policy.yaml> <new-policy.yaml>
guardplane policy revision create --scope-type project --scope-id <project_id> --file <policy.yaml> [--summary "..."] [--based-on <revision_id>]
guardplane policy revision list [--scope-type project] [--scope-id <id>] [--limit 100]
guardplane policy revision show <revision_id>
guardplane policy rollout create --revision <revision_id> --mode enforce|dry_run [--target-org <id>] [--target-project <id>] [--target-agent <id>]
guardplane policy rollout list [--scope-type project] [--scope-id <id>] [--status ACTIVE]
guardplane policy rollout show <rollout_id>
guardplane policy rollout review <rollout_id> --decision approve|reject [--reason "..."]
guardplane policy rollout activate <rollout_id>
guardplane policy rollout rollback <rollout_id> [--reason "..."]
guardplane policy simulate-run --run <run_id> (--revision <revision_id> | --rollout <rollout_id>)
guardplane policy simulate-bundle --bundle <bundle.json> (--revision <revision_id> | --rollout <rollout_id>)
guardplane tenant org create <org_id> [--policy baseline.yaml] [--region kr-seoul] [--key-ref kms://org/root]
guardplane tenant business-unit create <business_unit_id> --org <org_id> [--region kr-seoul] [--key-ref kms://org/bu]
guardplane tenant project create <project_id> --org <org_id> [--business-unit <id>] [--policy overlay.yaml] [--region kr-seoul] [--failover-region jp-tokyo] [--key-ref kms://org/project] [--max-active-runs 10] [--max-runs-per-day 100] [--max-syscalls-per-run 50] [--max-write-bytes-per-run 1048576] [--max-net-fetches-per-run 5] [--max-proc-spawns-per-run 10] [--max-proc-spawn-seconds-per-run 120] [--approval-sla-seconds 900]
guardplane tenant workspace add <workspace_id> --project <project_id> --root <path>
guardplane tenant membership grant <subject> --subject-type user|group|service_account --scope-type global|org|business_unit|project --scope-id <id> --role <role>
guardplane tenant key-rotate --scope-type org|business_unit|project --scope-id <id> --key-ref kms://ring/new-key
guardplane worker add <name> --endpoint <url> --auth-token-env <ENV> [--backend docker] [--worker-class default] [--region kr-seoul] [--tenant-scope-type org|business_unit|project] [--tenant-scope-id <id>] [--max-concurrency 1] [--tls-ca-file ca.pem] [--tls-client-cert-file client.crt] [--tls-client-key-file client.key] [--tls-pin-sha256 <sha256>] [--max-retries 1] [--backoff-seconds 0.25] [--request-timeout-seconds 20]
guardplane worker list
guardplane worker pools
guardplane worker show <name>
guardplane worker probe <name>
guardplane worker drain <name> [--reason "..."] [--until <iso8601>]
guardplane worker resume <name>
guardplane worker pin-refresh <name>
guardplane mcp add <name> --cmd "<command>" [--session-mode persistent|oneshot] [--auth-env KEY] [--tool-metadata file.yaml]
guardplane mcp list
guardplane tools
guardplane storage inspect [--db state/state.db|postgresql://...]
guardplane storage schema-version [--db state/state.db|postgresql://...]
guardplane storage upgrade [--db state/state.db|postgresql://...] [--dry-run]
guardplane storage backup --output export.json [--db state/state.db|postgresql://...]
guardplane storage restore --input export.json [--db state/state.db|postgresql://...]
guardplane storage export --output export.json [--db state/state.db|postgresql://...]
guardplane storage import --input export.json [--db state/state.db|postgresql://...]
guardplane storage migrate --source state/state.db --target postgresql://guardplane:${DB_PASSWORD}@db/guardplane [--backup-output backup.json] [--dry-run]
guardplane doctor
guardplane daemon start [--host 127.0.0.1 --port 8787 | --socket /tmp/guardplaned.sock] [--tls-certfile cert.pem --tls-keyfile key.pem]
guardplane daemon leases [--lease-type run|approval|schema|maintenance] [--active-only]
guardplane daemon maintenance-run
guardplane daemon admission-stats [--horizon-seconds 3600] [--top-n 10]

Runtime API

  • GET /healthz (no auth)
  • GET /readyz (no auth)
  • GET /metrics (auth required)
  • GET /v1/coordination/leases
  • GET /v1/admission/stats
  • POST /v1/maintenance/run
  • GET /v1/auth/whoami
  • GET /v1/auth/tokens
  • POST /v1/auth/tokens
  • POST /v1/auth/tokens/{token_id}/rotate
  • POST /v1/auth/tokens/{token_id}/revoke
  • POST /v1/auth/break-glass
  • POST /v1/auth/directory/sync
  • GET /v1/auth/directory/users
  • GET /v1/auth/directory/groups
  • POST /v1/init
  • POST /v1/orgs
  • GET /v1/orgs
  • POST /v1/projects
  • GET /v1/projects
  • POST /v1/workspaces
  • GET /v1/workspaces
  • POST /v1/memberships
  • GET /v1/memberships
  • POST /v1/agents
  • POST /v1/runs
  • POST /v1/runs/{run_id}/cancel
  • GET /v1/runs
  • GET /v1/runs/{run_id}
  • GET /v1/runs/{run_id}/events
  • GET /v1/runs/{run_id}/integrity
  • GET /v1/runs/{run_id}/bundle
  • POST /v1/syscall
  • GET /v1/approvals
  • GET /v1/approvals/{request_id}
  • POST /v1/approvals/{request_id}/grant
  • GET /v1/audit/events
  • GET /v1/executive/dashboard
  • GET /v1/executive/saved-queries
  • POST /v1/executive/saved-queries
  • GET /v1/executive/saved-queries/{query_id}
  • POST /v1/executive/saved-queries/{query_id}/execute
  • GET /v1/executive/scheduled-reports
  • POST /v1/executive/scheduled-reports
  • POST /v1/executive/scheduled-reports/{report_id}/run
  • GET /v1/executive/alert-policies
  • POST /v1/executive/alert-policies
  • POST /v1/executive/alert-policies/{alert_id}/evaluate
  • POST /v1/compliance/evidence
  • POST /v1/compliance/evidence/verify
  • GET /v1/compliance/holds
  • POST /v1/compliance/holds
  • POST /v1/compliance/holds/{hold_id}/release
  • POST /v1/policy/evaluate
  • POST /v1/policy/explain/agents/{agent_id}
  • GET /v1/policy/effective/agents/{agent_id}
  • POST /v1/policy/revisions
  • GET /v1/policy/revisions
  • GET /v1/policy/revisions/{revision_id}
  • POST /v1/policy/rollouts
  • GET /v1/policy/rollouts
  • GET /v1/policy/rollouts/{rollout_id}
  • POST /v1/policy/rollouts/{rollout_id}/review
  • POST /v1/policy/rollouts/{rollout_id}/activate
  • POST /v1/policy/rollouts/{rollout_id}/rollback
  • POST /v1/policy/simulate
  • POST /v1/mcp/servers
  • GET /v1/mcp/servers
  • GET /v1/mcp/tools
  • POST /v1/workers
  • GET /v1/workers
  • GET /v1/workers/pools
  • POST /v1/worker/execute
  • GET /ui
  • GET /ui/approvals
  • GET /ui/runs/{run_id}

Operational Store

SQLite remains the default. Batch 10 starts the migration path toward a multi-user operational store.

Use PostgreSQL by setting GUARDPLANE_DB_URL:

export DB_PASSWORD="<set-me>"
export GUARDPLANE_DB_URL=postgresql://guardplane:${DB_PASSWORD}@db.internal:5432/guardplane
pip install 'guardplane[postgres]'
guardplaned

Local migration tooling:

guardplane storage schema-version
guardplane storage upgrade --dry-run
guardplane storage inspect
guardplane storage backup --output state-backup.json
guardplane storage migrate \
  --source ./state/state.db \
  --target postgresql://guardplane:${DB_PASSWORD}@db.internal:5432/guardplane \
  --backup-output ./state-backup.json \
  --dry-run
guardplane storage migrate \
  --source ./state/state.db \
  --target postgresql://guardplane:${DB_PASSWORD}@db.internal:5432/guardplane \
  --backup-output ./state-backup.json
guardplane storage inspect --db postgresql://guardplane:${DB_PASSWORD}@db.internal:5432/guardplane

Live PostgreSQL integration test is opt-in:

pip install 'guardplane[postgres]'
GUARDPLANE_RUN_POSTGRES_INTEGRATION=1 python3.11 -m pytest tests/test_storage_postgres_integration.py

MCP Control Plane

Register a persistent MCP server with metadata:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" mcp add browser \
  --cmd "python3.11 examples/mock_mcp_server.py" \
  --session-mode persistent \
  --tool-metadata examples/mcp_browser_metadata.yaml

Inspect server health:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" mcp list
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" tools

If the MCP server needs secrets, pass only variable names:

export BROWSER_API_TOKEN="..."
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" mcp add browser \
  --cmd "python3.11 examples/mock_mcp_server.py" \
  --auth-env BROWSER_API_TOKEN

Reference: docs/MCP_OPERATIONS.md

Policy Tooling

Validate a policy before rollout:

guardplane policy validate examples/policy.yaml

Run fixture-based policy tests:

guardplane policy test examples/policy.yaml --cases examples/policy_cases.yaml

Preview one decision without starting a run:

guardplane policy dry-run \
  --policy examples/policy.yaml \
  --capability proc.spawn \
  --resource python3.11 \
  --extra-json '{"command":"python3.11"}'

Review a policy change before deployment:

guardplane policy diff old-policy.yaml new-policy.yaml

Inspect the merged runtime policy that actually applies to one agent:

guardplane policy effective --agent researcher

Explain one runtime decision with rollout context and matched rule:

guardplane policy explain \
  --agent researcher \
  --capability fs.write \
  --resource workspace/reports/out.txt

Propose and stage a rollout instead of replacing policy files in place:

guardplane policy revision create \
  --scope-type project \
  --scope-id proj-a \
  --file examples/policy.yaml \
  --summary "tighten report writes"

guardplane policy rollout create \
  --revision <revision_id> \
  --mode dry_run \
  --target-agent researcher

guardplane policy rollout review <rollout_id> --decision approve
guardplane policy rollout activate <rollout_id>

Simulate a candidate revision against a historical run before activation:

guardplane policy simulate-run --run <run_id> --revision <revision_id>
guardplane policy simulate-bundle --bundle bundle.json --rollout <rollout_id>

Generate a signed compliance evidence pack and optionally place the covered runs on legal hold:

guardplane audit evidence-pack \
  --case-id INC-1042 \
  --project-id proj-a \
  --time-from 2026-03-01T00:00:00Z \
  --time-to 2026-03-07T23:59:59Z \
  --redact-external \
  --create-hold \
  --hold-reason "pending regulator review" \
  --output evidence.json

guardplane audit verify-evidence --input evidence.json
guardplane audit deliver-evidence --input evidence.json --connector splunk --url https://splunk.example/hec

Auth And Audit Operations

Inspect your current principal:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" auth whoami

Create a scoped approval token:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" auth create \
  --role approver \
  --label security-approver

Verify a run's sealed event chain:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" audit verify <run_id>

Mirror one run bundle to object-style storage and ship NDJSON to a SIEM endpoint:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" audit export-run <run_id> \
  --object-dir ./audit-archive \
  --siem-url https://siem.example/guardplane

Create a signed archive bundle and verify it before long-term storage:

export GUARDPLANE_AUDIT_SIGNING_KEY="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" audit archive-run <run_id> \
  --archive-dir ./audit-archives \
  --tier hot
guardplane audit verify-archive ./audit-archives/hot/2026-03-07/<run_id>
guardplane audit restore-archive ./audit-archives/hot/2026-03-07/<run_id> \
  --output-dir ./restored-audit

Adapter SDKs

Guardplane ships a versioned adapter contract, a Python SDK, and a package-ready TypeScript SDK.

Local package install:

# from the Guardplane repository root
npm install ./sdk/typescript

First-party examples:

Starter policy packs:

Runtime Architecture

flowchart LR
    A["Agent or Wrapper"] --> B["guardplane CLI"]
    B --> C["guardplaned"]
    C --> D["Auth Middleware"]
    D --> D1["RBAC + Token Store"]
    D1 --> E["Policy Engine"]
    E --> F["Approval Gate"]
    F --> G["Sandbox Executor"]
    G --> G1["Local Backend"]
    G --> G2["Docker Backend"]
    G --> G3["Bwrap Backend"]
    C --> H["SQLite state"]
    C --> I["JSONL events"]
    I --> I1["Hash Chain + Integrity Verify"]
    I --> I2["Audit Sinks"]
    C --> J["Metrics"]
Loading

Details: docs/ARCHITECTURE.md

Multi-Tenant Control Plane

Projects remain the primary run boundary, but topology can now be modeled as org -> business_unit -> project -> workspace.

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" tenant org create acme \
  --policy examples/policy.yaml \
  --region kr-seoul \
  --key-ref kms://acme/root
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" tenant business-unit create research --org acme \
  --region kr-seoul \
  --key-ref kms://acme/research
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" tenant project create core --org acme --business-unit research \
  --policy examples/policy.yaml \
  --region kr-seoul \
  --failover-region jp-tokyo \
  --key-ref kms://acme/core \
  --max-active-runs 10 \
  --max-runs-per-day 200 \
  --max-syscalls-per-run 50 \
  --max-net-fetches-per-run 5 \
  --max-proc-spawns-per-run 10 \
  --max-proc-spawn-seconds-per-run 120 \
  --approval-sla-seconds 900
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" tenant membership grant ci-submitter \
  --scope-type project --scope-id core --role run_submitter

Run list, approval queue, audit queries, and approval grant are filtered by project scope for non-admin callers. Org baseline policy + project overlay + agent policy are merged in that order. Use guardplane policy effective --agent <agent_id> to inspect the final document and the project quota applied to that agent. Region pinning and segmented worker fleets are covered in docs/TENANT_TOPOLOGY.md.

Remote Workers And Trace UI

Register a remote worker:

export GUARDPLANE_WORKER_TOKEN="<worker-admin-token>"
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" worker add worker-a \
  --endpoint https://worker-a.internal:8787 \
  --auth-token-env GUARDPLANE_WORKER_TOKEN \
  --backend docker \
  --worker-class default \
  --region kr-seoul \
  --tenant-scope-type project \
  --tenant-scope-id core \
  --tls-ca-file certs/ca.pem \
  --tls-client-cert-file certs/control-plane.crt \
  --tls-client-key-file certs/control-plane.key \
  --label region=lab

Pin the worker certificate after enrollment:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" worker pin-refresh worker-a

Drain a worker for maintenance:

guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" worker drain worker-a --reason "certificate rotation"
guardplane --auth-token "$GUARDPLANE_AUTH_TOKEN" worker resume worker-a

Open the trace UI:

open http://127.0.0.1:8787/ui

The UI shell is public, but it still uses a bearer token for /v1/* API calls inside the page.

UI workflows now available:

  • Pending approval queue with inline Approve And Resume
  • Dedicated queue page at /ui/approvals
  • Executive operator page at /ui/executive
  • Cross-run search scoped by global|org|business_unit|project
  • Saved queries, scheduled reports, and alert policies from the UI
  • Run detail provenance cards
  • Replay filters by event_type, capability, status, and free-text payload search

Security Model

  • Deny-by-default capability checks
  • Workspace boundary enforcement before file/proc cwd access
  • Approval required rules block execution until explicitly granted
  • Daemon API bearer token auth by default, with managed tokens and RBAC
  • Optional TLS/mTLS and stricter UDS trust mode
  • net.fetch limited to http|https URLs with explicit domain allowlists
  • Event-sourced audit trail for every syscall decision/result
  • Tamper-evident event sealing with integrity verification

Security Limits

  • Guardplane is still policy+container guardrail centered, not full kernel-hard isolation.
  • bridge mode is not process isolation; it only guards emitted actions.
  • For production, rotate bootstrap credentials into managed tokens, export sealed audit events off-host, and use Docker or bwrap backend where possible.

Metrics

Prometheus format:

  • guardplane_runs_total{status=...}
  • guardplane_syscall_latency_seconds_*
  • guardplane_approval_wait_seconds_*
  • guardplane_approval_pending_total
  • guardplane_approval_overdue_total
  • guardplane_approval_oldest_pending_seconds
  • guardplane_mcp_latency_seconds_*
  • guardplane_mcp_requests_total
  • guardplane_mcp_server_up

Docker deployment

export GUARDPLANE_AUTH_TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(24))')"
export GUARDPLANE_DOCKER_EGRESS_MODE=proxy-required
export GUARDPLANE_HTTP_PROXY=http://host.docker.internal:3128
export GUARDPLANE_WEBHOOK_URLS="https://hooks.example/guardplane"
docker build -t guardplaned:local .
docker run --rm -p 8787:8787 \
  -e GUARDPLANE_AUTH_TOKEN="$GUARDPLANE_AUTH_TOKEN" \
  -e GUARDPLANE_SANDBOX_BACKEND=docker \
  -e GUARDPLANE_DOCKER_IMAGE=guardplane-sandbox:local \
  -e GUARDPLANE_DOCKER_EGRESS_MODE="$GUARDPLANE_DOCKER_EGRESS_MODE" \
  -e GUARDPLANE_HTTP_PROXY="$GUARDPLANE_HTTP_PROXY" \
  -e GUARDPLANE_WEBHOOK_URLS="$GUARDPLANE_WEBHOOK_URLS" \
  -v "$PWD/state:/app/state" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  guardplaned:local

Build sandbox image:

docker build -f Dockerfile.sandbox -t guardplane-sandbox:local .

Contained wrapper guidance:

  • Prefer contained over bridge for real wrapper execution.
  • Put wrapper scripts and prompt/config inputs inside the sandbox image or under the workspace mount.
  • Keep wrapper commands non-interactive and deterministic; JSONL action output should go to stdout only.
  • Use GUARDPLANE_DOCKER_MEMORY, GUARDPLANE_DOCKER_CPUS, GUARDPLANE_DOCKER_PIDS_LIMIT, and GUARDPLANE_SANDBOX_CMD_TIMEOUT_SECONDS to bound wrapper execution.

Real-Agent Validation

These validation scripts are repository assets, not installed package assets. Run them from a source checkout:

git clone https://github.com/lhy0718/Guardplane.git
cd Guardplane
scripts/verify_codex_e2e.sh

Strict mode:

scripts/verify_codex_e2e.sh --require-real-agent

Claude Code path:

scripts/verify_claude_e2e.sh \
  --planner-cmd "python3.11 examples/claude_cli_wrapper.py --claude-bin /absolute/path/to/claude --prompt-file {prompt_file} --output-json {output_json}"

Strict Claude Code mode:

scripts/verify_claude_e2e.sh \
  --require-real-agent \
  --planner-cmd "python3.11 examples/claude_cli_wrapper.py --claude-bin /absolute/path/to/claude --prompt-file {prompt_file} --output-json {output_json}"

Llama / Ollama path:

scripts/verify_llama_e2e.sh \
  --model llama3.2 \
  --api-base http://127.0.0.1:11434

Strict Llama / Ollama mode:

scripts/verify_llama_e2e.sh \
  --require-real-agent \
  --model llama3.2 \
  --api-base http://127.0.0.1:11434

No-mock mode:

export MY_REAL_AGENT_TOKEN="real-secret-for-validation"
scripts/verify_codex_e2e.sh \
  --require-real-agent \
  --no-mock \
  --mcp-cmd "<real mcp server cmd>" \
  --mcp-server-name browser \
  --mcp-tool-name open \
  --mcp-tool-args-file ./examples/mcp_open_args.json \
  --secret-scope real_agent/token \
  --secret-env-name MY_REAL_AGENT_TOKEN \
  --require-real-secrets

--no-mock disables the built-in mock MCP server. In that mode, Scenario E uses the exact server/tool/args contract you pass in, and Scenario F validates secrets.read with the --secret-scope / --secret-env-name pair you provide.

If --secret-env-name is omitted, the script derives one from --secret-scope by uppercasing it and replacing non-alphanumeric characters with _.

Release gate script:

scripts/verify_release_gate.sh

Developer Commands

make install-dev
make daemon
make init
make test
make validate-codex
make validate-codex-strict
make validate-claude
make validate-claude-strict
make validate-llama
make validate-llama-strict
make verify-release-gate
make release-check
make dist
make docker-build
make docker-build-sandbox
make smoke
make smoke-sandbox
make clean-state

Documentation

Developer path:

Security path:

Platform path:

Leadership path:

Community

Packages

 
 
 

Contributors

Languages