{
  "markdown": "# Runestone Agent Gatekeeper\n\n[![CI](https://github.com/Runestone-Labs/gatekeeper/actions/workflows/ci.yml/badge.svg)](https://github.com/Runestone-Labs/gatekeeper/actions/workflows/ci.yml)\n[![npm](https://img.shields.io/npm/v/@runestone-labs/gatekeeper-client)](https://www.npmjs.com/package/@runestone-labs/gatekeeper-client)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)\n\nA policy-based gatekeeper service that sits between AI agents and real-world tools (shell, HTTP, filesystem), enforcing approvals, denials, USD budgets, and audit logging.\n\n## Who Is This For\n\nIf you're running AI agents (Claude, GPT, local LLMs, MCP servers) that execute tool calls against real systems — and you're responsible for what happens when those calls go wrong — Gatekeeper is for you. Specifically:\n\n- **You build agentic apps** and your agent can run shell commands, write files, or hit external APIs.\n- **You've already had one \"why did it do that?\" moment** — or you're staring down the possibility.\n- **You want a self-hostable, auditable, single-binary boundary** between your agent and the world, without adopting an enterprise SSO stack or wiring everything through a cloud proxy.\n- **You need USD budgets and signed approvals** as first-class primitives, not features bolted onto an observability tool.\n\nYou were probably using: nothing (and trusting the prompt), ad-hoc Python wrappers around `subprocess`, a cloud LLM gateway, or shell-level `sudoers` rules that don't understand tool semantics.\n\n## What Problem This Solves\n\nAI agents need to execute actions in the real world: running shell commands, writing files, making HTTP requests. Without guardrails, an agent can accidentally (or adversarially) execute dangerous operations.\n\nThe Gatekeeper intercepts all tool requests and:\n- **Allows** low-risk operations immediately\n- **Denies** operations that match dangerous patterns\n- **Requires human approval** for sensitive operations\n- **Catches sensitive-boundary crossings** — Keychain, SSH keys, cloud credentials, browser profiles, package-registry tokens, env files (see [Sensitive Boundary Protection](#sensitive-boundary-protection))\n- **Rejects when a USD / token / call budget is exceeded** (optional) — per **actor** (a rolling guardrail) or per **run** (`scope: run`, keyed on `runId`: caps a single agentic run at the action boundary, where recursive burn compounds)\n- **Optionally proxies Anthropic model calls** — route inference through the gatekeeper so every `/v1/messages` call is audited, the API key stays centralized, and real per-token **cost** is metered onto the audit row and into budgets (off by default; see [docs/API.md](docs/API.md#all-anthropic))\n\nAll decisions are logged to an append-only audit trail (JSONL or Postgres). JSONL receipts include a sequence, previous-entry hash, and entry hash for independent tamper/gap verification. An aggregation endpoint (`/usage`) exposes call counts — and real cost/token sums — by actor × tool × day. A budget endpoint (`/budget`) surfaces current spend vs cap per configured rule.\n\nGatekeeper only controls actions that are actually routed through it. It does not infer coverage from installation. Verify the client's actual routing configuration before describing a runtime as protected.\n\n## Setup CLI\n\nThe `gatekeeper` CLI previews every client configuration change, creates timestamped backups before applying one, and can restore the last patch:\n\n```bash\nnpm install --global @runestone-labs/gatekeeper\ngatekeeper init --client claude-code\ngatekeeper doctor\ngatekeeper verify ./data/audit\n```\n\nSupported setup targets are `claude-code`, `openclaw`, and `mcp`. Fresh Claude Code patches set `GATEKEEPER_FAIL_CLOSED=1`. Existing fail-open hooks remain usable, but `doctor` reports them as a protection failure with the exact migration command.\n\nThe Cloud connection protocol is implemented for dogfooding, but the hosted team product is not\ngenerally available. The experimental connection flow uses browser device authorization and a scoped\ninstance token stored locally with mode `0600`:\n\n```bash\ngatekeeper connect\ngatekeeper disconnect\n```\n\nDo not expect the public Cloud endpoint to be available during demand validation. Disconnect revokes\nCloud access without turning off local OSS enforcement. See [RUNESTONE_CLOUD.md](RUNESTONE_CLOUD.md).\n\nGatekeeper only controls actions that are actually routed through it. Native or bypass routes remain outside its boundary, so verify each client's real routing configuration before describing a runtime as protected.\n\n## Need a teammate to handle approvals while agents run unattended?\n\nThe local Gatekeeper server and MCP integration are free. We're testing demand for **Gatekeeper\nTeam at $49/month**, for up to five members and five instances: route an agent's held action to an\naccountable teammate for approval or denial, with a record of what happened. This is a proposed\nCloud service, not a public hosted product; **no payment is collected** during this test.\n\n[See the proposed workflow and share your team's needs](https://gatekeeper.runestonelabs.io/cloud-beta?utm_source=github_readme&utm_medium=docs&utm_campaign=gatekeeper_self_serve_2026_09).\nYou can also optionally report whether you've routed a first real action through local Gatekeeper\nor used it again on a later day. These are manual form choices, not verified external activation.\nDon't send commands or raw audit logs; this test adds no automatic usage reporting to the MCP server.\n\n## Sensitive Boundary Protection\n\nCoding agents fail in subtle ways. They start with a safe task, then escalate\ninto sensitive local operations while trying to be helpful. Gatekeeper ships\na built-in rule pack that catches these crossings.\n\n**Real failure pattern:** A coding agent debugging a Puppeteer / Chromium\n\"Safe Storage\" prompt on macOS first applied a normal flag-based fix\n(`--use-mock-keychain`, `--password-store=basic`, throwaway `--user-data-dir`).\nWhen the prompt persisted, the agent pivoted to inspecting the user's\nKeychain — and proposed deleting entries \"so there's nothing to access.\"\n\n```bash\n# Step 2 of the agent's reasoning — Gatekeeper requires approval (high risk)\nsecurity find-generic-password -s \"Chromium Safe Storage\"\n\n# Step 3 of the agent's reasoning — Gatekeeper denies outright (critical)\nsecurity delete-generic-password -s \"Chromium Safe Storage\"\n```\n\nGatekeeper classifies these against a built-in rule pack:\n\n| Resource class            | Examples                                                    |\n| ------------------------- | ----------------------------------------------------------- |\n| `credential_store`        | macOS Keychain (find / dump / delete)                       |\n| `private_key`             | `~/.ssh/id_*`, `*.pem`, `~/.ssh` directory enumeration      |\n| `cloud_credentials`       | `~/.aws/credentials`, `~/.config/gcloud/`, `~/.azure/`      |\n| `env_secret`              | `cat .env`, `.env.*` reads via shell                        |\n| `package_registry_token`  | `~/.npmrc`, `~/.pypirc`                                     |\n| `developer_auth`          | `~/.git-credentials`, `gh auth token`                       |\n| `browser_profile`         | Chrome / Chromium / Brave / Arc / Firefox profile dirs      |\n| `unknown_sensitive`       | Broad recursive home-dir secret greps                       |\n\nEach rule has an `effect` (`allow | require_approval | deny`), a stable\n`category`, a `risk` level, and an optional `safer_alternative` redirect.\nRules are mirrored into `riskFlags` (`boundary:keychain-read`,\n`category:credential_store_access`, `risk:high`) so existing audit consumers\nwork unchanged.\n\n**Defaults always load** — no opt-in required. Override or extend them under\n`sensitive_boundaries:` in your `policy.yaml`; the full reference dump lives\nat [`policies/sensitive-boundaries.yaml`](policies/sensitive-boundaries.yaml).\nA demo fixture for the Puppeteer → Keychain escalation is at\n[`examples/sensitive-boundaries/keychain-scope-creep.json`](examples/sensitive-boundaries/keychain-scope-creep.json).\n\n### Use it from Claude Code\n\n[`@runestone-labs/gatekeeper-claude-code`](integrations/claude-code/) is a\nClaude Code PreToolUse hook that routes `Bash` / `Write` / `Edit` / `WebFetch`\nthrough Gatekeeper before Claude Code executes them. Install once globally,\ndrop the [`settings.example.json`](integrations/claude-code/settings.example.json)\nsnippet into `~/.claude/settings.json`, and every gated tool call gets\nevaluated against the boundary pack:\n\n```bash\nnpm install -g @runestone-labs/gatekeeper-claude-code\n```\n\n```json\n{\n  \"hooks\": {\n    \"PreToolUse\": [\n      {\n        \"matcher\": \"Bash|Write|Edit|WebFetch\",\n        \"hooks\": [{ \"type\": \"command\", \"command\": \"gatekeeper-claude-code-hook\" }]\n      }\n    ]\n  }\n}\n```\n\nThe legacy hook remains fail-open by default if the Gatekeeper server is down. The setup\nCLI sets `GATEKEEPER_FAIL_CLOSED=1` for new managed installations and reports existing configurations\nthat do not. Approval-required Bash, Write, and WebFetch calls are registered\nas exact idempotent holds and executed locally once after approval. Edit is\npolicy-checked but is never reduced to a destructive whole-file write. See\n[`integrations/claude-code/README.md`](integrations/claude-code/README.md)\nfor the full configuration reference.\n\n### Use it from any MCP client\n\n[`@runestone-labs/gatekeeper-mcp`](integrations/mcp-server/) is a stdio MCP\nserver that lets any MCP client (Claude Desktop, Claude Code, Cursor, …) run\nreal tool calls — `shell_exec`, `files_write`, `http_request`, a generic\n`gatekeeper_call`, plus `gatekeeper_health` — but only by routing every call\nthrough Gatekeeper. Identity, role, and origin are pinned server-side (the\nmodel can't escalate via tool arguments), and it fails closed: a deny, a\npending approval, or any malformed Gatekeeper response surfaces as an error,\nnever as a successful result.\n\nAdd it to your MCP client config:\n\n```json\n{\n  \"mcpServers\": {\n    \"gatekeeper\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"@runestone-labs/gatekeeper-mcp\"],\n      \"env\": {\n        \"GATEKEEPER_URL\": \"http://127.0.0.1:3847\",\n        \"GATEKEEPER_ROLE\": \"agent\"\n      }\n    }\n  }\n}\n```\n\nIt's also listed on the MCP Registry (preview) as\n`io.github.runestone-labs/gatekeeper`. See\n[`integrations/mcp-server/README.md`](integrations/mcp-server/README.md) for\nthe full tool table and security model.\n\n## Threat Model\n\nThis gatekeeper protects against:\n\n1. **Accidental damage**: Agent runs `rm -rf /` or overwrites critical files\n2. **Prompt injection execution**: Malicious content tricks agent into dangerous actions\n3. **Exfiltration**: Agent sends secrets to external services\n4. **SSRF attacks**: Agent accesses internal services via HTTP\n\nThis gatekeeper does NOT protect against:\n\n- Malicious operator with access to the policy file\n- Attacks on the gatekeeper service itself\n- Social engineering of the human approver\n- Denial of service (no rate limiting)\n\n## Quick Start with Docker\n\nThe fastest way to try Gatekeeper:\n\n```bash\ngit clone https://github.com/Runestone-Labs/gatekeeper.git\ncd gatekeeper\nnpm run bootstrap    # generates .env with a fresh GATEKEEPER_SECRET\ndocker compose up\n```\n\n`npm run bootstrap` creates `.env` from `.env.example` and writes a random 48-character `GATEKEEPER_SECRET` into it. Skip if you already have a `.env` you want to keep — docker-compose will error clearly if the secret isn't set.\n\nGatekeeper is now running at http://127.0.0.1:3847 with demo mode enabled.\n\nTest it with the quickstart script (walks through DENY, ALLOW, and APPROVE):\n```bash\nbash examples/quickstart.sh\n```\n\nOr test individual decisions:\n```bash\n# requestId must be a UUID\n# This will be DENIED (dangerous pattern)\ncurl -s -X POST http://127.0.0.1:3847/tool/shell.exec \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"requestId\":\"'\"$(uuidgen)\"'\",\"actor\":{\"type\":\"agent\",\"name\":\"test\",\"role\":\"openclaw\"},\"args\":{\"command\":\"rm -rf /\"}}'\n\n# This will be ALLOWED\ncurl -s -X POST http://127.0.0.1:3847/tool/http.request \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"requestId\":\"'\"$(uuidgen)\"'\",\"actor\":{\"type\":\"agent\",\"name\":\"test\",\"role\":\"openclaw\"},\"args\":{\"url\":\"https://httpbin.org/get\",\"method\":\"GET\"}}'\n```\n\nTo customize policy:\n```bash\ncp policy.example.yaml policy.yaml\n# Edit policy.yaml, then update docker-compose.yaml volume to use ./policy.yaml\ndocker-compose restart\n```\n\nFor manual installation without Docker, see below.\n\n## Quick Start (Manual)\n\n### 1. Install Dependencies\n\n```bash\nnpm install\n```\n\n### 2. Configure Environment\n\n```bash\n# Required: Secret for HMAC signing (at least 32 characters)\nexport GATEKEEPER_SECRET=\"your-secret-key-at-least-32-chars-long\"\n\n# Provider selection (optional)\nexport APPROVAL_PROVIDER=local   # local | slack | runestone (default: local)\nexport AUDIT_SINK=jsonl          # jsonl | postgres (Cloud is an additive secondary)\nexport POLICY_SOURCE=yaml        # yaml | runestone (Cloud retains last-known-good locally)\n\n# Optional: Slack webhook for approval notifications (when using slack provider)\nexport SLACK_WEBHOOK_URL=\"https://hooks.slack.com/services/...\"\n\n# Optional: Custom port (default: 3847)\nexport GATEKEEPER_PORT=3847\n\n# Optional: Bind host (default: 127.0.0.1)\n# Use 0.0.0.0 when running in Docker.\nexport GATEKEEPER_HOST=127.0.0.1\n\n# Optional: Base URL for approval links\nexport BASE_URL=\"http://127.0.0.1:3847\"\n\n# Optional: Route Anthropic model calls through the gatekeeper (default: off)\n# When enabled, point your SDK's ANTHROPIC_BASE_URL at <gatekeeper>/anthropic.\n# See docs/API.md for the full /anthropic proxy reference.\nexport ENABLE_ANTHROPIC_PROXY=false\n\n# Optional: Anthropic API key. When set, the /anthropic proxy injects it on\n# proxied calls so the key can live only in gatekeeper (not in the agent).\nexport ANTHROPIC_API_KEY=\"sk-ant-...\"\n```\n\n### 3. Create Policy File\n\n```bash\ncp policy.example.yaml policy.yaml\n# Edit policy.yaml to match your requirements\n```\n\n### 4. Start the Server\n\n```bash\nnpm start\n# Or for development with auto-reload:\nnpm run dev\n```\n\n## Demo (2 minutes)\n\nSee all three decision types in action:\n\n```bash\n# Install dependencies\nnpm install\n\n# Set a demo secret (or use your own)\nexport GATEKEEPER_SECRET=\"demo-secret-at-least-32-characters-long\"\n\n# Run the demo\nnpm run demo\n```\n\nThe demo runs through:\n1. **DENY** - Dangerous command (`rm -rf /`) is blocked\n2. **APPROVE** - Safe command (`ls -la`) requires approval, then auto-approved\n3. **ALLOW** - HTTP request executes immediately\n\n### Recording\n\n```bash\n# Record with asciinema (creates demo.cast)\nnpm run demo:record\n\n# Playback\nasciinema play demo.cast\n\n# Create GIF/MP4 with VHS (requires: brew install vhs)\nnpm run demo:gif\n```\n\n### Outputs\n\n- `demo.cast` - Terminal recording (asciinema format)\n- `demo.gif` - Animated GIF for sharing\n- `demo.mp4` - Video file\n- `data/audit/YYYY-MM-DD.jsonl` - Audit log with all demo actions\n\n## Provider Architecture\n\nThe gatekeeper uses a pluggable provider system for flexibility:\n\n### Approval Providers\n- **local** (default): Logs approval URLs to console\n- **slack**: Sends interactive approval requests via Slack webhook\n- **runestone**: Enterprise control plane (coming soon)\n\n### Audit Sinks\n- **jsonl** (default): Writes to daily JSONL files in `data/audit/`\n- **runestone**: Stream to cloud for search and compliance (coming soon)\n\n### Policy Sources\n- **yaml** (default): Load from local YAML file\n- **runestone**: Managed policies with version control (coming soon)\n\n## Example Requests\n\nAll tool requests must include `actor.role` to enforce principal policies. For safe retries, include an `idempotencyKey`.\n\n### Execute a Tool (Allow Decision)\n\n```bash\ncurl -X POST http://127.0.0.1:3847/tool/http.request \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"requestId\": \"550e8400-e29b-41d4-a716-446655440000\",\n    \"actor\": {\n      \"type\": \"agent\",\n      \"name\": \"my-agent\",\n      \"role\": \"openclaw\",\n      \"runId\": \"run-123\"\n    },\n    \"args\": {\n      \"url\": \"https://api.example.com/data\",\n      \"method\": \"GET\"\n    }\n  }'\n```\n\nResponse (200):\n```json\n{\n  \"decision\": \"allow\",\n  \"requestId\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"reasonCode\": \"POLICY_ALLOW\",\n  \"humanExplanation\": \"Policy allows \\\"http.request\\\".\",\n  \"policyVersion\": \"sha256:abc123...\",\n  \"success\": true,\n  \"result\": {\n    \"status\": 200,\n    \"headers\": {\"content-type\": \"application/json\"},\n    \"body\": \"{...}\"\n  },\n  \"executionReceipt\": {\n    \"startedAt\": \"2024-01-15T10:30:00.000Z\",\n    \"completedAt\": \"2024-01-15T10:30:00.120Z\",\n    \"durationMs\": 120\n  }\n}\n```\n\n### Execute a Tool (Approve Decision)\n\n```bash\ncurl -X POST http://127.0.0.1:3847/tool/shell.exec \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"requestId\": \"550e8400-e29b-41d4-a716-446655440001\",\n    \"actor\": {\n      \"type\": \"agent\",\n      \"name\": \"my-agent\",\n      \"role\": \"openclaw\"\n    },\n    \"args\": {\n      \"command\": \"ls -la /tmp\"\n    }\n  }'\n```\n\nResponse (202):\n```json\n{\n  \"decision\": \"approve\",\n  \"requestId\": \"550e8400-e29b-41d4-a716-446655440001\",\n  \"approvalId\": \"abc123...\",\n  \"expiresAt\": \"2026-01-31T13:00:00.000Z\",\n  \"reasonCode\": \"POLICY_APPROVAL_REQUIRED\",\n  \"humanExplanation\": \"Policy requires human approval before running \\\"shell.exec\\\".\",\n  \"message\": \"Approval required. Check local for approval links.\",\n  \"approvalRequest\": {\n    \"approvalId\": \"abc123...\",\n    \"expiresAt\": \"2026-01-31T13:00:00.000Z\",\n    \"reasonCode\": \"POLICY_APPROVAL_REQUIRED\",\n    \"humanExplanation\": \"Policy requires human approval before running \\\"shell.exec\\\".\"\n  },\n  \"policyVersion\": \"sha256:abc123...\"\n}\n```\n\n### Execute a Tool (Deny Decision)\n\n```bash\ncurl -X POST http://127.0.0.1:3847/tool/shell.exec \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"requestId\": \"550e8400-e29b-41d4-a716-446655440002\",\n    \"actor\": {\n      \"type\": \"agent\",\n      \"name\": \"my-agent\",\n      \"role\": \"openclaw\"\n    },\n    \"args\": {\n      \"command\": \"rm -rf /\"\n    }\n  }'\n```\n\nResponse (403):\n```json\n{\n  \"decision\": \"deny\",\n  \"requestId\": \"550e8400-e29b-41d4-a716-446655440002\",\n  \"reasonCode\": \"TOOL_DENY_PATTERN\",\n  \"humanExplanation\": \"Request matches a deny pattern configured for this tool.\",\n  \"policyVersion\": \"sha256:abc123...\"\n}\n```\n\n### Capability Tokens (Pre-Approved)\n\nIf a tool is configured with `decision: approve`, you can pre-authorize a specific call\nwith a capability token scoped to tool + args hash:\n\n```bash\nnpm run capability:create -- --tool shell.exec --args /tmp/args.json --ttl 3600\n```\n\nInclude the `capabilityToken` in the tool request. Gatekeeper will allow the call\nwithout manual approval if the token is valid.\n\n### Health Check\n\n```bash\ncurl http://127.0.0.1:3847/health\n```\n\nResponse:\n```json\n{\n  \"version\": \"0.3.2\",\n  \"policyHash\": \"sha256:abc123...\",\n  \"uptime\": 3600,\n  \"pendingApprovals\": 2,\n  \"demoMode\": false,\n  \"providers\": {\n    \"approval\": \"local\",\n    \"policy\": \"yaml\"\n  },\n  \"database\": {\n    \"available\": true,\n    \"healthy\": true,\n    \"latencyMs\": 2\n  },\n  \"memory\": {\n    \"enabled\": true\n  }\n}\n```\n\n## Policy Configuration\n\nSee `policy.example.yaml` for a complete example.\n\n```yaml\ntools:\n  shell.exec:\n    decision: approve           # allow | approve | deny\n    deny_patterns:\n      - \"rm -rf\"               # Regex patterns to block\n    allowed_cwd_prefixes:\n      - \"/tmp/\"                # Allowed working directories\n    allowed_commands:\n      - \"ls\"\n      - \"git\"\n    sandbox_command_prefix:\n      - \"firejail\"\n      - \"--noprofile\"\n      - \"--\"\n    max_output_bytes: 1048576\n    max_timeout_ms: 30000\n\n  files.write:\n    decision: approve\n    allowed_paths:\n      - \"/tmp/\"\n    deny_extensions:\n      - \".env\"\n    max_size_bytes: 10485760\n\n  http.request:\n    decision: allow\n    allowed_methods: [\"GET\", \"POST\"]\n    allowed_domains:\n      - \"api.example.com\"\n    deny_domains:\n      - \"pastebin.com\"\n    deny_ip_ranges:            # SSRF protection\n      - \"127.0.0.0/8\"\n      - \"169.254.0.0/16\"\n    max_body_bytes: 1048576\n    max_redirects: 3\n```\n\nFor a complete policy writing tutorial, see [docs/POLICY_GUIDE.md](docs/POLICY_GUIDE.md).\n\n## Approval Flow\n\n1. Agent submits tool request\n2. Gatekeeper evaluates against policy\n3. If `approve`: Creates pending approval, sends notification via configured provider\n4. Human clicks Approve or Deny link\n5. If Approved: Tool executes, result returned\n6. All actions logged to audit trail\n\nApproval links are:\n- HMAC-signed (tamper-proof)\n- Single-use (prevents replay)\n- Time-limited (1 hour expiry)\n\nFor a detailed approval workflow guide, see [docs/APPROVALS.md](docs/APPROVALS.md).\n\n## Audit Logs\n\nAll requests are logged via the configured audit sink. Default (jsonl) writes to `data/audit/YYYY-MM-DD.jsonl`:\n\n```json\n{\n  \"timestamp\": \"2026-01-31T12:00:00.000Z\",\n  \"requestId\": \"550e8400-...\",\n  \"tool\": \"shell.exec\",\n  \"decision\": \"approve\",\n  \"actor\": {\"type\": \"agent\", \"name\": \"my-agent\", \"role\": \"openclaw\"},\n  \"argsSummary\": \"{\\\"command\\\":\\\"ls -la\\\"}\",\n  \"riskFlags\": [],\n  \"policyHash\": \"sha256:abc123...\",\n  \"gatekeeperVersion\": \"0.3.2\"\n}\n```\n\nLogs are:\n- Append-only (never modified)\n- One file per day (easy rotation)\n- Include policy hash (for forensics)\n- Secrets are redacted\n\nFor a complete audit log reference with querying examples, see [docs/AUDIT_LOGS.md](docs/AUDIT_LOGS.md).\n\n## Client Library\n\nInstall the TypeScript client for integrating your agent with Gatekeeper:\n\n```bash\nnpm install @runestone-labs/gatekeeper-client\n```\n\n```typescript\nimport { GatekeeperClient } from '@runestone-labs/gatekeeper-client';\n\nconst client = new GatekeeperClient({\n  baseUrl: 'http://127.0.0.1:3847',\n  role: 'openclaw',\n});\n\n// Execute a shell command through the gatekeeper\nconst result = await client.shellExec({ command: 'ls -la' });\nconsole.log(result.decision); // 'allow' | 'approve' | 'deny'\n```\n\nSee the full [client README](integrations/typescript-client/README.md) for all available methods.\n\n## Using with Real Agents\n\nGatekeeper is designed to be agent-agnostic. Any agent that can route tool calls over HTTP can integrate with Gatekeeper. See [INTEGRATING_AGENTS.md](INTEGRATING_AGENTS.md) for the integration pattern.\n\n## Planned Team Cloud\n\nOSS Local remains free and authoritative. Runestone is validating an optional\nhosted layer for shared approvals, policy versions, redacted evidence, drift\nalerts, and weekly reports. The proposed Team plan is $49/month for five\ninstances and five members.\n\nCloud development remains frozen until at least five qualified external teams\nand three explicit $49/month commitments cross the public demand gate. [Join\ndemand validation](https://gatekeeper.runestonelabs.io/cloud-beta?utm_source=github&utm_medium=readme&utm_campaign=cloud_beta_validation).\n\nCloud never receives raw prompts, results, file contents, HTTP bodies or\nheaders, environment values, or arbitrary custom arguments. See\n[RUNESTONE_CLOUD.md](RUNESTONE_CLOUD.md) for the exact versioned schemas and\nfailure behavior.\n\n## Security Decisions\n\n| Feature | Implementation | Rationale |\n|---------|----------------|-----------|\n| Approval signing | HMAC-SHA256 of full payload | Prevents parameter tampering |\n| Single-use approvals | Status field + atomic update | Prevents replay attacks |\n| Expiry | 1 hour default | Limits approval window |\n| Input validation | Zod with `.strict()` | Rejects unknown fields |\n| Shell constraints | cwd allowlist, timeout caps | Limits blast radius |\n| SSRF protection | DNS resolution + IP checks | Blocks internal access |\n| Audit logging | Append-only via pluggable sink | Tamper-evident trail |\n\n## Development\n\n```bash\n# Type check\nnpm run typecheck\n\n# Run tests\nnpm run test:run\n\n# Run with auto-reload\nnpm run dev\n\n# Run production\nnpm start\n```\n\n## Memory Module (Optional)\n\nThe memory module provides graph-based knowledge storage (entities, episodes, evidence) via PostgreSQL + Apache AGE. It is an **optional module** — Gatekeeper works as a standalone policy engine without it.\n\n**Without a database**: Only core tools (`shell.exec`, `files.write`, `http.request`) are registered. Policy enforcement, approvals, and JSONL audit logging work normally.\n\n**With a database**: Memory tools are additionally registered, providing a knowledge graph for AI assistants.\n\n### Configuration\n\n```bash\n# Set DATABASE_URL to enable the memory module\nexport DATABASE_URL=\"postgresql://user:pass@localhost:5432/memory\"\n\n# Or explicitly control (overrides DATABASE_URL detection)\nexport ENABLE_MEMORY=true   # or false to disable even with a DATABASE_URL\n```\n\n### Database Setup\n\n```bash\n# Generate migration SQL from schema changes\nnpm run db:generate\n\n# Apply migrations to a running database\nnpm run db:migrate\n\n# Or push schema directly (dev only)\nnpm run db:push\n```\n\n### Memory Tools\n\n| Tool | Description |\n|------|-------------|\n| `memory.upsert` | Create/update entities (people, projects, concepts) |\n| `memory.link` | Create relationships between entities |\n| `memory.unlink` | Remove relationships between entities |\n| `memory.query` | Query entities (with full-text search) and traverse relationships |\n| `memory.episode` | Log decisions, events, and observations |\n| `memory.evidence` | Attach evidence/provenance to entities or episodes |\n\n```bash\n# Create an entity\ncurl -X POST http://127.0.0.1:3847/tool/memory.upsert \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"requestId\":\"...\",\"actor\":{\"type\":\"agent\",\"name\":\"test\",\"role\":\"openclaw\"},\"args\":{\"type\":\"person\",\"name\":\"Alice\"}}'\n\n# Link two entities\ncurl -X POST http://127.0.0.1:3847/tool/memory.link \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"requestId\":\"...\",\"actor\":{\"type\":\"agent\",\"name\":\"test\",\"role\":\"openclaw\"},\"args\":{\"sourceId\":\"<id1>\",\"targetId\":\"<id2>\",\"relation\":\"knows\"}}'\n```\n\n### Schema Architecture\n\nThe database schema is split into two modules:\n- **`src/db/schema/audit.ts`** — Audit logs table (core gatekeeper, always available)\n- **`src/db/schema/memory.ts`** — Knowledge graph tables: entities, episodes, evidence (optional module)\n\nThe KG schema is a generic entity/episode/evidence model. Application-specific ontology (entity types, facet types, edge relations) is defined by the consuming application, not by gatekeeper.\n\nSee [docs/MEMORY.md](docs/MEMORY.md) for setup and full API reference.\n\n## Documentation\n\n### Guides\n- [docs/POLICY_GUIDE.md](docs/POLICY_GUIDE.md) - How to write and customize policies (including budgets)\n- [docs/APPROVALS.md](docs/APPROVALS.md) - Approval workflow details and troubleshooting\n- [docs/AUDIT_LOGS.md](docs/AUDIT_LOGS.md) - Audit log format and querying\n- [docs/MEMORY.md](docs/MEMORY.md) - Graph-based memory system setup and API reference\n- [docs/KG_PATTERNS.md](docs/KG_PATTERNS.md) - Practical patterns for using the memory module (entities, episodes, evidence, provenance conventions)\n- [docs/DEPLOY.md](docs/DEPLOY.md) - Production deployment + non-obvious operational behaviors\n\n### Reference\n- [THREAT_MODEL.md](THREAT_MODEL.md) - Security assumptions and non-goals\n- [INTEGRATING_AGENTS.md](INTEGRATING_AGENTS.md) - Using Gatekeeper with real agents\n- [RUNESTONE_CLOUD.md](RUNESTONE_CLOUD.md) - OSS vs Cloud architecture\n\n### Contributing\n- [CONTRIBUTING.md](CONTRIBUTING.md) - How to contribute\n- [SECURITY.md](SECURITY.md) - Security policy and vulnerability reporting\n- [GOVERNANCE.md](GOVERNANCE.md) - Project governance\n- [CHANGELOG.md](CHANGELOG.md) - Release history\n\n## License\n\nApache-2.0 - See [LICENSE](LICENSE) for details.\n",
  "bytes": 27502,
  "sha": "2c57705edeb5a70f8e35d80ac0ddce700fa6c2818691f6fdd3191b1a1e6bf459",
  "repo_slug": "runestone-labs/gatekeeper",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_runestone_labs_gatekeeper_f1b1efee/readme"
}