Docs navigation

Policy engine

Every event is evaluated against your project's policies before the action is approved. Four outcomes: allow, warn, block, require_approval. All matching enabled policies are collected and the most severe decision wins — a catch-all allow can never shadow a block.

Default policies

Seven built-in policies are seeded per project (toggle them under Policies):

PolicyDecision
Block invalid signaturesblock
Block unknown toolsblock
Block agents with trust score below 30block
Warn on unapproved MCP serverswarn
Require approval for sensitive data accessrequire_approval
Warn if trust score below 50warn
Allow normal approved signed activityallow

Custom policies

# Conditions are AND-ed; strings accept exact values, arrays, or "*" (any).
curl -X POST https://www.proofledger.dev/api/policies \
  -H "authorization: Bearer $PROOFLEDGER_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "name": "Block prod DB access from experimental agents",
    "decision": "block",
    "severity": "critical",
    "priority": 8,
    "conditions": {
      "environment": "development",
      "apiEndpoint": "*",
      "toolName": ["db_query", "db_write"]
    }
  }'

Condition fields: agentId · owner · framework · model · toolName · mcpServer · apiEndpoint · eventType · eventCategory · environment · trustScoreMax · trustScoreMin · sensitiveAction · toolKnown · toolApproved · signatureValid.

Pre-checking an action

// Dry-run: no event logged, no trust change. Use before executing.
const evaluation = await ProofLedger.evaluatePolicy({
  toolName: "wire_transfer",
  sensitiveAction: true,
});
// { decision: "require_approval",
//   matched: [{ name: "Require approval for sensitive data access", … }],
//   reason: "Require approval for sensitive data access → require_approval" }

Monitor vs. enforce

In monitor mode (default), decisions are logged and returned but never interrupt the agent. In enforce mode:

ProofLedger.enable({ …, policyMode: "enforce" });

// block           → PolicyBlockedError is thrown (the attempt IS logged first)
// require_approval → the SDK BLOCKS, polling until a human decides in the
//                    dashboard's Approvals inbox. Approved → continues.
//                    Denied or timed out (default 5 min) → ApprovalDeniedError.
//                    Fail-closed: an undecided action is not an approved action.

await ProofLedger.logDecision({
  action: "Access buyer financial records",
  sensitiveAction: true,          // waits here for human sign-off
});

Note

Approve/deny decisions are themselves written to the agent's audit trail as approval_decided events — a replay shows exactly who unblocked the action and when. Tune the wait with approvalTimeoutMs / approvalIntervalMs.