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/governance/approvals

Decide on approval:

POST /api/v1/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/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/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/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:

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

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

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 creates a credential ref scoped to a specific agent
3. Agent receives opaque ref_id (e.g., cref_abc123...)
4. Agent passes ref_id to egress proxy
5. Egress proxy resolves ref → 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 (default 1 hour)
  • Max uses: Refs can be limited to N resolutions
  • Revocation: Refs can be revoked instantly without affecting the underlying credential
  • Transparent rotation: Rotating the underlying credential does not invalidate active refs

API

Create a credential ref:

POST /api/v1/governance/credential-refs
Content-Type: application/json

{
"credential_id": "cred-uuid",
"agent_id": "agent-uuid",
"ttl_seconds": 3600,
"max_uses": 100
}

Resolve a ref (called by egress proxy, not agents):

POST /api/v1/governance/credential-refs/resolve
Content-Type: application/json

{
"ref_id": "cref_abc123...",
"agent_id": "agent-uuid"
}

Revoke a ref:

DELETE /api/v1/governance/credential-refs/{ref_id}

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):
...