Skip to main content

Governance SDK

The Governance SDK provides a policy enforcement layer that controls what AI agents can do within a workspace. Every tool call and agent-to-agent message passes through the Policy Enforcement Point (PEP), which evaluates it against the workspace's active policy before execution.

The four subsystems are:

  1. PDP/PEP — Policy Decision Point + Policy Enforcement Point (rule engine + inline interceptor)
  2. HITL Approvals — Human-in-the-loop durable suspend/resume gates with TTL
  3. Audit Trail — SHA-256 hash-chained compliance records with evidence export
  4. Credential Isolation — Ephemeral credential refs that prevent agents from seeing raw secrets

Refs #4343, #4344, #4345, #4346, #4347

Architecture​

Agent Tool Call
│
▼
┌─────────────┐ ┌─────────────┐
│ PEP │────▶│ PDP │
│ (intercept) │ │ (evaluate) │
└──────┬──────┘ └─────────────┘
│
┌───┼───┐
│ │ │
ALLOW DENY REQUIRE_APPROVAL
│ │ │
▼ ▼ ▼
exec 403 Approval Gate
(HITL)
│
▼
Audit Trail
(hash chain)

The PDP evaluates tool calls against a JSON policy using first-match-wins priority ordering. The PEP intercepts requests at the middleware level and acts on the PDP decision. Every decision is logged to the audit trail.

Policy DSL Reference​

Policies are JSON documents containing an ordered list of rules. Each rule matches against the tool call context using glob patterns and arg predicates.

Policy Structure​

{
"policy_id": "pol_abc123",
"workspace_id": "ws_xyz",
"default_effect": "allow",
"enforcement_mode": "enforce",
"rules": [
{
"priority": 0,
"effect": "deny",
"tool": "deploy",
"capability": "*",
"target": "*.production",
"arg_predicates": {},
"description": "Block production deploys"
}
]
}

Rule Fields​

FieldTypeDescription
priorityintLower number = higher precedence. First match wins.
effectstringallow, deny, or require_approval
toolstringGlob pattern matching the tool name
capabilitystringGlob pattern matching the capability (e.g., tool_execute, memory_write)
targetstringGlob pattern matching the target resource
arg_predicatesobjectConditional checks on tool arguments
descriptionstringHuman-readable explanation for audit logs

Arg Predicates​

Arg predicates let you write conditional rules based on tool arguments:

{
"effect": "deny",
"tool": "transfer",
"target": "*",
"arg_predicates": {
"amount": {"op": "gt", "value": 1000}
},
"description": "Block transfers over $1,000"
}

Supported operators:

OperatorDescriptionExample
eqEqual{"op": "eq", "value": "prod"}
neNot equal{"op": "ne", "value": "test"}
gtGreater than{"op": "gt", "value": 1000}
gteGreater than or equal{"op": "gte", "value": 100}
ltLess than{"op": "lt", "value": 50}
lteLess than or equal{"op": "lte", "value": 10}
containsString contains{"op": "contains", "value": "secret"}

Effects​

EffectHTTP StatusBehavior
allow200Tool call proceeds
deny403Tool call blocked, reason returned
require_approval202Tool call suspended, approval_id returned

Default Effect and Fail Modes​

  • default_effect: Applied when no rule matches. Defaults to allow (fail-open).
  • Fail-open (default): If the PDP encounters an internal error, the call is allowed.
  • Fail-closed: Workspaces can opt into fail-closed mode. On PDP error, the call is denied.

Example Policies​

Block production deploys except from CI:

{
"rules": [
{
"priority": 0,
"effect": "allow",
"tool": "deploy",
"target": "*.production",
"arg_predicates": {
"source": {"op": "eq", "value": "ci-pipeline"}
},
"description": "CI can deploy to production"
},
{
"priority": 1,
"effect": "deny",
"tool": "deploy",
"target": "*.production",
"description": "Block manual production deploys"
}
]
}

Require approval for destructive operations:

{
"rules": [
{
"priority": 0,
"effect": "require_approval",
"tool": "delete_*",
"target": "*",
"description": "Destructive ops need human approval"
}
]
}

Approval Gates (HITL)​

When a rule has effect: require_approval, the PEP returns HTTP 202 with an approval_id. The agent run is suspended until a human decides.

Approval Lifecycle​

1. PEP evaluates → require_approval
2. Approval record created (status: pending, TTL: 30 min)
3. Notification sent to approver_ref (e.g., team:platform-ops)
4. Human reviews context and decides:
- approved → signal: resume → agent continues
- denied → signal: abort → agent stops
5. If TTL expires → status: expired → signal: abort

API​

Create approval (automatic — triggered by PEP):

POST /api/v1/cloud/governance/approvals

Decide on approval:

POST /api/v1/cloud/governance/approvals/{approval_id}/decide
Content-Type: application/json

{
"decision": "approved",
"note": "Reviewed and approved for staging cleanup"
}

Escalate to different approver:

POST /api/v1/cloud/governance/approvals/{approval_id}/escalate
Content-Type: application/json

{
"new_approver": "user:uuid-of-senior-engineer",
"extend_ttl_minutes": 60
}

List pending approvals:

GET /api/v1/cloud/governance/approvals?status=pending&approver_ref=team:platform-ops

Approver Refs​

FormatBehavior
team:<name>Any authenticated user can decide
user:<uuid>Only that specific user can decide

Audit Trail + Evidence Export​

Every governance decision is recorded in a SHA-256 hash-chained audit trail. Each record links to the previous via prev_hash, creating a tamper-evident log suitable for SOC 2, ISO 27001, GDPR, and HIPAA compliance.

Hash Chain Structure​

Genesis (000...000)
│
▼
Record 1: SHA-256(prev_hash + content) = hash_1
│
▼
Record 2: SHA-256(hash_1 + content) = hash_2
│
▼
Record N: SHA-256(hash_{n-1} + content) = hash_n

Record Fields​

Each audit record contains:

FieldDescription
agent_idAgent that triggered the decision
identityUser or agent identity
capabilityCapability being exercised
toolTool being called
decisionallow, deny, or require_approval
input_hashSHA-256 of the tool call input
output_hashSHA-256 of the tool call output
controlsMatched compliance control IDs
record_hashHash of this record (chain link)
prev_hashHash of the previous record
latency_msPDP evaluation latency

Chain Verification​

GET /api/v1/cloud/governance/audit/verify?start_id=1&end_id=1000

Returns:

{
"valid": true,
"broken_at": null,
"records_checked": 1000
}

Evidence Export​

Export compliance evidence for a specific framework and time range. This is a two-step job flow: creating the export returns a job_id, which you then use to download the bundle.

1. Create the export job:

POST /api/v1/cloud/governance/audit/export
Content-Type: application/json

{
"framework": "SOC2",
"start_time": "2026-01-01T00:00:00Z",
"end_time": "2026-06-30T23:59:59Z"
}

Response (201 Created):

{
"job_id": "a1b2c3d4-...",
"status": "completed",
"framework": "SOC2",
"created_at": "2026-10-08T12:00:00+00:00"
}

Exports currently run synchronously, so status is already "completed" by the time this response returns — the job_id still needs to be used to fetch the bundle in the next step.

2. Download the bundle:

GET /api/v1/cloud/governance/audit/export/{job_id}

Response (200 OK):

{
"framework": "SOC2",
"start_time": "2026-01-01T00:00:00+00:00",
"end_time": "2026-06-30T23:59:59+00:00",
"total_records": 1842,
"records": [],
"exported_at": "2026-10-08T12:00:01+00:00"
}

Supported frameworks: SOC2, ISO27001, GDPR, HIPAA

Control mappings use prefix matching:

FrameworkControl ID Prefix
SOC 2CC (e.g., CC6.1, CC7.2)
ISO 27001A. (e.g., A.9.4.1)
GDPRGDPR- (e.g., GDPR-Art25)
HIPAAHIPAA- (e.g., HIPAA-164.312)

Credential Isolation​

Agents never see raw API keys. Instead, they receive opaque credential references (cref_...) that the egress proxy resolves at request time.

How It Works​

1. Admin stores credential in Credential Vault (AES-256-GCM encrypted)
2. Admin mints a credential ref scoped to a specific agent + provider
3. Agent receives opaque ref_id (e.g., cref_abc123...)
4. Agent passes ref_id to egress proxy / ClawRouter
5. ClawRouter resolves ref at execution time → decrypts credential → injects into outbound request
6. Every resolution is audit-logged

Security Properties​

  • Agent isolation: Agents only see opaque ref IDs, never raw secrets
  • TTL expiry: Refs expire after a configurable TTL in minutes (default 60)
  • Revocation: Refs can be revoked instantly without affecting the underlying vault credential
  • Transparent rotation: Rotating the underlying vault secret does not invalidate active refs

API​

Base path: /api/v1/cloud/credentials/refs

Mint a credential ref:

POST /api/v1/cloud/credentials/refs
Content-Type: application/json

{
"agent_id": "agent-uuid",
"provider": "openai",
"scopes": ["chat.completions"],
"ttl_minutes": 60,
"vault_key_id": "vault-key-uuid"
}

Response (201 Created):

{
"ref_id": "cref_abc123...",
"agent_id": "agent-uuid",
"provider": "openai",
"scopes": ["chat.completions"],
"vault_key_id": "vault-key-uuid",
"status": "active",
"ttl_at": "2026-10-08T13:00:00+00:00",
"created_at": "2026-10-08T12:00:00+00:00"
}

List active refs for an agent (metadata only — never raw secrets):

GET /api/v1/cloud/credentials/refs?agent_id=agent-uuid

Revoke a ref:

DELETE /api/v1/cloud/credentials/refs/{ref_id}

Rotate the underlying vault secret:

POST /api/v1/cloud/credentials/refs/rotate
Content-Type: application/json

{
"vault_key_id": "vault-key-uuid",
"new_value": "sk-new-secret-value..."
}

Active refs keep working after rotation — they resolve through the vault, which now holds the new value. There is no separate "resolve" endpoint callable by clients; resolution happens internally when the egress proxy / ClawRouter makes the outbound call on the agent's behalf.

Performance​

The governance layer is designed for sub-50ms overhead on every tool call:

Operationp95 TargetMeasured
PDP evaluate (50 rules)< 50ms~0.5ms
Ungoverned workspace< 5ms~0.01ms
Audit hash chain append< 20ms~0.05ms

Ungoverned workspaces (no active policy) have effectively zero overhead — the PDP returns ALLOW immediately without scanning any rules.

Middleware Integration​

The GovernancePEPMiddleware automatically intercepts:

  • POST /api/v1/mcp/tools/*/call — MCP tool calls
  • POST /api/v1/cloud/a2a/* — Agent-to-agent messages

No application code changes are needed. The middleware reads X-Workspace-ID and X-Agent-ID headers to determine the policy context.

# Middleware is added to the FastAPI app automatically
app.add_middleware(GovernancePEPMiddleware)

For endpoint-level enforcement, use the decorator:

from app.services.governance_pep_service import enforce_governance

@router.post("/tools/{tool_id}/call")
@enforce_governance(tool="mcp_tool_call", capability="tool_execute")
async def call_tool(request: Request, tool_id: str):
...

Internal Dogfooding​

AINative uses the governance layer on its own agent swarm. The swarm-agent-protection policy enforces rules on the 16-agent local swarm:

RuleEffectWhat it protects
crontab_disablerequire_approvalPrevents agents from disabling other agents' cron entries without human approval
agent_retirerequire_approvalPrevents agents from deprecating scripts without approval
schema_droprequire_approvalPrevents dropping production tables without approval
credential_exposedenyBlocks hardcoding credentials in source code
ir_email_replyrequire_approvalRequires pass detection before auto-replying to investors
* (default)allowEverything else proceeds normally

Using the governance check in agents​

from governance_check import check_crontab_disable

decision = check_crontab_disable(
agent_id="worktree-agent",
target_agent="startup_camp",
reason="event ended",
)

if not decision.allowed:
log.warning(f"BLOCKED: {decision.reason}")
return # do not proceed

# proceed with the action

Available convenience wrappers:

from governance_check import (
check_crontab_disable, # Before disabling cron entries
check_agent_retire, # Before deprecating agent scripts
check_credential_write, # Before writing credentials to files
check_ir_auto_reply, # Before auto-replying to investors
check_schema_drop, # Before dropping production tables
check_governance, # Generic check for any action
)

All checks fail open — if the PDP is unreachable, actions are allowed with a logged warning. This preserves the enable-first design principle.