Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
271 changes: 271 additions & 0 deletions docs/how-to/security/policy-enforcement.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
---
title: Policy Enforcement Setup
description: Configure PDP integration to add authorization decisions to your agent
---

# Policy Enforcement Setup

Add authorization decisions to badge-verified requests by connecting to a Policy Decision Point (PDP).

Comment on lines +6 to +9
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new how-to page isn’t linked from the MkDocs nav (mkdocs.yml → How-To Guides → Security), so it will be hard to discover from the site UI. Add a nav entry for how-to/security/policy-enforcement.md near the other security guides.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bbb5e91. Updated to link to RFC-005 Appendix B with markup [RFC-005 Appendix B](../../../rfcs/rfc-005-gateway-protocol.md#appendix-b-embedded-pdp).

---

## Problem

You need to:

- Control which agents can access which endpoints based on policy
- Enforce rate limits, field redaction, or audit logging per-agent
- Gradually roll out policy enforcement without breaking existing traffic
- Override policy in emergencies

---

## Solution

Configure the CapiscIO server to enforce policy decisions. Two PDP modes are available:

| Mode | Description | Use When |
|------|-------------|----------|
| **Embedded PDP** | In-process OPA evaluator; zero-latency policy queries | Single-node deployments, fast iteration, no external dependencies |
| **External PDP** | Remote HTTP PDP (any OPA/Cedar/custom endpoint) | Multi-node fleets, existing PDP infrastructure, custom engines |
Comment on lines +25 to +30
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Markdown tables in this page start with || ..., which renders as an extra empty first column in MkDocs/Markdown. Use a single leading/trailing | for each row (consistent with other docs pages).

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bbb5e91. Changed these to ### sub-headings under their respective parent ## sections.


---

## Option A: Embedded PDP (Recommended)

The embedded PDP runs an OPA evaluator inside the server process. It automatically builds a policy bundle from your registry data (agents, MCP servers) and a built-in starter policy.

### Prerequisites

- A running CapiscIO server with badge verification enabled
- `CAPISCIO_WORKSPACE` set to your workspace/org UUID

### Step 1: Enable the Embedded PDP

```bash
export CAPISCIO_EMBEDDED_PDP=true
export CAPISCIO_WORKSPACE=YOUR_WORKSPACE_UUID
export CAPISCIO_ENFORCEMENT_MODE=EM-OBSERVE
```

The server starts an in-process OPA evaluator with:

- A **starter Rego policy** that allows requests from registered agents with active status
- A **policy bundle** auto-built from registry data (agents, MCP servers, workspace policies)
- **Hybrid rebuild**: timer-based polling (default 30s) plus event-driven rebuild on registry changes

### Step 2: Review Decisions

Check server logs for policy events. Each request emits:

| Field | Description |
|-------|-------------|
| `capiscio.policy.decision` | `ALLOW`, `DENY`, or `ALLOW_OBSERVE` |
| `capiscio.policy.decision_id` | Unique evaluation ID |
| `staleness.bundle_stale` | `true` if the policy bundle is stale |
| `staleness.bundle_age_ms` | Age of the bundle in milliseconds (when stale) |

Verify that legitimate requests receive `ALLOW` and unauthorized access patterns receive `DENY` before tightening the enforcement mode.

### Step 3: Tighten Enforcement

Once decisions look correct, move through the enforcement modes:

```bash
# Allow but log — PDP decisions are informational
export CAPISCIO_ENFORCEMENT_MODE=EM-OBSERVE

# Deny unauthorized — PDP DENY blocks requests (fail-closed on PDP failure)
export CAPISCIO_ENFORCEMENT_MODE=EM-GUARD

# Full enforcement — unknown obligation types also cause denial
export CAPISCIO_ENFORCEMENT_MODE=EM-STRICT
```

### Bundle Staleness

When the embedded PDP's policy bundle hasn't been rebuilt within the staleness threshold:

| Mode | Behavior |
|------|-----------|
| `EM-OBSERVE` | Request proceeds; `staleness.bundle_stale` annotation in telemetry |
| `EM-GUARD` | Request proceeds; `staleness.bundle_stale` annotation in telemetry |
| `EM-STRICT` | Request denied with `BUNDLE_STALE` error code |
Comment on lines +87 to +93
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spelling: the table header uses “Behaviour”, but the docs elsewhere use American English “Behavior”. Consider changing to “Behavior” for consistency.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed — the table renders correctly in MkDocs Material. The alignment markers (---, :---:) are valid standard Markdown table syntax and render as expected. No change needed.


Configure staleness via:

```bash
export CAPISCIO_BUNDLE_STALENESS_THRESHOLD=5m # default: 5m
export CAPISCIO_BUNDLE_POLL_INTERVAL=30s # default: 30s
```

### Bundle Endpoint

When the embedded PDP is active, a bundle endpoint is available for external OPA consumers:

```
GET /v1/bundles/{workspace_id}
```

This requires `X-Capiscio-Registry-Key` authentication and the API key must belong to the matching workspace/org.

---

## Option B: External PDP

Connect to any PDP that accepts HTTP POST requests with PIP-format decision requests ([RFC-005 §5.1](https://github.com/capiscio/capiscio-rfcs)).

### Prerequisites

- A running CapiscIO server with badge verification enabled
- A PDP that accepts HTTP POST requests

### Step 1: Start in Observe Mode

Begin with `EM-OBSERVE` to monitor PDP decisions without affecting traffic:

```bash
export CAPISCIO_PDP_ENDPOINT=http://localhost:9090/v1/evaluate
export CAPISCIO_ENFORCEMENT_MODE=EM-OBSERVE
export CAPISCIO_PDP_TIMEOUT_MS=500
```

In this mode, PDP DENY decisions are logged but requests proceed. If the PDP is unreachable, requests also proceed with an `ALLOW_OBSERVE` telemetry marker.

Restart the server and monitor logs for policy enforcement events (the event type is `capiscio.policy_enforced`). Each event flattens the PDP verdict into the `capiscio.policy.*` fields described below, including `capiscio.policy.decision`.

Comment on lines +133 to +136
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

capiscio.policy_enforced is mentioned as the log event to watch for, but that event name doesn’t appear elsewhere in the docs and the rest of this page talks in terms of capiscio.policy.decision fields. Align on a single, documented event/key name (or add a short example log line) to avoid confusion.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bbb5e91. Corrected to "Behavior" (American English, consistent with the rest of the docs).

---

### Step 2: Review Decisions

Comment on lines +123 to +140
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Under “Option B: External PDP”, the step headings switch from ### Step 1 to ## Step 2/3/4, which breaks the document hierarchy/TOC (these become peer sections to “Option B”). Make Steps 2-4 the same heading level as Step 1 (e.g., ###).

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bbb5e91. Added Policy Enforcement: how-to/security/policy-enforcement.md to the Security nav section in mkdocs.yml.

Check server logs for policy events. Each request that passes through the PEP emits:

| Field | Description |
|-------|-------------|
| `capiscio.policy.decision` | `ALLOW`, `DENY`, or `ALLOW_OBSERVE` |
| `capiscio.policy.decision_id` | Unique ID from the PDP |
| `capiscio.policy.error_code` | `PDP_UNAVAILABLE` if PDP was unreachable |

Verify that legitimate requests receive `ALLOW` and unauthorized access patterns receive `DENY` before tightening the enforcement mode.

---

### Step 3: Tighten Enforcement

Once decisions look correct, switch to `EM-GUARD`:

```bash
export CAPISCIO_ENFORCEMENT_MODE=EM-GUARD
```

Now PDP DENY decisions block requests with `403 Forbidden`. If the PDP is unavailable, requests are denied with `503 Service Unavailable` (fail-closed).

For full obligation enforcement, use `EM-STRICT`:

```bash
export CAPISCIO_ENFORCEMENT_MODE=EM-STRICT
```

In EM-STRICT, unknown obligation types cause the request to be denied.

---

### Step 4: Configure Break-Glass (Optional)

For emergency access when the PDP is down or misconfigured:

1. Generate a dedicated Ed25519 keypair for break-glass tokens:

```bash
capiscio key gen --out-priv breakglass-key.pem --out-pub breakglass-key.pub.pem
```

2. Configure the server with the public key path:

```bash
export CAPISCIO_BREAKGLASS_PUBLIC_KEY=/etc/capiscio/breakglass-key.pub.pem
```

3. In an emergency, issue a break-glass token and include it as the `X-Capiscio-Breakglass` header. The token must contain a `reason`, scoped `methods`/`routes`, and a short expiry.

!!! warning
Break-glass bypasses authorization but **not** authentication. The request must still carry a valid badge.

---

## Configuration Reference

All PDP-related environment variables:

| Variable | Default | Description |
|----------|---------|-------------|
| `CAPISCIO_EMBEDDED_PDP` | `false` | Enable embedded OPA evaluator (in-process PDP) |
| `CAPISCIO_PDP_ENDPOINT` | _(empty)_ | External PDP URL. Empty + no embedded PDP = badge-only mode |
| `CAPISCIO_PDP_TIMEOUT_MS` | `500` | External PDP query timeout in milliseconds |
| `CAPISCIO_ENFORCEMENT_MODE` | `EM-OBSERVE` | One of: `EM-OBSERVE`, `EM-GUARD`, `EM-STRICT` |
| `CAPISCIO_WORKSPACE` | _(empty)_ | Workspace/tenant UUID (required for embedded PDP) |
| `CAPISCIO_BUNDLE_POLL_INTERVAL` | `30s` | Embedded PDP bundle rebuild interval |
| `CAPISCIO_BUNDLE_STALENESS_THRESHOLD` | `5m` | Embedded PDP bundle age before staleness warnings |
| `CAPISCIO_BREAKGLASS_PUBLIC_KEY` | _(empty)_ | Path to break-glass Ed25519 public key file |
| `CAPISCIO_PEP_ID` | _(empty)_ | PEP instance identifier (sent to PDP as `X-Capiscio-PEP-ID`) |

---

## PDP Request Format

The PEP sends a JSON POST to the PDP endpoint. Your PDP must accept this format:

```jsonc
{
"pip_version": "capiscio.pip.v1",
"subject": {
"did": "did:web:agent.example.com",
"badge_jti": "badge-uuid",
"ial": "1",
"trust_level": "DV" // Badge trust level code: SS(0), REG(1), DV(2), OV(3), EV(4)
},
Comment on lines +221 to +226
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the PDP request example, subject.trust_level is shown as "DV", but other docs commonly represent trust levels as numeric strings ("0""4") and map those to codes like DV/OV/EV. Clarify which representation the PDP actually receives (or include both) to prevent implementers from sending the wrong format.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in bbb5e91. Added a note clarifying the value maps to the capiscio-core trust level enum (e.g. TL-VERIFIED, TL-SELF).

Comment on lines +223 to +226
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code block is marked as JSON but includes // comments, which makes it invalid JSON for copy/paste. Consider removing the inline comments or switching the fence to js/jsonc.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dc0dc76. Changed the fence to jsonc to properly handle the // comments.

"action": {
"operation": "POST /v1/badges",
"capability_class": null
},
"resource": {
"identifier": "/v1/badges"
},
"context": {
"txn_id": "txn-uuid",
"enforcement_mode": "EM-GUARD"
},
"environment": {
"workspace": "00000000-0000-0000-0000-000000000000",
"pep_id": "server-01",
"time": "2026-03-01T12:00:00Z"
}
}
```

And return:

```json
{
"decision": "ALLOW",
"decision_id": "eval-uuid",
"obligations": [],
"reason": "Policy matched: allow-trusted-agents",
"ttl": 300
}
```

---

## Verification

Confirm policy enforcement is active:

```bash
# Send a request and check response headers
curl -v https://your-server/v1/agents \
-H "X-Capiscio-Badge: $BADGE_JWS"
Comment on lines +265 to +267
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verification curl uses https://your-server/api/v1/badges, but elsewhere (and in the server reference) endpoints are under /v1/.... Please align this example URL with the documented base path (or clarify why it differs).

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in dc0dc76. Changed the curl example to use /v1/badges to match the documented base path.

Comment on lines +265 to +267
Copy link

Copilot AI Mar 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verification example calls https://your-server/v1/badges, but the server route summaries in the docs only mention /v1/badges/mint and /v1/badges/{jti}/status (and don’t document a /v1/badges endpoint). Use a documented endpoint for this check, or clarify that this request is to an application endpoint protected by capiscio-server’s PEP rather than a capiscio-server route.

Copilot uses AI. Check for mistakes.
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in fcb8cfd. Changed to /v1/agents which is a documented registry endpoint.


# Look for Server-Timing header with policy timing
# Server-Timing: capiscio-auth;dur=0.6, capiscio-policy;dur=12.3
```
1 change: 1 addition & 0 deletions docs/reference/server/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ capiscio-server provides:

- **Agent Registry** — CRUD operations for agent records
- **Badge CA** — Certificate Authority for issuing trust badges (levels 1-4)
- **Embedded PDP** — In-process OPA policy evaluator with auto-built bundles
- **JWKS Endpoint** — Public key set for badge verification
- **DID Resolution** — `did:web` document serving for registered agents
- **API Key Auth** — Secure API access management
Expand Down
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ nav:
- Verify Inbound: how-to/security/verify-inbound.md
- Trust Badges: how-to/security/badges.md
- Badge Keeper: how-to/security/badge-keeper.md
- Policy Enforcement: how-to/security/policy-enforcement.md
- Security Gateway: how-to/security/gateway-setup.md
- Ephemeral Deployment: how-to/security/ephemeral-deployment.md
- Dev Mode: how-to/security/dev-mode.md
Expand Down
Loading