Policy decision and control plane for agent input, runtime action, and resource boundaries.
AgentGate is a security admission controller for agentic systems. Agent hosts submit structured intent at critical boundaries: model input, tool execution, and resource access. AgentGate evaluates policy and returns an executable decision with obligations. It is not an agent runtime, not an SDK helper, not an OS sandbox, and not a DLP filter.
sequenceDiagram
participant Adapter as "Adapter / Host (PEP)"
participant Core as "AgentGate Core"
participant Policy as "Policy Engine"
participant Console as "Events / Timeline / Console"
Adapter->>Core: "POST /v1/decide"
Core->>Policy: "Evaluate PolicyRequest"
Policy-->>Core: "Decision { effect, obligations, explanation }"
Core-->>Adapter: "Decision"
Adapter->>Adapter: "Execute obligations"
Adapter->>Core: "POST /v1/report"
Core->>Console: "Persist and render events"
AgentGate enforces policy at connected surfaces. Coverage gaps are visible, not hidden.
input - Intercepts model input before it reaches the model. Handles secret detection, taint classification, input rewrite, and SecretHandle creation.
runtime - Intercepts tool calls before execution. Evaluates bash, network egress, filesystem writes, process spawns, and other high-risk side effects. High-risk actions require approval by default.
resource - Intercepts resource access and SecretHandle resolution before the adapter retrieves real values. Enforces task-scoped secret access.
Supporting channels, such as approval transports, notification, audit rendering, and the web console, are not enforcement surfaces and do not make security decisions.
Policies are organized into Bundles, each containing ordered Rules. Active bundles run in priority order; within a bundle, rules run in rule priority order.
Effects
| Effect | Behavior |
|---|---|
allow |
Permit the action. |
allow_with_audit |
Permit and emit an audit event. |
deny |
Block the action. |
approval_required |
Pause the attempt pending operator approval. |
exclusion |
Explicit exclusion effect with block semantics. |
Conditions are CEL expressions against the request fact model and Core-owned session facts.
Obligations are actions executed on match: redact audit fields, rewrite input, create a SecretHandle, request approval, pause or abort a task.
AgentGate fails closed on invalid requests, missing active policy, CEL indeterminate results, and missing secret policy.
The default policy (config/default_policy.json) ships with these rules:
inputsecret-like content -> rewrite to SecretHandleruntimebash -> approval requiredruntimeopen-world actions -> approval requiredruntimehigh-risk side effects (filesystem write, network egress, process spawn, secret resolve) -> approval requiredresourceSecretHandle resolve -> allow with audit, subject to task/session scope match
sequenceDiagram
participant Input as "Input Surface"
participant Core as "AgentGate Core"
participant Model as "Model Context"
participant Resource as "Resource Surface"
Input->>Core: "POST /v1/decide (input with secret-like content)"
Core->>Core: "Detect secret and create task-scoped SecretHandle"
Core-->>Input: "allow_with_audit + rewrite_input"
Input->>Model: "Forward placeholder text, not raw secret"
Resource->>Core: "POST /v1/decide (resolve_secret_handle)"
Core->>Core: "Validate session/task scope"
alt "scope mismatch"
Core-->>Resource: "deny + abort_task"
else "scope match"
Core-->>Resource: "allow_with_audit + resolve_secret_handle"
Resource->>Core: "POST /v1/report (redacted metadata)"
end
sequenceDiagram
participant Adapter as "Runtime Adapter"
participant Core as "AgentGate Core"
participant Operator as "Operator / Approval Transport"
Adapter->>Core: "POST /v1/decide (tool_attempt)"
Core->>Core: "Match approval_required rule"
Core-->>Adapter: "approval_required + approval_request + pause_for_approval"
Adapter->>Adapter: "Pause attempt"
Operator->>Core: "POST /v1/approvals/{id}/resolve"
alt "allow_once"
Core->>Core: "Create attempt-scoped grant"
Adapter->>Core: "Retry same attempt"
Core-->>Adapter: "allow_with_audit"
else "deny"
Core-->>Adapter: "attempt remains blocked"
else "expired or store failure"
Core-->>Adapter: "fail closed"
end
flowchart TB
subgraph Host["Agent Host / Adapter (PEP)"]
InputHook["Input Hook"]
RuntimeHook["Runtime Tool Hook"]
ResourcePEP["Resource Provider"]
end
subgraph Core["AgentGate Core"]
API["HTTP API"]
Policy["Policy Engine<br/>(CEL + rules)"]
Secrets["SecretHandle Chain"]
Approvals["Approvals Engine"]
Store["SQLite Store<br/>events, bundles, approvals, adapters"]
Integrations["Integration Definitions<br/>health + matching"]
end
Host -->|POST /v1/register| API
Host -->|POST /v1/decide| API
Host -->|POST /v1/report| API
API --> Policy
Policy --> Secrets
Policy --> Approvals
API --> Store
API --> Integrations
API --> Console["Web Console"]
API --> Transport["Approval Transport<br/>(e.g. Feishu)"]
API --> Resource["Resource Adapter"]
Core - Go + chi HTTP service. SQLite persistence via modernc.org/sqlite. Bearer token authorization with three roles: adapter, operator, admin. Strict JSON decoding; unknown fields return 400.
Integration model - Three distinct layers:
| Layer | What it represents |
|---|---|
| Adapter Registration | Who is actually connected right now |
| Integration Definition | Who the operator expects to be connected |
| Policy / Admission | Which requests are accepted and what obligations apply |
| Method | Path | Role | Description |
|---|---|---|---|
POST |
/v1/register |
adapter | Register adapter and declare surfaces |
POST |
/v1/decide |
adapter | Submit a PolicyRequest, receive a Decision |
POST |
/v1/report |
adapter | Report attempt outcome |
GET |
/v1/coverage |
operator | Surface coverage summary |
GET |
/v1/events |
operator | Security event log |
GET |
/v1/approvals |
operator | Pending approvals |
POST |
/v1/approvals/{id}/resolve |
operator | Allow or deny an approval |
* |
/internal/policy/* |
admin | Bundle and rule management |
* |
/internal/integrations/* |
admin | Integration definition management |
{
"request_id": "req_01",
"request_kind": "tool_attempt",
"actor": {
"user_id": "u1",
"host_id": "openclaw"
},
"session": {
"session_id": "sess_01",
"task_id": "task_01",
"attempt_id": "att_01"
},
"action": {
"tool": "bash",
"operation": "execute",
"side_effects": ["process_spawn", "network_egress"],
"open_world": true
},
"target": {
"kind": "process",
"identifier": "/bin/sh"
},
"content": {
"summary": "...",
"data_classes": []
},
"context": {
"surface": "runtime",
"taints": ["untrusted_external"],
"raw": {}
}
}{
"decision_id": "dec_01",
"request_id": "req_01",
"effect": "approval_required",
"reason_code": "runtime_high_risk_requires_approval",
"obligations": [
{
"type": "approval_request",
"params": {
"approval_id": "appr_01",
"scope": "attempt"
}
},
{
"type": "task_control",
"params": {
"action": "pause_for_approval"
}
}
],
"explanation": {
"summary": "Runtime attempt has high-risk side effects and requires an attempt-scoped approval."
},
"decided_at": "2026-04-29T00:00:00Z"
}OpenClaw (packages/openclaw-adapter) - Host plugin adapter. Registers input and runtime surfaces. Maps OpenClaw input and tool events to PolicyRequests. Executes rewrite_input, block, and approval obligations. Accepts optional integration_id for Definition matching.
Feishu (packages/feishu-adapter) - Approval transport channel. Polls pending approvals, sends interactive Feishu cards, resolves approvals via card callback. Not an enforcement surface.
Resource (packages/resource-adapter) - Registers resource surface. Resolves SecretHandles through AgentGate policy evaluation.
An adapter is any service that:
- Calls
POST /v1/registeron startup with its surfaces andintegration_id. - Calls
POST /v1/decidebefore executing any action at a covered surface. - Executes the returned obligations exactly as specified.
- Calls
POST /v1/reportwith the outcome.
AgentGate does not call adapters. Adapters call AgentGate.
The console connects to a live AgentGate Core instance. All data is real; there is no mock layer.
Events - Filterable security event log with decision detail: matched rules, applied rules, obligations, evidence, taints, and data classes.
Timeline - Trace-oriented view of event volume and span detail across sessions and tasks.
Approvals - Pending approval queue. Allow once (attempt-scoped) or deny.
Policy - Bundle and rule management. CEL rule editor with fact model completion. Validate, save, publish, archive.
Integrations - Integration Definition management. Live adapter matching, surface coverage, and health status (connected / stale / missing / disabled). Health is computed by Core; the console renders it.
Settings - AgentGate base URL and token configuration, stored locally in the browser.
AgentGate computes health status for each Integration Definition by matching against live adapter registrations via integration_id.
flowchart TD
Start["Integration Definition"] --> Enabled{"enabled?"}
Enabled -->|false| Disabled["disabled"]
Enabled -->|true| Matched{"matched adapters?"}
Matched -->|none| Missing["missing"]
Matched -->|one or more| Connected{"any connected?"}
Connected -->|yes| Healthy["connected"]
Connected -->|no| Stale["stale"]
For multi-instance deployments, the best healthy instance wins. An adapter that registers without a matching Definition is shown in live adapter views and does not make any Definition healthy.
Requirements - Go 1.22+, Node.js 20+, Bun.
# Install everything and build the local demo stack
./scripts/install-agentgate.sh
# Run tests
go test ./...
# Type-check all TypeScript workspaces
bun run typecheck
# Build all TypeScript workspaces
bun run build
# Start Core + hosted console
go run ./cmd/agentgate
# Optional: start console in Vite dev mode
cd web && bun run devFor the local-first demo/deploy path, see:
Token roles
| Role | Scope |
|---|---|
adapter |
/v1/register, /v1/decide, /v1/report |
operator |
/v1/coverage, /v1/events, /v1/approvals |
admin |
/internal/* and all operator endpoints |
Configure tokens with environment variables:
export AGENTGATE_ADAPTER_TOKENS=adapter-local-token
export AGENTGATE_OPERATOR_TOKENS=operator-local-token
export AGENTGATE_ADMIN_TOKENS=admin-local-tokenIf these variables are omitted, AgentGate now falls back to the same local defaults so go run ./cmd/agentgate works out of the box for local evaluation.
See config/default_policy.json for the default policy bundle.