Policy & Approvals
Overview
Section titled “Overview”LatchGate uses OPA (Open Policy Agent) for deterministic, machine-enforced policy evaluation. The default policy is at policies/opa/latchgate.rego with ACL data in policies/data.json (generated by latchgate init, managed via latchgate policy).
Policy is default-deny: every action is denied unless all rules pass.
Evaluation order
Section titled “Evaluation order”The default Rego policy evaluates rules from most restrictive first:
- Trust gate — reject untrusted action digests
- ACL check — principal must be authorized for this action
- Base scope — lease must carry
tools:call - Required scopes — lease must carry all action-specific scopes
- Budget (calls) — session must have remaining call budget
- Sink check — requested targets must be within ACL allowlist
- Database gate — blocked DB operations are denied (planned; dormant in v0.1)
- Risk gate — high/critical risk actions require human approval
- Database approval — DB writes on sensitive tables require approval (planned, dormant in v0.1)
- Allow — all checks pass, return approved sinks and budget
The database rules are present in the Rego policy and exercised by make test-opa for forward compatibility, but never fire in v0.1 because no action manifest sets a database_context input field — the database provider ships in the planned releases.
Policy input
Section titled “Policy input”The kernel constructs a PolicyInput struct and sends it to OPA. It includes: principal, action_id, action_trust_verdict, risk_level, scopes, required_scopes, requested_sinks, budgets_before, and database operation details when applicable.
Policy output
Section titled “Policy output”OPA returns one of three decisions:
allow: true— action proceeds to grant issuance and dispatchallow: false— action denied withdeny_reasonpending_approval: true— action held for human approval
When allowed, the policy also returns approved_sinks and budget decrements.
Budgets
Section titled “Budgets”Budgets are per-session counters tracked atomically in Redis via Lua scripts.
- Call budget — maximum number of action executions per session
Budgets are atomically debited before dispatch via a Redis Lua script. If an execution fails, the debit is rolled back. This ensures budgets are never over-consumed even under concurrent requests.
Budget values are set at lease issuance time via max_calls.
Human-in-the-loop approvals
Section titled “Human-in-the-loop approvals”When policy returns pending_approval, the kernel creates an immutable pending approval record and returns HTTP 202 to the caller. The pending approval stores the exact approved execution plan — it is never re-derived from the current manifest or registry state.
Approval lifecycle
Section titled “Approval lifecycle”- Agent submits action => policy returns
pending_approval=> kernel returns 202 withapproval_id. - Webhook fires —
approval.pendingnotification arrives in Slack/Teams/PagerDuty within seconds (if webhooks are configured). - Operator reviews the pending approval (via CLI
latchgate approvals list/show, the TUI, or the API). - Operator approves or denies.
- On approval, the kernel executes the action through the same hardened path (budget, grant, dispatch, verify, receipt). Webhooks fire:
approval.granted+action.executed. - On denial,
approval.deniedwebhook fires. On expiry,approval.expiredfires (background scanner, 30-second interval).
Approval notifications
Section titled “Approval notifications”Without notifications, operators must poll for pending approvals. Configure outbound webhooks to push approval.pending events to Slack, Teams, PagerDuty, or any HTTPS endpoint. For terminal-first operators, latchgate tui provides a real-time dashboard with inline approve/deny.
Reviewing approvals
Section titled “Reviewing approvals”The operator TUI is the primary way to handle approvals. Launch it and switch to the Approvals screen (2):
latchgate tui↑/↓(ork/j) — move through pending approvals; the detail pane shows the full approved plan.a— approve the selected (pending) approval.d— deny the selected (pending) approval.
The screen updates in real time, so approval.pending events surface without polling.
CLI equivalent
latchgate approvals list # show pending approvalslatchgate approvals show <approval_id> # show detailslatchgate approvals approve <approval_id> # approvelatchgate approvals deny <approval_id> # denyApproval API
Section titled “Approval API”GET /v1/approvals # list pendingGET /v1/approvals/{id} # show detailsPOST /v1/approvals/{id}/approve # approvePOST /v1/approvals/{id}/deny # denyAll approval endpoints require operator authentication (API key + DPoP proof).
Approval integrity
Section titled “Approval integrity”The approval_hash covers: request_hash, action_digest, policy_version, approved targets, secrets, egress, budgets, and expiry. Approval consumption is atomic and one-shot — concurrent approvals or retries cannot dispatch the same side effect twice.
The approving operator’s identity (approved_by) is recorded in the grant and bound into the grant signature.
Revocation
Section titled “Revocation”- Lease revocation — invalidate a specific lease.
- Approval invalidation — expire or deny a pending approval.
- Global kill-switch —
latchgate revokeadvances the revocation epoch. All leases and grants from prior epochs are immediately invalid. See CLI Reference for details.
Managing ACLs
Section titled “Managing ACLs”ACLs map principals to the actions they may invoke, stored in policies/data.json. Sinks are auto-derived from manifest declared_side_effects — a security invariant that is never set by hand, in either the TUI or the CLI.
Via the operator TUI (recommended)
Section titled “Via the operator TUI (recommended)”Switch to the Setup screen, Principals sub-tab (6 → 3):
↑/↓(ork/j) — select a principal.g— grant actions to the selected principal. Action IDs are validated against manifests as you type, so typos are caught immediately.x— revoke actions from the selected principal. Sinks are recomputed from the remaining actions.n— add a new principal entry.
CLI equivalent
The latchgate policy command manages policies/data.json without manual JSON editing.
Grant actions:
latchgate policy grant agent-ops http_fetch,github_read,http_post ✓ agent-ops: 3 action(s) granted
Actions http_fetch, github_read, http_post Sinks http_read, http_write (auto-derived)Action IDs are validated against manifests before writing. Unknown actions are rejected with a suggestion for typos.
Revoke actions (sinks are recomputed from the remaining actions; if all are revoked, the principal entry is removed):
latchgate policy revoke agent-ops http_postView the ACL:
latchgate policy show # all principalslatchgate policy show agent-ops # single principal with risk breakdownlatchgate policy list # compact tableDev preset wildcard
Section titled “Dev preset wildcard”latchgate init --preset permissive creates a wildcard ACL ("*") granting all actions to any caller. This makes init => up work out of the box for evaluation. For production, use a restrictive preset — the ACL starts empty and you use policy grant to add specific principals.
Customizing policy
Section titled “Customizing policy”For ACL changes (granting/revoking actions), use latchgate policy grant and latchgate policy revoke — see Managing ACLs via CLI above.
For custom Rego rules beyond ACL management, edit policies/opa/latchgate.rego directly, then reload OPA. Run policy tests with:
make test-opaThe kernel sends the same PolicyInput regardless of policy complexity — you can add custom rules without modifying LatchGate itself.
For action manifest format and built-in actions, see Actions & Manifests. For writing custom actions with policy hooks, see Custom Actions.