diff --git a/docs/concepts/enforcement.md b/docs/concepts/enforcement.md
index a2a3464..a79159c 100644
--- a/docs/concepts/enforcement.md
+++ b/docs/concepts/enforcement.md
@@ -127,6 +127,82 @@ Server-Timing: capiscio-auth;dur=0.618;desc="CapiscIO Verification"
* `dur`: Duration in milliseconds.
* `desc`: Description of the operation.
+## 5. Policy Enforcement (PDP Integration)
+
+Badge verification answers **"who is calling?"** — but not **"are they allowed to do this?"**. Policy enforcement adds authorization decisions via a Policy Decision Point (PDP).
+
+### How It Works
+
+The Policy Enforcement Point (PEP) sits between badge verification and your route handlers. After a badge is verified, the PEP asks an external PDP whether the request should proceed.
+
+```
+┌─────────┐ ┌─────────────┐ ┌─────┐ ┌──────────────┐
+│ Request │────▶│ Badge │────▶│ PEP │────▶│ Your Handler │
+│(+ badge) │ │ Verification│ │ │ │ │
+└─────────┘ └─────────────┘ └──┬──┘ └──────────────┘
+ │
+ ┌────▼────┐
+ │ PDP │
+ │(external)│
+ └─────────┘
+```
+
+The PEP sends a **PIP request** (Policy Information Point) containing identity, action, and resource attributes. The PDP returns ALLOW or DENY, plus optional **obligations** the PEP must enforce.
+
+### Enforcement Modes
+
+Four modes control how strictly the PEP enforces PDP decisions. They form a total order from most permissive to most restrictive:
+
+| Mode | PDP Denies | PDP Unavailable | Unknown Obligation |
+|------|-----------|-----------------|-------------------|
+| **EM-OBSERVE** | Log only, allow through | Allow (emit `ALLOW_OBSERVE`) | Log, skip |
+| **EM-GUARD** | Block request | Deny (fail-closed) | Log, skip |
+| **EM-DELEGATE** | Block request | Deny (fail-closed) | Log warning, proceed |
+| **EM-STRICT** | Block request | Deny (fail-closed) | Deny request |
+
+Start with `EM-OBSERVE` to monitor decisions without affecting traffic, then tighten as your policies mature.
+
+### Obligations
+
+A PDP can attach obligations to an ALLOW decision — actions the PEP must perform before forwarding the request. Examples from the RFC:
+
+| Obligation Type | Purpose | Example Params |
+|----------------|---------|----------------|
+| `rate_limit.apply` | Enforce per-agent rate limits | `{ "rpm": 100, "key": "agent-did" }` |
+| `redact.fields` | Remove sensitive fields | `{ "fields": ["/pii/email"] }` |
+| `log.enhanced` | Enhanced audit logging | `{ "level": "audit" }` |
+| `require_step_up` | Require human review | `{ "mode": "human_review" }` |
+
+How the PEP handles obligation failures depends on the enforcement mode. In EM-STRICT, failing to enforce any known obligation blocks the request. In EM-OBSERVE, failures are logged but the request proceeds.
+
+### Decision Caching
+
+The PEP caches ALLOW decisions to reduce PDP latency. Cache keys are derived from the agent DID, badge JTI, operation, and resource. The effective TTL is the minimum of:
+
+- The PDP-returned `ttl`
+- The badge `exp` (expiry)
+- The envelope `expires_at` (if present)
+
+DENY decisions are not cached by default.
+
+### Break-Glass Override
+
+For emergencies, a break-glass token bypasses the PDP entirely. The token is a signed JWS (EdDSA) with a scoped grant and an expiry (recommended: 5 minutes). It skips authorization but **not** authentication — the badge must still be valid.
+
+The PEP emits `capiscio.policy.override = true` telemetry for audit when a break-glass token is used.
+
+!!! warning "Break-Glass Keys"
+ Use a separate Ed25519 keypair for break-glass tokens. Do not reuse your CA badge-signing key.
+
+### Badge-Only Mode
+
+If no PDP endpoint is configured, the PEP is a no-op passthrough. Badge verification still runs, but no policy decisions are made. This is the default behavior.
+
+!!! tip "Getting Started"
+ See [Policy Enforcement Setup](../how-to/security/policy-enforcement.md) for step-by-step configuration.
+
+---
+
## Summary: What Guard Does and Doesn't Do
| Guard Does | Guard Doesn't |
@@ -135,3 +211,5 @@ Server-Timing: capiscio-auth;dur=0.618;desc="CapiscIO Verification"
| ✅ Enforce payload integrity (SHA-256 body hash) | ❌ Manage a central registry (coming soon) |
| ✅ Block replay attacks (timestamp validation) | ❌ Replace your IAM/SSO (human auth) |
| ✅ Work with keys you provision | ❌ Auto-discover external agent keys (coming soon) |
+| ✅ Enforce PDP policy decisions (when configured) | ❌ Provide a PDP (bring your own) |
+| ✅ Support break-glass emergency overrides | ❌ Bypass authentication for overrides |
diff --git a/docs/how-to/security/policy-enforcement.md b/docs/how-to/security/policy-enforcement.md
new file mode 100644
index 0000000..97ea6f2
--- /dev/null
+++ b/docs/how-to/security/policy-enforcement.md
@@ -0,0 +1,181 @@
+---
+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).
+
+---
+
+## 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 forward policy queries to your PDP. The PEP middleware runs after badge verification and before your route handlers.
+
+---
+
+## Prerequisites
+
+- A running CapiscIO server with badge verification enabled
+- A PDP that accepts HTTP POST requests with PIP-format decision requests (RFC-005 §5.1)
+
+---
+
+## 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 `capiscio.policy_enforced` events.
+
+---
+
+## Step 2: Review Decisions
+
+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 mis-configured:
+
+1. Generate a dedicated Ed25519 keypair for break-glass tokens:
+
+ ```bash
+ capiscio keygen --output breakglass-key
+ ```
+
+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_PDP_ENDPOINT` | _(empty)_ | PDP URL. Empty = badge-only mode (no policy enforcement) |
+| `CAPISCIO_PDP_TIMEOUT_MS` | `500` | PDP query timeout in milliseconds |
+| `CAPISCIO_ENFORCEMENT_MODE` | `EM-OBSERVE` | One of: `EM-OBSERVE`, `EM-GUARD`, `EM-DELEGATE`, `EM-STRICT` |
+| `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`) |
+| `CAPISCIO_WORKSPACE` | _(empty)_ | Workspace/tenant identifier |
+
+---
+
+## PDP Request Format
+
+The PEP sends a JSON POST to the PDP endpoint. Your PDP must accept this format:
+
+```json
+{
+ "pip_version": "capiscio.pip.v1",
+ "subject": {
+ "did": "did:web:agent.example.com",
+ "badge_jti": "badge-uuid",
+ "ial": "1",
+ "trust_level": "DV"
+ },
+ "action": {
+ "operation": "POST /api/v1/badges",
+ "capability_class": null
+ },
+ "resource": {
+ "identifier": "/api/v1/badges"
+ },
+ "context": {
+ "txn_id": "txn-uuid",
+ "enforcement_mode": "EM-GUARD"
+ },
+ "environment": {
+ "workspace": "production",
+ "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/api/v1/badges \
+ -H "X-Capiscio-Badge: $BADGE_JWS"
+
+# Look for Server-Timing header with policy timing
+# Server-Timing: capiscio-auth;dur=0.6, capiscio-policy;dur=12.3
+```
diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md
index cb5f686..784329b 100644
--- a/docs/reference/configuration.md
+++ b/docs/reference/configuration.md
@@ -206,6 +206,32 @@ config = SecurityConfig.from_env()
---
+## Policy Enforcement (Server)
+
+These environment variables configure the PDP integration on the CapiscIO server (RFC-005). They are read by the server, not the SDK.
+
+| Variable | Type | Default | Description |
+|----------|------|---------|-------------|
+| `CAPISCIO_PDP_ENDPOINT` | `string` | _(empty)_ | PDP URL. Empty = badge-only mode |
+| `CAPISCIO_PDP_TIMEOUT_MS` | `int` | `500` | PDP query timeout in milliseconds |
+| `CAPISCIO_ENFORCEMENT_MODE` | `string` | `EM-OBSERVE` | `EM-OBSERVE`, `EM-GUARD`, `EM-DELEGATE`, or `EM-STRICT` |
+| `CAPISCIO_BREAKGLASS_PUBLIC_KEY` | `string` | _(empty)_ | Path to break-glass Ed25519 public key |
+| `CAPISCIO_PEP_ID` | `string` | _(empty)_ | PEP instance identifier |
+| `CAPISCIO_WORKSPACE` | `string` | _(empty)_ | Workspace/tenant identifier |
+
+### Example
+
+```bash
+# Enable policy enforcement in observe mode
+export CAPISCIO_PDP_ENDPOINT=http://pdp.internal:9090/v1/evaluate
+export CAPISCIO_ENFORCEMENT_MODE=EM-OBSERVE
+export CAPISCIO_PDP_TIMEOUT_MS=500
+```
+
+See [Policy Enforcement Setup](../how-to/security/policy-enforcement.md) for step-by-step configuration.
+
+---
+
## Complete Example
```python
diff --git a/docs/reference/go-api.md b/docs/reference/go-api.md
index 53b611e..fc047d8 100644
--- a/docs/reference/go-api.md
+++ b/docs/reference/go-api.md
@@ -6,14 +6,14 @@
This API reference is automatically generated from the Go source code in
[capiscio-core](https://github.com/capiscio/capiscio-core) using `gomarkdoc`.
- **Regenerate:** `gomarkdoc ./pkg/... > docs/reference/go-api.md`
+ **Regenerate:** `./scripts/generate-docs.sh`
---
-## Package: agentcard
+# agentcard
```go
-import "github.com/capiscio/capiscio-core/pkg/agentcard"
+import "github.com/capiscio/capiscio-core/v2/pkg/agentcard"
```
Package agentcard defines the data structures for the A2A Agent Card.
@@ -176,27 +176,204 @@ const (
# badge
```go
-import "github.com/capiscio/capiscio-core/pkg/badge"
+import "github.com/capiscio/capiscio-core/v2/pkg/badge"
```
+Package badge provides badge client functionality for requesting badges from a CA.
+
Package badge provides functionality for issuing and verifying Trust Badges.
+Package badge provides badge client functionality for requesting badges from a CA.
+
## Index
+- [Constants](<#constants>)
+- [Variables](<#variables>)
+- [func GetErrorCode\(err error\) string](<#GetErrorCode>)
- [func SignBadge\(claims \*Claims, privateKey crypto.PrivateKey\) \(string, error\)](<#SignBadge>)
+- [type ChallengeResponse](<#ChallengeResponse>)
- [type Claims](<#Claims>)
+ - [func \(c \*Claims\) AgentID\(\) string](<#Claims.AgentID>)
+ - [func \(c \*Claims\) AssuranceLevel\(\) string](<#Claims.AssuranceLevel>)
+ - [func \(c \*Claims\) Domain\(\) string](<#Claims.Domain>)
+ - [func \(c \*Claims\) ExpiresAt\(\) time.Time](<#Claims.ExpiresAt>)
+ - [func \(c \*Claims\) HasProofOfPossession\(\) bool](<#Claims.HasProofOfPossession>)
+ - [func \(c \*Claims\) IsExpired\(\) bool](<#Claims.IsExpired>)
+ - [func \(c \*Claims\) IsNotYetValid\(\) bool](<#Claims.IsNotYetValid>)
+ - [func \(c \*Claims\) IssuedAtTime\(\) time.Time](<#Claims.IssuedAtTime>)
+ - [func \(c \*Claims\) TrustLevel\(\) string](<#Claims.TrustLevel>)
+- [type Client](<#Client>)
+ - [func NewClient\(caURL, apiKey string\) \*Client](<#NewClient>)
+ - [func \(c \*Client\) RequestBadge\(ctx context.Context, opts RequestBadgeOptions\) \(\*RequestBadgeResult, error\)](<#Client.RequestBadge>)
+- [type ClientError](<#ClientError>)
+ - [func \(e \*ClientError\) Error\(\) string](<#ClientError.Error>)
+ - [func \(e \*ClientError\) IsAuthError\(\) bool](<#ClientError.IsAuthError>)
+ - [func \(e \*ClientError\) IsNotFoundError\(\) bool](<#ClientError.IsNotFoundError>)
+- [type ConfirmationClaim](<#ConfirmationClaim>)
- [type CredentialSubject](<#CredentialSubject>)
+- [type DVClient](<#DVClient>)
+ - [func NewDVClient\(caURL string\) \*DVClient](<#NewDVClient>)
+ - [func NewDVClientWithHTTPClient\(caURL string, httpClient \*http.Client\) \*DVClient](<#NewDVClientWithHTTPClient>)
+ - [func \(c \*DVClient\) CreateOrder\(ctx context.Context, domain, challengeType string, jwk \*jose.JSONWebKey\) \(\*DVOrder, error\)](<#DVClient.CreateOrder>)
+ - [func \(c \*DVClient\) FinalizeOrder\(ctx context.Context, orderID string\) \(\*DVGrant, error\)](<#DVClient.FinalizeOrder>)
+ - [func \(c \*DVClient\) GetOrder\(ctx context.Context, orderID string\) \(\*DVOrder, error\)](<#DVClient.GetOrder>)
+- [type DVGrant](<#DVGrant>)
+- [type DVOrder](<#DVOrder>)
+- [type Error](<#Error>)
+ - [func AsError\(err error\) \(\*Error, bool\)](<#AsError>)
+ - [func NewError\(code, message string\) \*Error](<#NewError>)
+ - [func WrapError\(code, message string, cause error\) \*Error](<#WrapError>)
+ - [func \(e \*Error\) Error\(\) string](<#Error.Error>)
+ - [func \(e \*Error\) Is\(target error\) bool](<#Error.Is>)
+ - [func \(e \*Error\) Unwrap\(\) error](<#Error.Unwrap>)
- [type Keeper](<#Keeper>)
- - [func NewKeeper\(config KeeperConfig\) \*Keeper](<#NewKeeper>)
+ - [func NewKeeper\(config KeeperConfig\) \(\*Keeper, error\)](<#NewKeeper>)
- [func \(k \*Keeper\) CheckAndRenew\(\) error](<#Keeper.CheckAndRenew>)
- [func \(k \*Keeper\) Renew\(\) error](<#Keeper.Renew>)
- [func \(k \*Keeper\) Run\(ctx context.Context\) error](<#Keeper.Run>)
+ - [func \(k \*Keeper\) RunWithEvents\(ctx context.Context, events chan\<\- KeeperEvent\) error](<#Keeper.RunWithEvents>)
- [type KeeperConfig](<#KeeperConfig>)
+- [type KeeperEvent](<#KeeperEvent>)
+- [type KeeperEventType](<#KeeperEventType>)
+- [type KeeperMode](<#KeeperMode>)
+- [type PoPClient](<#PoPClient>)
+ - [func NewPoPClient\(caURL, apiKey string\) \*PoPClient](<#NewPoPClient>)
+ - [func NewPoPClientWithHTTPClient\(caURL, apiKey string, httpClient \*http.Client\) \*PoPClient](<#NewPoPClientWithHTTPClient>)
+ - [func \(c \*PoPClient\) RequestPoPBadge\(ctx context.Context, opts RequestPoPBadgeOptions\) \(\*RequestPoPBadgeResult, error\)](<#PoPClient.RequestPoPBadge>)
+- [type PoPProofClaims](<#PoPProofClaims>)
+- [type RenewalResult](<#RenewalResult>)
+- [type RequestBadgeOptions](<#RequestBadgeOptions>)
+- [type RequestBadgeResult](<#RequestBadgeResult>)
+- [type RequestPoPBadgeOptions](<#RequestPoPBadgeOptions>)
+- [type RequestPoPBadgeResult](<#RequestPoPBadgeResult>)
+- [type RevocationCache](<#RevocationCache>)
- [type VerifiableCredential](<#VerifiableCredential>)
- [type Verifier](<#Verifier>)
- [func NewVerifier\(reg registry.Registry\) \*Verifier](<#NewVerifier>)
- [func \(v \*Verifier\) Verify\(ctx context.Context, token string\) \(\*Claims, error\)](<#Verifier.Verify>)
+ - [func \(v \*Verifier\) VerifyWithOptions\(ctx context.Context, token string, opts VerifyOptions\) \(\*VerifyResult, error\)](<#Verifier.VerifyWithOptions>)
+- [type VerifyMode](<#VerifyMode>)
+- [type VerifyOptions](<#VerifyOptions>)
+- [type VerifyResult](<#VerifyResult>)
+
+
+## Constants
+
+Error codes as defined in RFC\-002 §8.4. These are spec\-level error codes, not HTTP status codes.
+
+```go
+const (
+ // ErrCodeMalformed indicates the JWS structure is invalid.
+ ErrCodeMalformed = "BADGE_MALFORMED"
+
+ // ErrCodeSignatureInvalid indicates signature verification failed.
+ ErrCodeSignatureInvalid = "BADGE_SIGNATURE_INVALID"
+
+ // ErrCodeExpired indicates current time >= exp.
+ ErrCodeExpired = "BADGE_EXPIRED"
+
+ // ErrCodeNotYetValid indicates current time < iat.
+ ErrCodeNotYetValid = "BADGE_NOT_YET_VALID"
+
+ // ErrCodeIssuerUntrusted indicates iss is not in the trusted issuer list.
+ ErrCodeIssuerUntrusted = "BADGE_ISSUER_UNTRUSTED"
+
+ // ErrCodeAudienceMismatch indicates the verifier is not in the aud claim.
+ ErrCodeAudienceMismatch = "BADGE_AUDIENCE_MISMATCH"
+
+ // ErrCodeRevoked indicates the badge jti is on the revocation list.
+ ErrCodeRevoked = "BADGE_REVOKED"
+
+ // ErrCodeClaimsInvalid indicates required claims are missing or malformed.
+ ErrCodeClaimsInvalid = "BADGE_CLAIMS_INVALID"
+
+ // ErrCodeAgentDisabled indicates the agent sub is disabled.
+ ErrCodeAgentDisabled = "BADGE_AGENT_DISABLED"
+
+ // ErrCodeRevocationCheckFailed indicates revocation check failed.
+ // RFC-002 v1.3 §7.5: Used when sync fails AND cache stale for levels 2+.
+ ErrCodeRevocationCheckFailed = "REVOCATION_CHECK_FAILED"
+)
+```
+
+
+
+```go
+const (
+ // REVOCATION_CACHE_MAX_STALENESS is the default maximum age for cached data.
+ // RFC-002 v1.3 §7.5: 300 seconds (5 minutes) - revocation cache older than
+ // this is considered stale and triggers fail-closed for levels 2+.
+ REVOCATION_CACHE_MAX_STALENESS = 5 * time.Minute
+
+ // DefaultStaleThreshold is an alias for backward compatibility.
+ // Deprecated: Use REVOCATION_CACHE_MAX_STALENESS instead.
+ DefaultStaleThreshold = REVOCATION_CACHE_MAX_STALENESS
+
+ // StaleFailClosedMinLevel is the minimum trust level that enforces fail-closed
+ // on stale data. RFC-002 v1.3 §7.5: Levels 2+ MUST fail on stale cache.
+ StaleFailClosedMinLevel = 2
+)
+```
+
+DefaultCAURL is the default CapiscIO Registry URL.
+
+```go
+const DefaultCAURL = "https://registry.capisc.io"
+```
+
+DefaultTTL is the default badge TTL per RFC\-002.
+
+```go
+const DefaultTTL = 5 * time.Minute
+```
+
+## Variables
+
+Predefined sentinel errors for common cases. Use these with errors.Is\(\) for type\-safe error checking.
+
+```go
+var (
+ // ErrMalformed is returned when the JWS structure is invalid.
+ ErrMalformed = NewError(ErrCodeMalformed, "badge structure is invalid")
+
+ // ErrSignatureInvalid is returned when signature verification fails.
+ ErrSignatureInvalid = NewError(ErrCodeSignatureInvalid, "signature verification failed")
+
+ // ErrExpired is returned when the badge has expired.
+ ErrExpired = NewError(ErrCodeExpired, "badge has expired")
+
+ // ErrNotYetValid is returned when the badge is not yet valid (iat in future).
+ ErrNotYetValid = NewError(ErrCodeNotYetValid, "badge is not yet valid")
+
+ // ErrIssuerUntrusted is returned when the issuer is not trusted.
+ ErrIssuerUntrusted = NewError(ErrCodeIssuerUntrusted, "issuer is not trusted")
+
+ // ErrAudienceMismatch is returned when verifier is not in audience.
+ ErrAudienceMismatch = NewError(ErrCodeAudienceMismatch, "verifier not in badge audience")
+
+ // ErrRevoked is returned when the badge has been revoked.
+ ErrRevoked = NewError(ErrCodeRevoked, "badge has been revoked")
+
+ // ErrClaimsInvalid is returned when required claims are missing or malformed.
+ ErrClaimsInvalid = NewError(ErrCodeClaimsInvalid, "required claims missing or malformed")
+
+ // ErrAgentDisabled is returned when the agent has been disabled.
+ ErrAgentDisabled = NewError(ErrCodeAgentDisabled, "agent has been disabled")
+
+ // ErrRevocationCheckFailed is returned when revocation check fails with stale cache.
+ // RFC-002 v1.3 §7.5: Used for fail-closed on stale cache for levels 2+.
+ ErrRevocationCheckFailed = NewError(ErrCodeRevocationCheckFailed, "revocation check failed")
+)
+```
+
+
+## func [GetErrorCode]()
+
+```go
+func GetErrorCode(err error) string
+```
+GetErrorCode extracts the error code from an Error, or returns empty string.
## func [SignBadge]()
@@ -207,921 +384,23959 @@ func SignBadge(claims *Claims, privateKey crypto.PrivateKey) (string, error)
SignBadge creates a signed JWS token from the given claims using the private key. It defaults to EdDSA \(Ed25519\) signing.
+
+## type [ChallengeResponse]()
+
+ChallengeResponse represents the server's challenge response.
+
+```go
+type ChallengeResponse struct {
+ ChallengeID string `json:"challenge_id"`
+ Nonce string `json:"nonce"`
+ ExpiresAt time.Time `json:"expires_at"`
+ Aud string `json:"aud"`
+ HTU string `json:"htu"`
+ HTM string `json:"htm"`
+}
+```
+
-## type [Claims]()
+## type [Claims]()
-Claims represents the JWT claims payload for a CapiscIO Trust Badge. It follows the structure defined in the Minimal Authority Stack plan.
+Claims represents the JWT claims payload for a CapiscIO Trust Badge. See RFC\-002: Trust Badge Specification.
```go
type Claims struct {
- // Issuer is the entity that issued the badge (e.g., "https://registry.capisc.io").
+ // JTI is the unique Badge ID (UUID v4). Used for revocation and audit.
+ JTI string `json:"jti"`
+
+ // Issuer is the CA that signed the Badge (e.g., "https://registry.capisc.io").
Issuer string `json:"iss"`
- // Subject is the unique identifier of the Agent (e.g., "did:capiscio:agent:12345").
+ // Subject is the agent's DID. MUST be a valid did:web identifier.
+ // Format: did:web:registry.capisc.io:agents:
Subject string `json:"sub"`
+ // Audience is the list of trust domains/services where Badge is valid.
+ // Optional. If present, verifiers MUST check their identity is included.
+ Audience []string `json:"aud,omitempty"`
+
// IssuedAt is the timestamp when the badge was issued (Unix timestamp).
IssuedAt int64 `json:"iat"`
// Expiry is the timestamp when the badge expires (Unix timestamp).
Expiry int64 `json:"exp"`
+ // NotBefore is the timestamp before which the badge MUST NOT be accepted.
+ // Optional. Per RFC-002 §4.3.1.
+ NotBefore int64 `json:"nbf,omitempty"`
+
+ // IAL is the Identity Assurance Level. REQUIRED per RFC-002 §4.3.2.
+ // "0" = Account-attested (IAL-0), "1" = Proof of Possession (IAL-1).
+ IAL string `json:"ial"`
+
// Key is the public key of the subject, embedded for offline verification.
+ // REQUIRED in production. MAY be omitted in non-production environments.
Key *jose.JSONWebKey `json:"key,omitempty"`
+ // CNF is the confirmation claim per RFC 7800.
+ // When present, binds the badge to a specific key holder.
+ // Used for Proof of Possession (PoP) badges (RFC-002 §7.2.2, RFC-003).
+ CNF *ConfirmationClaim `json:"cnf,omitempty"`
+
+ // PoPChallengeID is a reference to the PoP challenge used during issuance.
+ // Optional. Provides audit trail for PoP-issued badges (RFC-002 §4.3.3).
+ PoPChallengeID string `json:"pop_challenge_id,omitempty"`
+
+ // AgentCardHash is the SHA-256 hash of the canonical AgentCard at issuance.
+ // Optional. Enables verifiers to detect AgentCard drift (RFC-002 §4.3.3).
+ AgentCardHash string `json:"agent_card_hash,omitempty"`
+
+ // DIDDocHash is the SHA-256 hash of the DID Document at issuance.
+ // Optional. Enables verifiers to detect key rotation (RFC-002 §4.3.3).
+ DIDDocHash string `json:"did_doc_hash,omitempty"`
+
// VC contains the Verifiable Credential data.
VC VerifiableCredential `json:"vc"`
}
```
-
-## type [CredentialSubject]()
-
-CredentialSubject contains the specific claims.
+
+### func \(\*Claims\) [AgentID]()
```go
-type CredentialSubject struct {
- // Domain is the security domain of the agent (e.g., "finance.internal").
- Domain string `json:"domain,omitempty"`
-
- // Level indicates the trust level (RFC-002 §5): "0"=SS, "1"=REG, "2"=DV, "3"=OV, "4"=EV.
- Level string `json:"level,omitempty"`
-}
+func (c *Claims) AgentID() string
```
-
-## type [Keeper]()
+AgentID extracts the agent ID from the Subject DID. For did:web:registry.capisc.io:agents:my\-agent\-001, returns "my\-agent\-001". Returns empty string if the DID format is invalid.
-Keeper manages the lifecycle of a Trust Badge file.
+
+### func \(\*Claims\) [AssuranceLevel]()
```go
-type Keeper struct {
- // contains filtered or unexported fields
-}
+func (c *Claims) AssuranceLevel() string
```
-
-### func [NewKeeper]()
+AssuranceLevel returns the identity assurance level of the badge. Per RFC\-002 §7.2.1: \- IAL\-0: Account\-attested bearer badge \- IAL\-1: Proof of Possession badge The IAL claim is authoritative; cnf is supporting evidence.
+
+
+### func \(\*Claims\) [Domain]()
```go
-func NewKeeper(config KeeperConfig) *Keeper
+func (c *Claims) Domain() string
```
-NewKeeper creates a new Keeper.
+Domain returns the domain from the VC credential subject.
-
-### func \(\*Keeper\) [CheckAndRenew]()
+
+### func \(\*Claims\) [ExpiresAt]()
```go
-func (k *Keeper) CheckAndRenew() error
+func (c *Claims) ExpiresAt() time.Time
```
-CheckAndRenew checks if the badge needs renewal and renews it if necessary.
+ExpiresAt returns the expiry time as a time.Time.
-
-### func \(\*Keeper\) [Renew]()
+
+### func \(\*Claims\) [HasProofOfPossession]()
```go
-func (k *Keeper) Renew() error
+func (c *Claims) HasProofOfPossession() bool
```
-Renew generates a new badge and writes it to disk.
+HasProofOfPossession returns true if this is a PoP\-issued badge.
-
-### func \(\*Keeper\) [Run]()
+
+### func \(\*Claims\) [IsExpired]()
```go
-func (k *Keeper) Run(ctx context.Context) error
+func (c *Claims) IsExpired() bool
```
-Run starts the keeper loop.
-
-
-## type [KeeperConfig]()
+IsExpired returns true if the badge has expired.
-KeeperConfig holds configuration for the Badge Keeper.
+
+### func \(\*Claims\) [IsNotYetValid]()
```go
-type KeeperConfig struct {
- PrivateKey crypto.PrivateKey
- Claims Claims
- OutputFile string
- Expiry time.Duration
- RenewBefore time.Duration
- CheckInterval time.Duration
-}
+func (c *Claims) IsNotYetValid() bool
```
-
-## type [VerifiableCredential]()
+IsNotYetValid returns true if the badge's iat is in the future.
-VerifiableCredential represents the simplified VC object.
+
+### func \(\*Claims\) [IssuedAtTime]()
```go
-type VerifiableCredential struct {
- // Type is the JSON-LD type(s) of the credential.
- Type []string `json:"type"`
-
- // CredentialSubject contains the claims about the subject.
- CredentialSubject CredentialSubject `json:"credentialSubject"`
-}
+func (c *Claims) IssuedAtTime() time.Time
```
-
-## type [Verifier]()
+IssuedAtTime returns the issued\-at time as a time.Time.
-Verifier validates TrustBadges.
+
+### func \(\*Claims\) [TrustLevel]()
```go
-type Verifier struct {
- // contains filtered or unexported fields
-}
+func (c *Claims) TrustLevel() string
```
-
-### func [NewVerifier]()
+TrustLevel returns the trust level from the VC credential subject. Returns "1", "2", or "3", or empty string if not set.
+
+
+## type [Client]()
+
+Client is an HTTP client for requesting badges from a CA.
```go
-func NewVerifier(reg registry.Registry) *Verifier
+type Client struct {
+ CAURL string
+ APIKey string
+ HTTPClient *http.Client
+}
```
-NewVerifier creates a new Badge Verifier.
-
-
-### func \(\*Verifier\) [Verify]()
+
+### func [NewClient]()
```go
-func (v *Verifier) Verify(ctx context.Context, token string) (*Claims, error)
+func NewClient(caURL, apiKey string) *Client
```
-Verify checks the validity of a TrustBadge JWS token.
+NewClient creates a new badge client.
-# crypto
+
+### func \(\*Client\) [RequestBadge]()
```go
-import "github.com/capiscio/capiscio-core/pkg/crypto"
+func (c *Client) RequestBadge(ctx context.Context, opts RequestBadgeOptions) (*RequestBadgeResult, error)
```
-Package crypto provides cryptographic utilities for CapiscIO.
+RequestBadge requests a new badge from the CA.
-## Index
+
+## type [ClientError]()
-- [func CreateCanonicalJSON\(card \*agentcard.AgentCard\) \(\[\]byte, error\)](<#CreateCanonicalJSON>)
-- [type DefaultJWKSFetcher](<#DefaultJWKSFetcher>)
- - [func NewDefaultJWKSFetcher\(\) \*DefaultJWKSFetcher](<#NewDefaultJWKSFetcher>)
- - [func \(f \*DefaultJWKSFetcher\) Fetch\(ctx context.Context, url string\) \(\*jose.JSONWebKeySet, error\)](<#DefaultJWKSFetcher.Fetch>)
- - [func \(f \*DefaultJWKSFetcher\) FlushCache\(\)](<#DefaultJWKSFetcher.FlushCache>)
- - [func \(f \*DefaultJWKSFetcher\) SetTTL\(ttl time.Duration\)](<#DefaultJWKSFetcher.SetTTL>)
-- [type JWKSFetcher](<#JWKSFetcher>)
-- [type SignatureResult](<#SignatureResult>)
-- [type SignatureVerificationResult](<#SignatureVerificationResult>)
-- [type VerificationSummary](<#VerificationSummary>)
-- [type Verifier](<#Verifier>)
- - [func NewVerifier\(\) \*Verifier](<#NewVerifier>)
- - [func NewVerifierWithFetcher\(fetcher JWKSFetcher\) \*Verifier](<#NewVerifierWithFetcher>)
- - [func \(v \*Verifier\) VerifyAgentCardSignatures\(ctx context.Context, card \*agentcard.AgentCard\) \(\*SignatureVerificationResult, error\)](<#Verifier.VerifyAgentCardSignatures>)
+ClientError represents an error from the badge client.
+```go
+type ClientError struct {
+ Code string
+ Message string
+}
+```
-
-## func [CreateCanonicalJSON]()
+
+### func \(\*ClientError\) [Error]()
```go
-func CreateCanonicalJSON(card *agentcard.AgentCard) ([]byte, error)
+func (e *ClientError) Error() string
```
-CreateCanonicalJSON creates a canonical JSON representation of the Agent Card for signature verification. It removes the "signatures" field and ensures keys are sorted \(which encoding/json does by default\).
-
-## type [DefaultJWKSFetcher]()
-DefaultJWKSFetcher is the default implementation of JWKSFetcher.
+
+### func \(\*ClientError\) [IsAuthError]()
```go
-type DefaultJWKSFetcher struct {
- // contains filtered or unexported fields
-}
+func (e *ClientError) IsAuthError() bool
```
-
-### func [NewDefaultJWKSFetcher]()
+IsAuthError returns true if this is an authentication error.
+
+
+### func \(\*ClientError\) [IsNotFoundError]()
```go
-func NewDefaultJWKSFetcher() *DefaultJWKSFetcher
+func (e *ClientError) IsNotFoundError() bool
```
-NewDefaultJWKSFetcher creates a new fetcher with a default HTTP client and 1 hour cache TTL.
+IsNotFoundError returns true if the agent was not found.
-
-### func \(\*DefaultJWKSFetcher\) [Fetch]()
+
+## type [ConfirmationClaim]()
+
+ConfirmationClaim represents the cnf claim per RFC 7800. Used to bind a badge to a specific key for Proof of Possession.
```go
-func (f *DefaultJWKSFetcher) Fetch(ctx context.Context, url string) (*jose.JSONWebKeySet, error)
+type ConfirmationClaim struct {
+ // KID is the key ID referencing the key in the DID Document.
+ // This is the primary mechanism for PoP badges.
+ KID string `json:"kid,omitempty"`
+
+ // JWK is the full JWK of the confirmation key (alternative to kid).
+ JWK *jose.JSONWebKey `json:"jwk,omitempty"`
+
+ // JKT is the JWK thumbprint (SHA-256) of the confirmation key.
+ JKT string `json:"jkt,omitempty"`
+}
```
-Fetch retrieves the JWKS from the specified URL, using cache if available.
+
+## type [CredentialSubject]()
-
-### func \(\*DefaultJWKSFetcher\) [FlushCache]()
+CredentialSubject contains the specific claims.
```go
-func (f *DefaultJWKSFetcher) FlushCache()
+type CredentialSubject struct {
+ // Domain is the agent's home domain.
+ // MUST be validated according to the trust level's requirements.
+ Domain string `json:"domain,omitempty"`
+
+ // Level indicates the trust level: "1" (DV), "2" (OV), or "3" (EV).
+ Level string `json:"level,omitempty"`
+}
```
-FlushCache clears all cached JWKS entries.
+
+## type [DVClient]()
-
-### func \(\*DefaultJWKSFetcher\) [SetTTL]()
+DVClient is an HTTP client for Domain Validated badge orders \(RFC\-002 v1.2\).
```go
-func (f *DefaultJWKSFetcher) SetTTL(ttl time.Duration)
+type DVClient struct {
+ CAURL string
+ HTTPClient *http.Client
+}
```
-SetTTL configures the cache time\-to\-live.
+
+### func [NewDVClient]()
-
-## type [JWKSFetcher]()
+```go
+func NewDVClient(caURL string) *DVClient
+```
-JWKSFetcher handles fetching and caching of JSON Web Key Sets.
+NewDVClient creates a new DV client with a default HTTP client.
+
+
+### func [NewDVClientWithHTTPClient]()
```go
-type JWKSFetcher interface {
- Fetch(ctx context.Context, url string) (*jose.JSONWebKeySet, error)
-}
+func NewDVClientWithHTTPClient(caURL string, httpClient *http.Client) *DVClient
```
-
-## type [SignatureResult]()
+NewDVClientWithHTTPClient creates a new DV client with a custom HTTP client.
-SignatureResult holds the details of a single signature verification.
+
+### func \(\*DVClient\) [CreateOrder]()
```go
-type SignatureResult struct {
- Index int
- Valid bool
- Algorithm string
- KeyID string
- Issuer string
- JWKSUri string
- Error string
-}
+func (c *DVClient) CreateOrder(ctx context.Context, domain, challengeType string, jwk *jose.JSONWebKey) (*DVOrder, error)
```
-
-## type [SignatureVerificationResult]()
+CreateOrder creates a new DV badge order.
-SignatureVerificationResult contains the result of verifying all signatures.
+
+### func \(\*DVClient\) [FinalizeOrder]()
```go
-type SignatureVerificationResult struct {
- Valid bool
- Signatures []SignatureResult
- Summary VerificationSummary
-}
+func (c *DVClient) FinalizeOrder(ctx context.Context, orderID string) (*DVGrant, error)
```
-
-## type [VerificationSummary]()
+FinalizeOrder finalizes a DV badge order and receives a grant.
-VerificationSummary summarizes the results of all signature verifications.
+
+### func \(\*DVClient\) [GetOrder]()
```go
-type VerificationSummary struct {
- Total int
- Valid int
- Failed int
- Errors []string
-}
+func (c *DVClient) GetOrder(ctx context.Context, orderID string) (*DVOrder, error)
```
-
-## type [Verifier]()
+GetOrder gets the status of a DV badge order.
-Verifier handles Agent Card signature verification.
+
+## type [DVGrant]()
+
+DVGrant represents a DV grant JWT.
```go
-type Verifier struct {
- // contains filtered or unexported fields
+type DVGrant struct {
+ Grant string
+ ExpiresAt time.Time
}
```
-
-### func [NewVerifier]()
+
+## type [DVOrder]()
+
+DVOrder represents a DV badge order.
```go
-func NewVerifier() *Verifier
+type DVOrder struct {
+ ID string
+ Domain string
+ ChallengeType string
+ ChallengeToken string
+ Status string
+ ValidationURL string
+ DNSRecord string
+ ExpiresAt time.Time
+ FinalizedAt *time.Time
+}
```
-NewVerifier creates a new Verifier with the default JWKS fetcher.
+
+## type [Error]()
-
-### func [NewVerifierWithFetcher]()
+Error represents a badge verification error with an RFC\-002 error code.
```go
-func NewVerifierWithFetcher(fetcher JWKSFetcher) *Verifier
-```
+type Error struct {
+ // Code is one of the BADGE_* error codes.
+ Code string
-NewVerifierWithFetcher creates a new Verifier with a custom JWKS fetcher.
+ // Message is a human-readable description.
+ Message string
-
-### func \(\*Verifier\) [VerifyAgentCardSignatures]()
+ // Cause is the underlying error, if any.
+ Cause error
+}
+```
+
+
+### func [AsError]()
```go
-func (v *Verifier) VerifyAgentCardSignatures(ctx context.Context, card *agentcard.AgentCard) (*SignatureVerificationResult, error)
+func AsError(err error) (*Error, bool)
```
-VerifyAgentCardSignatures verifies all signatures in an Agent Card.
+AsError checks if err is an Error and returns it if so.
-# gateway
+
+### func [NewError]()
```go
-import "github.com/capiscio/capiscio-core/pkg/gateway"
+func NewError(code, message string) *Error
```
-Package gateway provides the HTTP middleware for the CapiscIO Security Sidecar.
+NewError creates a new Error with the given code and message.
-## Index
+
+### func [WrapError]()
-- [func ExtractBadge\(r \*http.Request\) string](<#ExtractBadge>)
-- [func NewAuthMiddleware\(verifier \*badge.Verifier, next http.Handler\) http.Handler](<#NewAuthMiddleware>)
+```go
+func WrapError(code, message string, cause error) *Error
+```
+WrapError creates a new Error that wraps an underlying error.
-
-## func [ExtractBadge]()
+
+### func \(\*Error\) [Error]()
```go
-func ExtractBadge(r *http.Request) string
+func (e *Error) Error() string
```
-ExtractBadge retrieves the badge from headers.
+Error implements the error interface.
-
-## func [NewAuthMiddleware]()
+
+### func \(\*Error\) [Is]()
```go
-func NewAuthMiddleware(verifier *badge.Verifier, next http.Handler) http.Handler
+func (e *Error) Is(target error) bool
```
-NewAuthMiddleware creates a middleware that enforces Badge validity.
+Is checks if the error matches a target error code.
-# protocol
+
+### func \(\*Error\) [Unwrap]()
```go
-import "github.com/capiscio/capiscio-core/pkg/protocol"
+func (e *Error) Unwrap() error
```
-Package protocol defines the interfaces and implementations for communicating with A2A agents.
+Unwrap returns the underlying cause for errors.Is/errors.As.
+
+
+## type [Keeper]()
+
+Keeper manages the lifecycle of a Trust Badge file.
+
+```go
+type Keeper struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewKeeper]()
+
+```go
+func NewKeeper(config KeeperConfig) (*Keeper, error)
+```
+
+NewKeeper creates a new Keeper. Returns an error if an unsupported mode is specified.
+
+
+### func \(\*Keeper\) [CheckAndRenew]()
+
+```go
+func (k *Keeper) CheckAndRenew() error
+```
+
+CheckAndRenew checks if the badge needs renewal and renews it if necessary. This is the legacy method for backward compatibility.
+
+
+### func \(\*Keeper\) [Renew]()
+
+```go
+func (k *Keeper) Renew() error
+```
+
+Renew generates a new badge and writes it to disk. This is the legacy method for backward compatibility.
+
+
+### func \(\*Keeper\) [Run]()
+
+```go
+func (k *Keeper) Run(ctx context.Context) error
+```
+
+Run starts the keeper loop.
+
+
+### func \(\*Keeper\) [RunWithEvents]()
+
+```go
+func (k *Keeper) RunWithEvents(ctx context.Context, events chan<- KeeperEvent) error
+```
+
+RunWithEvents starts the keeper loop and sends events to the provided channel. The channel is closed when the keeper stops.
+
+
+## type [KeeperConfig]()
+
+KeeperConfig holds configuration for the Badge Keeper.
+
+```go
+type KeeperConfig struct {
+ // Mode: self-sign, ca (deprecated), or pop (recommended)
+ Mode KeeperMode
+
+ // Common settings
+ OutputFile string
+ Expiry time.Duration
+ RenewBefore time.Duration
+ CheckInterval time.Duration
+ Domain string
+ TrustLevel string
+
+ // Self-sign mode settings
+ PrivateKey crypto.PrivateKey
+ Claims Claims
+
+ // CA mode settings (IAL-0, deprecated)
+ CAURL string
+ APIKey string
+ AgentID string
+
+ // PoP mode settings (IAL-1, recommended)
+ // AgentDID is the DID of the agent (e.g., did:key:z6Mk...)
+ AgentDID string
+ // Audience is the optional audience restrictions for the badge
+ Audience []string
+}
+```
+
+
+## type [KeeperEvent]()
+
+KeeperEvent represents an event emitted by the badge keeper.
+
+```go
+type KeeperEvent struct {
+ Type KeeperEventType
+ BadgeJTI string
+ Subject string
+ TrustLevel string
+ ExpiresAt time.Time
+ Error string
+ ErrorCode string
+ Timestamp time.Time
+ Token string // The badge token (optional, for renewed events)
+}
+```
+
+
+## type [KeeperEventType]()
+
+KeeperEventType defines the type of event emitted by the keeper.
+
+```go
+type KeeperEventType string
+```
+
+Keeper event types.
+
+```go
+const (
+ // KeeperEventStarted indicates the keeper has started.
+ KeeperEventStarted KeeperEventType = "started"
+ // KeeperEventRenewed indicates a badge was renewed.
+ KeeperEventRenewed KeeperEventType = "renewed"
+ // KeeperEventError indicates an error occurred.
+ KeeperEventError KeeperEventType = "error"
+ // KeeperEventStopped indicates the keeper has stopped.
+ KeeperEventStopped KeeperEventType = "stopped"
+)
+```
+
+
+## type [KeeperMode]()
+
+KeeperMode defines the mode of operation for the keeper.
+
+```go
+type KeeperMode string
+```
+
+
+
+```go
+const (
+ // KeeperModeSelfSign generates self-signed badges locally.
+ KeeperModeSelfSign KeeperMode = "self-sign"
+ // KeeperModeCA requests badges from a Certificate Authority (IAL-0, deprecated).
+ // Deprecated: Use KeeperModePoP for production - IAL-0 lacks cryptographic key binding.
+ KeeperModeCA KeeperMode = "ca"
+ // KeeperModePoP requests badges using Proof of Possession (RFC-003 IAL-1).
+ // This is the recommended mode for production as it provides cryptographic key binding.
+ KeeperModePoP KeeperMode = "pop"
+)
+```
+
+
+## type [PoPClient]()
+
+PoPClient is an HTTP client for requesting badges using Proof of Possession \(RFC\-003\). This provides IAL\-1 badge issuance with cryptographic key binding.
+
+```go
+type PoPClient struct {
+ CAURL string
+ APIKey string
+ HTTPClient *http.Client
+}
+```
+
+
+### func [NewPoPClient]()
+
+```go
+func NewPoPClient(caURL, apiKey string) *PoPClient
+```
+
+NewPoPClient creates a new PoP badge client with a default HTTP client. The default HTTP client uses a 30\-second timeout.
+
+
+### func [NewPoPClientWithHTTPClient]()
+
+```go
+func NewPoPClientWithHTTPClient(caURL, apiKey string, httpClient *http.Client) *PoPClient
+```
+
+NewPoPClientWithHTTPClient creates a new PoP badge client with a custom HTTP client. If httpClient is nil, a default client with 30\-second timeout is used.
+
+
+### func \(\*PoPClient\) [RequestPoPBadge]()
+
+```go
+func (c *PoPClient) RequestPoPBadge(ctx context.Context, opts RequestPoPBadgeOptions) (*RequestPoPBadgeResult, error)
+```
+
+RequestPoPBadge requests a badge using the PoP protocol \(RFC\-003 IAL\-1\). This provides cryptographic proof that the requester controls the DID's private key.
+
+
+## type [PoPProofClaims]()
+
+PoPProofClaims represents the claims in a PoP proof JWS.
+
+```go
+type PoPProofClaims struct {
+ CID string `json:"cid"` // Challenge ID
+ Nonce string `json:"nonce"` // Server nonce
+ Sub string `json:"sub"` // Subject (DID)
+ Aud string `json:"aud"` // Audience (registry)
+ HTU string `json:"htu"` // HTTP Target URI
+ HTM string `json:"htm"` // HTTP Method
+ IAT int64 `json:"iat"` // Issued at
+ Exp int64 `json:"exp"` // Expiration
+ JTI string `json:"jti"` // Proof JTI (unique)
+}
+```
+
+
+## type [RenewalResult]()
+
+RenewalResult contains details about a renewed badge.
+
+```go
+type RenewalResult struct {
+ JTI string
+ Subject string
+ TrustLevel string
+ ExpiresAt time.Time
+ Token string
+}
+```
+
+
+## type [RequestBadgeOptions]()
+
+RequestBadgeOptions contains options for badge request.
+
+```go
+type RequestBadgeOptions struct {
+ AgentID string
+ Domain string
+ TTL time.Duration
+ TrustLevel string
+ Audience []string
+}
+```
+
+
+## type [RequestBadgeResult]()
+
+RequestBadgeResult contains the result of a badge request.
+
+```go
+type RequestBadgeResult struct {
+ Token string
+ JTI string
+ Subject string
+ TrustLevel string
+ ExpiresAt time.Time
+}
+```
+
+
+## type [RequestPoPBadgeOptions]()
+
+RequestPoPBadgeOptions contains options for PoP badge request.
+
+```go
+type RequestPoPBadgeOptions struct {
+ // AgentDID is the DID of the agent (e.g., did:key:z6Mk... or did:web:...)
+ AgentDID string
+
+ // PrivateKey is the agent's private key for signing the PoP proof
+ PrivateKey crypto.PrivateKey
+
+ // TTL is the requested badge TTL (optional, default 5 min)
+ TTL time.Duration
+
+ // Audience is the optional audience restrictions
+ Audience []string
+}
+```
+
+
+## type [RequestPoPBadgeResult]()
+
+RequestPoPBadgeResult contains the result of a PoP badge request.
+
+```go
+type RequestPoPBadgeResult struct {
+ Token string
+ JTI string
+ Subject string
+ TrustLevel string
+ AssuranceLevel string // "IAL-1" for PoP badges
+ ExpiresAt time.Time
+ CNF map[string]interface{} // Confirmation claim with key binding
+}
+```
+
+
+## type [RevocationCache]()
+
+RevocationCache provides access to cached revocation data.
+
+```go
+type RevocationCache interface {
+ // IsRevoked checks if a badge jti is in the revocation cache.
+ IsRevoked(jti string) bool
+
+ // IsStale returns true if the cache is older than the threshold.
+ IsStale(threshold time.Duration) bool
+}
+```
+
+
+## type [VerifiableCredential]()
+
+VerifiableCredential represents the simplified VC object.
+
+```go
+type VerifiableCredential struct {
+ // Type is the JSON-LD type(s) of the credential.
+ // MUST include "VerifiableCredential" and "AgentIdentity".
+ Type []string `json:"type"`
+
+ // CredentialSubject contains the claims about the subject.
+ CredentialSubject CredentialSubject `json:"credentialSubject"`
+}
+```
+
+
+## type [Verifier]()
+
+Verifier validates TrustBadges per RFC\-002.
+
+```go
+type Verifier struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewVerifier]()
+
+```go
+func NewVerifier(reg registry.Registry) *Verifier
+```
+
+NewVerifier creates a new Badge Verifier.
+
+
+### func \(\*Verifier\) [Verify]()
+
+```go
+func (v *Verifier) Verify(ctx context.Context, token string) (*Claims, error)
+```
+
+Verify checks the validity of a TrustBadge JWS token using default options. For more control, use VerifyWithOptions.
+
+
+### func \(\*Verifier\) [VerifyWithOptions]()
+
+```go
+func (v *Verifier) VerifyWithOptions(ctx context.Context, token string, opts VerifyOptions) (*VerifyResult, error)
+```
+
+VerifyWithOptions performs badge verification with the specified options. Implements RFC\-002 §8.1 verification flow.
+
+For Level 0 self\-signed badges \(did:key issuer\):
+
+- Public key is extracted from the did:key identifier
+- Revocation check is skipped \(self\-signed badges not in registry\)
+- Agent status check is skipped \(no registry\)
+- iss must equal sub \(self\-assertion only\)
+
+
+## type [VerifyMode]()
+
+VerifyMode determines how verification is performed.
+
+```go
+type VerifyMode int
+```
+
+
+
+```go
+const (
+ // VerifyModeOnline performs real-time checks against the registry.
+ // This includes revocation checks and agent status checks.
+ VerifyModeOnline VerifyMode = iota
+
+ // VerifyModeOffline uses only local trust store and revocation cache.
+ // Does not make network requests.
+ VerifyModeOffline
+
+ // VerifyModeHybrid uses online checks when available, falls back to cache.
+ VerifyModeHybrid
+)
+```
+
+
+## type [VerifyOptions]()
+
+VerifyOptions configures badge verification behavior.
+
+```go
+type VerifyOptions struct {
+ // Mode determines online/offline verification behavior.
+ Mode VerifyMode
+
+ // TrustedIssuers is a list of allowed issuer DIDs (did:web or did:key).
+ // If empty, all issuers are accepted (not recommended for production).
+ // For Level 0 self-signed badges, the did:key issuer must be in this list
+ // or AcceptSelfSigned must be true.
+ TrustedIssuers []string
+
+ // AcceptSelfSigned allows Level 0 self-signed badges (did:key issuer).
+ // WARNING: Production verifiers SHOULD NOT accept self-signed badges
+ // unless explicitly required for specific use cases.
+ // Default: false (reject self-signed badges)
+ AcceptSelfSigned bool
+
+ // Audience is the verifier's identity for audience validation.
+ // If set and badge has aud claim, verifier must be in audience.
+ Audience string
+
+ // SkipRevocationCheck disables revocation checking (for testing only).
+ SkipRevocationCheck bool
+
+ // SkipAgentStatusCheck disables agent status checking (for testing only).
+ SkipAgentStatusCheck bool
+
+ // RevocationCache provides cached revocations for offline mode.
+ RevocationCache RevocationCache
+
+ // StaleThreshold is the maximum age of cached data before it's considered stale.
+ // RFC-002 v1.3: For IAL-2+ badges, stale cache causes verification to fail.
+ // Default: 24 hours if not set.
+ StaleThreshold time.Duration
+
+ // FailOpen allows verification to succeed even when staleness checks fail.
+ // WARNING: This is NOT recommended for production.
+ // RFC-002 v1.3 requires fail-closed behavior by default.
+ // Default: false (fail-closed)
+ FailOpen bool
+
+ // Now overrides the current time (for testing).
+ Now func() time.Time
+}
+```
+
+
+## type [VerifyResult]()
+
+VerifyResult contains the result of badge verification.
+
+```go
+type VerifyResult struct {
+ // Claims contains the verified badge claims.
+ Claims *Claims
+
+ // Mode indicates which verification mode was used.
+ Mode VerifyMode
+
+ // Warnings contains non-fatal issues encountered.
+ Warnings []string
+}
+```
+
+# crypto
+
+```go
+import "github.com/capiscio/capiscio-core/v2/pkg/crypto"
+```
+
+Package crypto provides cryptographic utilities for CapiscIO.
## Index
-- [type Client](<#Client>)
-- [type HTTPClient](<#HTTPClient>)
- - [func NewHTTPClient\(url string\) \*HTTPClient](<#NewHTTPClient>)
- - [func \(c \*HTTPClient\) Close\(\) error](<#HTTPClient.Close>)
- - [func \(c \*HTTPClient\) Ping\(ctx context.Context\) \(time.Duration, error\)](<#HTTPClient.Ping>)
-- [type JSONRPCClient](<#JSONRPCClient>)
- - [func NewJSONRPCClient\(url string\) \*JSONRPCClient](<#NewJSONRPCClient>)
- - [func \(c \*JSONRPCClient\) Close\(\) error](<#JSONRPCClient.Close>)
- - [func \(c \*JSONRPCClient\) Ping\(ctx context.Context\) \(time.Duration, error\)](<#JSONRPCClient.Ping>)
+- [func CreateCanonicalJSON\(card \*agentcard.AgentCard\) \(\[\]byte, error\)](<#CreateCanonicalJSON>)
+- [type DefaultJWKSFetcher](<#DefaultJWKSFetcher>)
+ - [func NewDefaultJWKSFetcher\(\) \*DefaultJWKSFetcher](<#NewDefaultJWKSFetcher>)
+ - [func \(f \*DefaultJWKSFetcher\) Fetch\(ctx context.Context, url string\) \(\*jose.JSONWebKeySet, error\)](<#DefaultJWKSFetcher.Fetch>)
+ - [func \(f \*DefaultJWKSFetcher\) FlushCache\(\)](<#DefaultJWKSFetcher.FlushCache>)
+ - [func \(f \*DefaultJWKSFetcher\) SetTTL\(ttl time.Duration\)](<#DefaultJWKSFetcher.SetTTL>)
+- [type JWKSFetcher](<#JWKSFetcher>)
+- [type SignatureResult](<#SignatureResult>)
+- [type SignatureVerificationResult](<#SignatureVerificationResult>)
+- [type VerificationSummary](<#VerificationSummary>)
+- [type Verifier](<#Verifier>)
+ - [func NewVerifier\(\) \*Verifier](<#NewVerifier>)
+ - [func NewVerifierWithFetcher\(fetcher JWKSFetcher\) \*Verifier](<#NewVerifierWithFetcher>)
+ - [func \(v \*Verifier\) VerifyAgentCardSignatures\(ctx context.Context, card \*agentcard.AgentCard\) \(\*SignatureVerificationResult, error\)](<#Verifier.VerifyAgentCardSignatures>)
+
+
+
+## func [CreateCanonicalJSON]()
+
+```go
+func CreateCanonicalJSON(card *agentcard.AgentCard) ([]byte, error)
+```
+
+CreateCanonicalJSON creates a canonical JSON representation of the Agent Card for signature verification. It removes the "signatures" field and ensures keys are sorted \(which encoding/json does by default\).
+
+
+## type [DefaultJWKSFetcher]()
+
+DefaultJWKSFetcher is the default implementation of JWKSFetcher.
+
+```go
+type DefaultJWKSFetcher struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewDefaultJWKSFetcher]()
+
+```go
+func NewDefaultJWKSFetcher() *DefaultJWKSFetcher
+```
+
+NewDefaultJWKSFetcher creates a new fetcher with a default HTTP client and 1 hour cache TTL.
+
+
+### func \(\*DefaultJWKSFetcher\) [Fetch]()
+
+```go
+func (f *DefaultJWKSFetcher) Fetch(ctx context.Context, url string) (*jose.JSONWebKeySet, error)
+```
+
+Fetch retrieves the JWKS from the specified URL, using cache if available.
+
+
+### func \(\*DefaultJWKSFetcher\) [FlushCache]()
+
+```go
+func (f *DefaultJWKSFetcher) FlushCache()
+```
+
+FlushCache clears all cached JWKS entries.
+
+
+### func \(\*DefaultJWKSFetcher\) [SetTTL]()
+
+```go
+func (f *DefaultJWKSFetcher) SetTTL(ttl time.Duration)
+```
+
+SetTTL configures the cache time\-to\-live.
+
+
+## type [JWKSFetcher]()
+
+JWKSFetcher handles fetching and caching of JSON Web Key Sets.
+
+```go
+type JWKSFetcher interface {
+ Fetch(ctx context.Context, url string) (*jose.JSONWebKeySet, error)
+}
+```
+
+
+## type [SignatureResult]()
+
+SignatureResult holds the details of a single signature verification.
+
+```go
+type SignatureResult struct {
+ Index int
+ Valid bool
+ Algorithm string
+ KeyID string
+ Issuer string
+ JWKSUri string
+ Error string
+}
+```
+
+
+## type [SignatureVerificationResult]()
+
+SignatureVerificationResult contains the result of verifying all signatures.
+
+```go
+type SignatureVerificationResult struct {
+ Valid bool
+ Signatures []SignatureResult
+ Summary VerificationSummary
+}
+```
+
+
+## type [VerificationSummary]()
+
+VerificationSummary summarizes the results of all signature verifications.
+
+```go
+type VerificationSummary struct {
+ Total int
+ Valid int
+ Failed int
+ Errors []string
+}
+```
+
+
+## type [Verifier]()
+
+Verifier handles Agent Card signature verification.
+
+```go
+type Verifier struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewVerifier]()
+
+```go
+func NewVerifier() *Verifier
+```
+
+NewVerifier creates a new Verifier with the default JWKS fetcher.
+
+
+### func [NewVerifierWithFetcher]()
+
+```go
+func NewVerifierWithFetcher(fetcher JWKSFetcher) *Verifier
+```
+
+NewVerifierWithFetcher creates a new Verifier with a custom JWKS fetcher.
+
+
+### func \(\*Verifier\) [VerifyAgentCardSignatures]()
+
+```go
+func (v *Verifier) VerifyAgentCardSignatures(ctx context.Context, card *agentcard.AgentCard) (*SignatureVerificationResult, error)
+```
+
+VerifyAgentCardSignatures verifies all signatures in an Agent Card.
+
+# did
+
+```go
+import "github.com/capiscio/capiscio-core/v2/pkg/did"
+```
+
+Package did provides utilities for parsing and working with DID identifiers. Supports did:web \(RFC\-002 §6.1\) and did:key \(RFC\-002 §6.6\) methods. See RFC\-002: Trust Badge Specification v1.1.
+
+## Index
+
+- [Constants](<#constants>)
+- [Variables](<#variables>)
+- [func NewAgentDID\(domain, agentID string\) string](<#NewAgentDID>)
+- [func NewCapiscIOAgentDID\(agentID string\) string](<#NewCapiscIOAgentDID>)
+- [func NewKeyDID\(publicKey \[\]byte\) string](<#NewKeyDID>)
+- [func PublicKeyFromKeyDID\(didStr string\) \(ed25519.PublicKey, error\)](<#PublicKeyFromKeyDID>)
+- [type DID](<#DID>)
+ - [func Parse\(did string\) \(\*DID, error\)](<#Parse>)
+ - [func \(d \*DID\) DocumentURL\(\) string](<#DID.DocumentURL>)
+ - [func \(d \*DID\) GetPublicKey\(\) ed25519.PublicKey](<#DID.GetPublicKey>)
+ - [func \(d \*DID\) IsAgentDID\(\) bool](<#DID.IsAgentDID>)
+ - [func \(d \*DID\) IsKeyDID\(\) bool](<#DID.IsKeyDID>)
+ - [func \(d \*DID\) IsWebDID\(\) bool](<#DID.IsWebDID>)
+ - [func \(d \*DID\) String\(\) string](<#DID.String>)
+
+
+## Constants
+
+Multicodec constants for did:key
+
+```go
+const (
+ // Ed25519MulticodecPrefix is the multicodec prefix for Ed25519 public keys (0xed01)
+ Ed25519MulticodecPrefix = 0xed01
+
+ // Ed25519PublicKeySize is the size of an Ed25519 public key in bytes
+ Ed25519PublicKeySize = 32
+)
+```
+
+DefaultDomain is the default domain for CapiscIO\-hosted agents.
+
+```go
+const DefaultDomain = "registry.capisc.io"
+```
+
+## Variables
+
+Common errors returned by this package.
+
+```go
+var (
+ ErrInvalidDID = errors.New("invalid DID format")
+ ErrUnsupportedMethod = errors.New("unsupported DID method (only did:web and did:key supported)")
+ ErrMissingAgentID = errors.New("missing agent ID in DID")
+ ErrInvalidKeyDID = errors.New("invalid did:key format")
+ ErrUnsupportedKeyType = errors.New("unsupported key type in did:key (only Ed25519 supported)")
+)
+```
+
+
+## func [NewAgentDID]()
+
+```go
+func NewAgentDID(domain, agentID string) string
+```
+
+NewAgentDID constructs a did:web identifier for an agent.
+
+Parameters:
+
+- domain: The domain hosting the agent \(e.g., "registry.capisc.io"\)
+- agentID: The unique agent identifier \(e.g., "my\-agent\-001"\)
+
+Returns: did:web:\:agents:\
+
+
+## func [NewCapiscIOAgentDID]()
+
+```go
+func NewCapiscIOAgentDID(agentID string) string
+```
+
+NewCapiscIOAgentDID constructs a did:web for an agent on the CapiscIO registry. Shorthand for NewAgentDID\(DefaultDomain, agentID\).
+
+
+## func [NewKeyDID]()
+
+```go
+func NewKeyDID(publicKey []byte) string
+```
+
+NewKeyDID constructs a did:key identifier from an Ed25519 public key. Format: did:key:z\
+
+Parameters:
+
+- publicKey: Ed25519 public key \(32 bytes\)
+
+Returns: did:key:z6Mk... formatted DID string
+
+
+## func [PublicKeyFromKeyDID]()
+
+```go
+func PublicKeyFromKeyDID(didStr string) (ed25519.PublicKey, error)
+```
+
+PublicKeyFromKeyDID extracts the Ed25519 public key from a did:key identifier. Returns the 32\-byte public key or an error if the DID is invalid.
+
+
+## type [DID]()
+
+DID represents a parsed DID identifier. Supports both did:web and did:key methods.
+
+For did:web: did:web:\:agents:\ For did:key: did:key:z\
+
+```go
+type DID struct {
+ // Method is the DID method ("web" or "key").
+ Method string
+
+ // Domain is the domain hosting the DID Document (did:web only).
+ Domain string
+
+ // Path segments after the domain (did:web only, e.g., ["agents", "my-agent-001"]).
+ PathSegments []string
+
+ // AgentID is the agent identifier (did:web only, extracted from path).
+ AgentID string
+
+ // PublicKey is the Ed25519 public key (did:key only, 32 bytes).
+ PublicKey []byte
+
+ // Raw is the original DID string.
+ Raw string
+}
+```
+
+
+### func [Parse]()
+
+```go
+func Parse(did string) (*DID, error)
+```
+
+Parse parses a DID identifier into its components. Supports both did:web and did:key methods.
+
+Returns ErrInvalidDID if the format is invalid. Returns ErrUnsupportedMethod if the method is not "web" or "key".
+
+Examples:
+
+- did:web:registry.capisc.io:agents:my\-agent\-001
+- did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK
+
+
+### func \(\*DID\) [DocumentURL]()
+
+```go
+func (d *DID) DocumentURL() string
+```
+
+DocumentURL returns the HTTPS URL for the DID Document per did:web spec. did:web:registry.capisc.io:agents:my\-agent\-001
+
+```
+→ https://registry.capisc.io/agents/my-agent-001/did.json
+```
+
+Returns empty string for did:key \(no remote document\). Uses HTTP when the hostname is "localhost" or "127.0.0.1", HTTPS otherwise.
+
+
+### func \(\*DID\) [GetPublicKey]()
+
+```go
+func (d *DID) GetPublicKey() ed25519.PublicKey
+```
+
+GetPublicKey returns the Ed25519 public key for did:key identifiers. Returns nil for did:web identifiers.
+
+
+### func \(\*DID\) [IsAgentDID]()
+
+```go
+func (d *DID) IsAgentDID() bool
+```
+
+IsAgentDID returns true if the DID follows the CapiscIO agent DID pattern. Pattern: did:web:\:agents:\
+
+
+### func \(\*DID\) [IsKeyDID]()
+
+```go
+func (d *DID) IsKeyDID() bool
+```
+
+IsKeyDID returns true if this is a did:key identifier.
+
+
+### func \(\*DID\) [IsWebDID]()
+
+```go
+func (d *DID) IsWebDID() bool
+```
+
+IsWebDID returns true if this is a did:web identifier.
+
+
+### func \(\*DID\) [String]()
+
+```go
+func (d *DID) String() string
+```
+
+String returns the canonical DID string.
+
+# gateway
+
+```go
+import "github.com/capiscio/capiscio-core/v2/pkg/gateway"
+```
+
+Package gateway provides the HTTP middleware for the CapiscIO Security Sidecar.
+
+## Index
+
+- [func ExtractBadge\(r \*http.Request\) string](<#ExtractBadge>)
+- [func NewAuthMiddleware\(verifier \*badge.Verifier, next http.Handler\) http.Handler](<#NewAuthMiddleware>)
+- [func NewPolicyMiddleware\(verifier \*badge.Verifier, config PEPConfig, next http.Handler, callbacks ...PolicyEventCallback\) http.Handler](<#NewPolicyMiddleware>)
+- [type PEPConfig](<#PEPConfig>)
+- [type PolicyEvent](<#PolicyEvent>)
+- [type PolicyEventCallback](<#PolicyEventCallback>)
+
+
+
+## func [ExtractBadge]()
+
+```go
+func ExtractBadge(r *http.Request) string
+```
+
+ExtractBadge retrieves the badge from headers.
+
+
+## func [NewAuthMiddleware]()
+
+```go
+func NewAuthMiddleware(verifier *badge.Verifier, next http.Handler) http.Handler
+```
+
+NewAuthMiddleware creates a middleware that enforces Badge validity. Deprecated: Use NewPolicyMiddleware for RFC\-005 PDP integration.
+
+
+## func [NewPolicyMiddleware]()
+
+```go
+func NewPolicyMiddleware(verifier *badge.Verifier, config PEPConfig, next http.Handler, callbacks ...PolicyEventCallback) http.Handler
+```
+
+NewPolicyMiddleware creates a full PEP middleware \(RFC\-005\). When PEPConfig.PDPClient is nil, operates in badge\-only mode \(identical to NewAuthMiddleware\).
+
+
+## type [PEPConfig]()
+
+PEPConfig configures the Policy Enforcement Point middleware \(RFC\-005\).
+
+```go
+type PEPConfig struct {
+ PDPClient pip.PDPClient // nil = badge-only mode (skip PDP)
+ EnforcementMode pip.EnforcementMode // default EMObserve
+ ObligationReg *pip.ObligationRegistry // nil = no obligation handling
+ DecisionCache pip.DecisionCache // nil = no caching
+ BreakGlassKey crypto.PublicKey // nil = break-glass disabled
+ PEPID string // PEP instance identifier
+ Workspace string // workspace/tenant identifier
+ Logger *slog.Logger // nil = slog.Default()
+}
+```
+
+
+## type [PolicyEvent]()
+
+PolicyEvent captures telemetry for a policy enforcement decision.
+
+```go
+type PolicyEvent struct {
+ Decision string
+ DecisionID string
+ Override bool
+ OverrideJTI string
+ CacheHit bool
+ PDPLatencyMs int64
+ Obligations []string
+ ErrorCode string
+}
+```
+
+
+## type [PolicyEventCallback]()
+
+PolicyEventCallback is invoked synchronously after each policy enforcement with the event data. Implementations MUST return quickly and avoid long\-running or blocking operations.
+
+```go
+type PolicyEventCallback func(event PolicyEvent, req *pip.DecisionRequest)
+```
+
+# mcp
+
+```go
+import "github.com/capiscio/capiscio-core/v2/pkg/mcp"
+```
+
+Package mcp implements MCP security services for tool authority \(RFC\-006\) and server identity verification \(RFC\-007\).
+
+This package provides:
+
+- Tool access evaluation with trust badge verification
+- Evidence emission for audit trails
+- Server identity verification with did:web origin binding
+
+Usage as library:
+
+```
+import "github.com/capiscio/capiscio-core/pkg/mcp"
+
+service := mcp.NewService(mcp.Dependencies{...})
+result, err := service.EvaluateToolAccess(ctx, req)
+```
+
+The package also provides gRPC service handlers that can be registered with a gRPC server:
+
+```
+pb.RegisterMCPServiceServer(grpcServer, service)
+```
+
+Package mcp provides evidence storage implementations for RFC\-006.
+
+## Index
+
+- [Constants](<#constants>)
+- [Variables](<#variables>)
+- [func CheckVersionCompatibility\(clientVersion string\) \(bool, string\)](<#CheckVersionCompatibility>)
+- [func CreatePoPRequest\(\) \(\*pop.MCPPoPRequest, error\)](<#CreatePoPRequest>)
+- [func CreatePoPResponse\(clientNonce string, privateKey ed25519.PrivateKey, keyID string\) \(\*pop.MCPPoPResponse, error\)](<#CreatePoPResponse>)
+- [func ParsePoPFromMeta\(meta map\[string\]interface\{\}\) \(\*pop.MCPPoPRequest, \*pop.MCPPoPResponse\)](<#ParsePoPFromMeta>)
+- [type AuthLevel](<#AuthLevel>)
+ - [func \(a AuthLevel\) String\(\) string](<#AuthLevel.String>)
+- [type CallerCredential](<#CallerCredential>)
+ - [func NewAPIKeyCredential\(apiKey string\) CallerCredential](<#NewAPIKeyCredential>)
+ - [func NewAnonymousCredential\(\) CallerCredential](<#NewAnonymousCredential>)
+ - [func NewBadgeCredential\(badgeJWS string\) CallerCredential](<#NewBadgeCredential>)
+ - [func \(c CallerCredential\) GetAuthLevel\(\) AuthLevel](<#CallerCredential.GetAuthLevel>)
+- [type Decision](<#Decision>)
+ - [func \(d Decision\) String\(\) string](<#Decision.String>)
+- [type DenyReason](<#DenyReason>)
+ - [func ErrorToDenyReason\(err error\) DenyReason](<#ErrorToDenyReason>)
+ - [func \(r DenyReason\) String\(\) string](<#DenyReason.String>)
+- [type Dependencies](<#Dependencies>)
+- [type EvaluateConfig](<#EvaluateConfig>)
+- [type EvaluateResult](<#EvaluateResult>)
+- [type EvaluateToolAccessInput](<#EvaluateToolAccessInput>)
+- [type EvidenceRateLimiter](<#EvidenceRateLimiter>)
+ - [func NewEvidenceRateLimiter\(window time.Duration, maxPerWindow int\) \*EvidenceRateLimiter](<#NewEvidenceRateLimiter>)
+ - [func \(r \*EvidenceRateLimiter\) IsRateLimited\(record EvidenceRecord\) bool](<#EvidenceRateLimiter.IsRateLimited>)
+- [type EvidenceRecord](<#EvidenceRecord>)
+- [type EvidenceStore](<#EvidenceStore>)
+- [type EvidenceStoreMode](<#EvidenceStoreMode>)
+- [type Guard](<#Guard>)
+ - [func NewGuard\(badgeVerifier \*badge.Verifier, evidenceStore EvidenceStore, opts ...GuardOption\) \*Guard](<#NewGuard>)
+ - [func \(g \*Guard\) EvaluateToolAccess\(ctx context.Context, toolName string, paramsHash string, serverOrigin string, credential CallerCredential, config \*EvaluateConfig\) \(\*EvaluateResult, error\)](<#Guard.EvaluateToolAccess>)
+- [type GuardOption](<#GuardOption>)
+ - [func WithEnforcementMode\(mode pip.EnforcementMode\) GuardOption](<#WithEnforcementMode>)
+ - [func WithGuardLogger\(logger \*slog.Logger\) GuardOption](<#WithGuardLogger>)
+ - [func WithObligationRegistry\(reg \*pip.ObligationRegistry\) GuardOption](<#WithObligationRegistry>)
+ - [func WithPDPClient\(client pip.PDPClient\) GuardOption](<#WithPDPClient>)
+- [type HealthInput](<#HealthInput>)
+- [type HealthStatus](<#HealthStatus>)
+ - [func CheckHealth\(\) \*HealthStatus](<#CheckHealth>)
+- [type HybridEvidenceStore](<#HybridEvidenceStore>)
+ - [func NewHybridEvidenceStore\(localDir string, registryCfg RegistryEvidenceStoreConfig\) \(\*HybridEvidenceStore, error\)](<#NewHybridEvidenceStore>)
+ - [func \(s \*HybridEvidenceStore\) Close\(\) error](<#HybridEvidenceStore.Close>)
+ - [func \(s \*HybridEvidenceStore\) Store\(ctx context.Context, record EvidenceRecord\) error](<#HybridEvidenceStore.Store>)
+- [type LocalEvidenceStore](<#LocalEvidenceStore>)
+ - [func NewLocalEvidenceStore\(dir string\) \(\*LocalEvidenceStore, error\)](<#NewLocalEvidenceStore>)
+ - [func \(s \*LocalEvidenceStore\) Close\(\) error](<#LocalEvidenceStore.Close>)
+ - [func \(s \*LocalEvidenceStore\) Store\(ctx context.Context, record EvidenceRecord\) error](<#LocalEvidenceStore.Store>)
+- [type NoOpEvidenceStore](<#NoOpEvidenceStore>)
+ - [func \(n \*NoOpEvidenceStore\) Store\(ctx context.Context, record EvidenceRecord\) error](<#NoOpEvidenceStore.Store>)
+- [type ParsedIdentity](<#ParsedIdentity>)
+ - [func ParseHTTPHeaders\(headers map\[string\]string\) \*ParsedIdentity](<#ParseHTTPHeaders>)
+ - [func ParseJSONRPCMeta\(meta map\[string\]interface\{\}\) \*ParsedIdentity](<#ParseJSONRPCMeta>)
+- [type RegistryEvidenceStore](<#RegistryEvidenceStore>)
+ - [func NewRegistryEvidenceStore\(cfg RegistryEvidenceStoreConfig\) \*RegistryEvidenceStore](<#NewRegistryEvidenceStore>)
+ - [func \(s \*RegistryEvidenceStore\) Close\(\) error](<#RegistryEvidenceStore.Close>)
+ - [func \(s \*RegistryEvidenceStore\) Store\(ctx context.Context, record EvidenceRecord\) error](<#RegistryEvidenceStore.Store>)
+- [type RegistryEvidenceStoreConfig](<#RegistryEvidenceStoreConfig>)
+- [type ServerErrorCode](<#ServerErrorCode>)
+ - [func ErrorToServerErrorCode\(err error\) ServerErrorCode](<#ErrorToServerErrorCode>)
+ - [func \(c ServerErrorCode\) String\(\) string](<#ServerErrorCode.String>)
+- [type ServerIdentityVerifier](<#ServerIdentityVerifier>)
+ - [func NewServerIdentityVerifier\(badgeVerifier \*badge.Verifier\) \*ServerIdentityVerifier](<#NewServerIdentityVerifier>)
+ - [func NewServerIdentityVerifierWithConfig\(badgeVerifier \*badge.Verifier, cacheConfig \*pop.CacheConfig\) \*ServerIdentityVerifier](<#NewServerIdentityVerifierWithConfig>)
+ - [func \(v \*ServerIdentityVerifier\) GetCachedSession\(serverDID string\) \(\*pop.CacheEntry, bool\)](<#ServerIdentityVerifier.GetCachedSession>)
+ - [func \(v \*ServerIdentityVerifier\) InvalidateByTrustLevel\(minLevelStr string\)](<#ServerIdentityVerifier.InvalidateByTrustLevel>)
+ - [func \(v \*ServerIdentityVerifier\) InvalidateSession\(serverDID string\)](<#ServerIdentityVerifier.InvalidateSession>)
+ - [func \(v \*ServerIdentityVerifier\) VerifyPoP\(ctx context.Context, result \*VerifyResult, popRequest \*pop.MCPPoPRequest, popResponse \*pop.MCPPoPResponse, publicKey ed25519.PublicKey, maxAge time.Duration\) \(\*VerifyResult, error\)](<#ServerIdentityVerifier.VerifyPoP>)
+ - [func \(v \*ServerIdentityVerifier\) VerifyServerIdentity\(ctx context.Context, serverDID string, serverBadgeJWS string, transportOrigin string, config \*VerifyConfig\) \(\*VerifyResult, error\)](<#ServerIdentityVerifier.VerifyServerIdentity>)
+ - [func \(v \*ServerIdentityVerifier\) VerifyWithCache\(ctx context.Context, serverDID string, serverBadgeJWS string, transportOrigin string, popRequest \*pop.MCPPoPRequest, popResponse \*pop.MCPPoPResponse, publicKey ed25519.PublicKey, config \*VerifyConfig\) \(\*VerifyResult, error\)](<#ServerIdentityVerifier.VerifyWithCache>)
+- [type ServerState](<#ServerState>)
+ - [func \(s ServerState\) String\(\) string](<#ServerState.String>)
+- [type Service](<#Service>)
+ - [func NewService\(deps \*Dependencies\) \*Service](<#NewService>)
+ - [func \(s \*Service\) EvaluateToolAccess\(ctx context.Context, input \*EvaluateToolAccessInput\) \(\*EvaluateResult, error\)](<#Service.EvaluateToolAccess>)
+ - [func \(s \*Service\) Health\(ctx context.Context, input \*HealthInput\) \*HealthStatus](<#Service.Health>)
+ - [func \(s \*Service\) ParseServerIdentityFromHTTP\(headers map\[string\]string\) \*ParsedIdentity](<#Service.ParseServerIdentityFromHTTP>)
+ - [func \(s \*Service\) ParseServerIdentityFromJSONRPC\(meta map\[string\]interface\{\}\) \*ParsedIdentity](<#Service.ParseServerIdentityFromJSONRPC>)
+ - [func \(s \*Service\) VerifyServerIdentity\(ctx context.Context, input \*VerifyServerIdentityInput\) \(\*VerifyResult, error\)](<#Service.VerifyServerIdentity>)
+- [type VerifyConfig](<#VerifyConfig>)
+ - [func DefaultVerifyConfig\(\) \*VerifyConfig](<#DefaultVerifyConfig>)
+- [type VerifyResult](<#VerifyResult>)
+ - [func \(r \*VerifyResult\) GetServerID\(\) string](<#VerifyResult.GetServerID>)
+ - [func \(r \*VerifyResult\) HasIdentity\(\) bool](<#VerifyResult.HasIdentity>)
+ - [func \(r \*VerifyResult\) IsDeclared\(\) bool](<#VerifyResult.IsDeclared>)
+ - [func \(r \*VerifyResult\) IsVerified\(\) bool](<#VerifyResult.IsVerified>)
+ - [func \(r \*VerifyResult\) TrustLevel\(\) int](<#VerifyResult.TrustLevel>)
+- [type VerifyServerIdentityInput](<#VerifyServerIdentityInput>)
+
+
+## Constants
+
+
+
+```go
+const (
+ // CoreVersion is the capiscio-core version
+ CoreVersion = "2.5.0"
+
+ // ProtoVersion is the MCP proto schema version
+ ProtoVersion = "1.0"
+
+ // MinMCPVersion is the minimum compatible MCP SDK version (capiscio-mcp)
+ // The MCP SDK has independent versioning starting from 0.1.0
+ MinMCPVersion = "0.1.0"
+
+ // MinVersion is the minimum compatible client SDK version (legacy capiscio-sdk)
+ MinVersion = "2.5.0"
+
+ // MaxVersionConstraint is the constraint for maximum compatible version
+ MaxVersionConstraint = "< 3.0.0"
+)
+```
+
+## Variables
+
+Error codes for MCP operations
+
+```go
+var (
+ // ErrBadgeMissing indicates a badge was required but not provided
+ ErrBadgeMissing = errors.New("badge required but not provided")
+
+ // ErrBadgeInvalid indicates the badge is malformed or unverifiable
+ ErrBadgeInvalid = errors.New("badge is invalid or malformed")
+
+ // ErrBadgeExpired indicates the badge has expired
+ ErrBadgeExpired = errors.New("badge has expired")
+
+ // ErrBadgeRevoked indicates the badge has been revoked
+ ErrBadgeRevoked = errors.New("badge has been revoked")
+
+ // ErrTrustInsufficient indicates the trust level is below minimum required
+ ErrTrustInsufficient = errors.New("trust level insufficient")
+
+ // ErrToolNotAllowed indicates the tool is not in the allowed list
+ ErrToolNotAllowed = errors.New("tool not allowed")
+
+ // ErrIssuerUntrusted indicates the badge issuer is not trusted
+ ErrIssuerUntrusted = errors.New("badge issuer not trusted")
+
+ // ErrPolicyDenied indicates policy evaluation failed
+ ErrPolicyDenied = errors.New("policy denied access")
+
+ // ErrDIDInvalid indicates the DID is malformed
+ ErrDIDInvalid = errors.New("DID is invalid")
+
+ // ErrDIDMismatch indicates the badge subject doesn't match disclosed DID
+ ErrDIDMismatch = errors.New("badge subject does not match disclosed DID")
+
+ // ErrOriginMismatch indicates the transport origin doesn't match did:web host
+ ErrOriginMismatch = errors.New("transport origin does not match DID host")
+
+ // ErrPathMismatch indicates the endpoint path doesn't match did:web path
+ ErrPathMismatch = errors.New("endpoint path does not match DID path")
+
+ // ErrAPIKeyInvalid indicates the API key is invalid
+ ErrAPIKeyInvalid = errors.New("API key is invalid")
+)
+```
+
+
+## func [CheckVersionCompatibility]()
+
+```go
+func CheckVersionCompatibility(clientVersion string) (bool, string)
+```
+
+CheckVersionCompatibility validates client/core version compatibility Returns true if the client version is compatible with this core version
+
+
+## func [CreatePoPRequest]()
+
+```go
+func CreatePoPRequest() (*pop.MCPPoPRequest, error)
+```
+
+CreatePoPRequest creates a PoP request for embedding in MCP initialize \_meta Clients should call this before initialize and include result in request
+
+
+## func [CreatePoPResponse]()
+
+```go
+func CreatePoPResponse(clientNonce string, privateKey ed25519.PrivateKey, keyID string) (*pop.MCPPoPResponse, error)
+```
+
+CreatePoPResponse creates a PoP response for embedding in MCP initialize response \_meta Servers should call this when receiving a PoP request and include result in response
+
+
+## func [ParsePoPFromMeta]()
+
+```go
+func ParsePoPFromMeta(meta map[string]interface{}) (*pop.MCPPoPRequest, *pop.MCPPoPResponse)
+```
+
+ParsePoPFromMeta extracts PoP request/response from \_meta Returns \(request, response\) where request is from client and response is from server
+
+
+## type [AuthLevel]()
+
+AuthLevel represents the authentication level of the caller
+
+```go
+type AuthLevel int
+```
+
+
+
+```go
+const (
+ AuthLevelUnspecified AuthLevel = iota
+ AuthLevelAnonymous
+ AuthLevelAPIKey
+ AuthLevelBadge
+)
+```
+
+
+### func \(AuthLevel\) [String]()
+
+```go
+func (a AuthLevel) String() string
+```
+
+String returns the string representation of the auth level
+
+
+## type [CallerCredential]()
+
+CallerCredential represents the caller's authentication credential
+
+```go
+type CallerCredential struct {
+ // BadgeJWS is the full badge JWT (if badge auth)
+ BadgeJWS string
+
+ // APIKey is the API key (if API key auth)
+ APIKey string
+
+ // IsAnonymous is true if no credential was provided
+ IsAnonymous bool
+}
+```
+
+
+### func [NewAPIKeyCredential]()
+
+```go
+func NewAPIKeyCredential(apiKey string) CallerCredential
+```
+
+NewAPIKeyCredential creates a credential from an API key
+
+
+### func [NewAnonymousCredential]()
+
+```go
+func NewAnonymousCredential() CallerCredential
+```
+
+NewAnonymousCredential creates an anonymous credential
+
+
+### func [NewBadgeCredential]()
+
+```go
+func NewBadgeCredential(badgeJWS string) CallerCredential
+```
+
+NewBadgeCredential creates a credential from a badge JWS
+
+
+### func \(CallerCredential\) [GetAuthLevel]()
+
+```go
+func (c CallerCredential) GetAuthLevel() AuthLevel
+```
+
+GetAuthLevel returns the authentication level for this credential
+
+
+## type [Decision]()
+
+Decision represents the access decision \(allow or deny\)
+
+```go
+type Decision int
+```
+
+
+
+```go
+const (
+ DecisionUnspecified Decision = iota
+ DecisionAllow
+ DecisionDeny
+)
+```
+
+
+### func \(Decision\) [String]()
+
+```go
+func (d Decision) String() string
+```
+
+String returns the string representation of the decision
+
+
+## type [DenyReason]()
+
+DenyReason represents the reason for access denial \(RFC\-006 §6.4\)
+
+```go
+type DenyReason int
+```
+
+
+
+```go
+const (
+ DenyReasonUnspecified DenyReason = iota
+ DenyReasonBadgeMissing
+ DenyReasonBadgeInvalid
+ DenyReasonBadgeExpired
+ DenyReasonBadgeRevoked
+ DenyReasonTrustInsufficient
+ DenyReasonToolNotAllowed
+ DenyReasonIssuerUntrusted
+ DenyReasonPolicyDenied
+)
+```
+
+
+### func [ErrorToDenyReason]()
+
+```go
+func ErrorToDenyReason(err error) DenyReason
+```
+
+ErrorToDenyReason converts an error to a DenyReason
+
+
+### func \(DenyReason\) [String]()
+
+```go
+func (r DenyReason) String() string
+```
+
+String returns the RFC\-006 §10 compliant error code string
+
+
+## type [Dependencies]()
+
+Dependencies holds the dependencies for the MCP service
+
+```go
+type Dependencies struct {
+ BadgeVerifier *badge.Verifier
+ EvidenceStore EvidenceStore
+}
+```
+
+
+## type [EvaluateConfig]()
+
+EvaluateConfig holds configuration for tool access evaluation
+
+```go
+type EvaluateConfig struct {
+ // TrustedIssuers is a list of trusted badge issuers
+ TrustedIssuers []string
+
+ // MinTrustLevel is the minimum required trust level (0-4)
+ MinTrustLevel int
+
+ // AcceptLevelZero allows self-signed did:key badges (Trust Level 0)
+ AcceptLevelZero bool
+
+ // AllowedTools is a list of allowed tool patterns (glob patterns)
+ AllowedTools []string
+
+ // PolicyVersion is the version of the policy being applied (RFC-006 §7.2)
+ PolicyVersion string
+}
+```
+
+
+## type [EvaluateResult]()
+
+EvaluateResult holds the result of tool access evaluation
+
+```go
+type EvaluateResult struct {
+ // Decision is the access decision (allow or deny)
+ Decision Decision
+
+ // DenyReason is the reason for denial (only set if Decision == DecisionDeny)
+ DenyReason DenyReason
+
+ // DenyDetail is a human-readable denial detail
+ DenyDetail string
+
+ // AgentDID is the extracted agent DID
+ AgentDID string
+
+ // BadgeJTI is the badge ID (if present)
+ BadgeJTI string
+
+ // AuthLevel is the authentication level
+ AuthLevel AuthLevel
+
+ // TrustLevel is the verified trust level (0-4)
+ TrustLevel int
+
+ // EvidenceJSON is the RFC-006 §7 compliant evidence JSON
+ EvidenceJSON string
+
+ // EvidenceID is the unique evidence record ID
+ EvidenceID string
+
+ // Timestamp is when the evaluation occurred
+ Timestamp time.Time
+
+ // PolicyDecisionID is the PDP decision ID (RFC-005, only set when PDP is configured)
+ PolicyDecisionID string
+
+ // PolicyDecision is the PDP decision string: ALLOW, DENY, or ALLOW_OBSERVE (RFC-005)
+ PolicyDecision string
+}
+```
+
+
+## type [EvaluateToolAccessInput]()
+
+EvaluateToolAccessInput represents the input for tool access evaluation
+
+```go
+type EvaluateToolAccessInput struct {
+ ToolName string
+ ParamsHash string
+ Origin string
+ Credential CallerCredential
+ Config *EvaluateConfig
+}
+```
+
+
+## type [EvidenceRateLimiter]()
+
+EvidenceRateLimiter prevents repetitive log flooding. It deduplicates evidence by fingerprint \(tool \+ agent \+ decision\).
+
+```go
+type EvidenceRateLimiter struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewEvidenceRateLimiter]()
+
+```go
+func NewEvidenceRateLimiter(window time.Duration, maxPerWindow int) *EvidenceRateLimiter
+```
+
+NewEvidenceRateLimiter creates a new rate limiter.
+
+
+### func \(\*EvidenceRateLimiter\) [IsRateLimited]()
+
+```go
+func (r *EvidenceRateLimiter) IsRateLimited(record EvidenceRecord) bool
+```
+
+IsRateLimited checks if an evidence record should be rate\-limited.
+
+
+## type [EvidenceRecord]()
+
+EvidenceRecord represents an RFC\-006 §7 compliant evidence record. Field names use dot notation per RFC\-006 §7.2 JSON schema.
+
+```go
+type EvidenceRecord struct {
+ // EventName MUST be "capiscio.tool_invocation" per RFC-006 §7.2
+ EventName string `json:"event.name"`
+
+ // AgentDID is the agent DID or equivalent principal
+ AgentDID string `json:"capiscio.agent.did"`
+
+ // BadgeJTI is the badge identifier, if present
+ BadgeJTI string `json:"capiscio.badge.jti,omitempty"`
+
+ // AuthLevel is "badge", "apikey", or "anonymous"
+ AuthLevel string `json:"capiscio.auth.level"`
+
+ // Target is the tool identifier
+ Target string `json:"capiscio.target"`
+
+ // PolicyVersion is the policy version used
+ PolicyVersion string `json:"capiscio.policy_version"`
+
+ // Decision is "ALLOW" or "DENY"
+ Decision string `json:"capiscio.decision"`
+
+ // ParamsHash is the SHA-256 hash of canonicalized tool parameters (optional)
+ ParamsHash string `json:"capiscio.tool.params_hash,omitempty"`
+
+ // DenyReason is the error code when decision is DENY (optional)
+ DenyReason string `json:"capiscio.deny_reason,omitempty"`
+
+ // Non-RFC fields for internal use
+ ID string `json:"id"`
+ Timestamp time.Time `json:"timestamp"`
+ TrustLevel int `json:"trust_level"`
+ ServerOrigin string `json:"server_origin,omitempty"`
+}
+```
+
+
+## type [EvidenceStore]()
+
+EvidenceStore is the interface for storing evidence records
+
+```go
+type EvidenceStore interface {
+ // Store saves an evidence record
+ Store(ctx context.Context, record EvidenceRecord) error
+}
+```
+
+
+## type [EvidenceStoreMode]()
+
+EvidenceStoreMode determines the storage backend
+
+```go
+type EvidenceStoreMode string
+```
+
+
+
+```go
+const (
+ // EvidenceStoreModeLocal stores evidence to local files
+ EvidenceStoreModeLocal EvidenceStoreMode = "local"
+
+ // EvidenceStoreModeRegistry streams evidence to registry server
+ EvidenceStoreModeRegistry EvidenceStoreMode = "registry"
+
+ // EvidenceStoreModeHybrid stores locally AND streams to registry
+ EvidenceStoreModeHybrid EvidenceStoreMode = "hybrid"
+)
+```
+
+
+## type [Guard]()
+
+Guard implements RFC\-006 tool access evaluation with atomic evidence emission.
+
+```go
+type Guard struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewGuard]()
+
+```go
+func NewGuard(badgeVerifier *badge.Verifier, evidenceStore EvidenceStore, opts ...GuardOption) *Guard
+```
+
+NewGuard creates a new Guard instance. Use GuardOption functions to configure PDP integration \(RFC\-005\).
+
+
+### func \(\*Guard\) [EvaluateToolAccess]()
+
+```go
+func (g *Guard) EvaluateToolAccess(ctx context.Context, toolName string, paramsHash string, serverOrigin string, credential CallerCredential, config *EvaluateConfig) (*EvaluateResult, error)
+```
+
+EvaluateToolAccess evaluates tool access and emits evidence atomically. This implements RFC\-006 §6.2\-6.4.
+
+When a PDPClient is configured \(via WithPDPClient\), the PDP is the authoritative decision source — inline policy \(trust level \+ allowed tools\) is skipped. When no PDPClient is configured, the inline policy is evaluated as before.
+
+Key design principle: Single operation returns both decision and evidence to avoid partial failures.
+
+
+## type [GuardOption]()
+
+GuardOption configures optional Guard behavior.
+
+```go
+type GuardOption func(*Guard)
+```
+
+
+### func [WithEnforcementMode]()
+
+```go
+func WithEnforcementMode(mode pip.EnforcementMode) GuardOption
+```
+
+WithEnforcementMode sets the enforcement mode.
+
+
+### func [WithGuardLogger]()
+
+```go
+func WithGuardLogger(logger *slog.Logger) GuardOption
+```
+
+WithGuardLogger sets the logger for the guard. A nil logger is treated as slog.Default\(\).
+
+
+### func [WithObligationRegistry]()
+
+```go
+func WithObligationRegistry(reg *pip.ObligationRegistry) GuardOption
+```
+
+WithObligationRegistry sets the obligation registry for PDP obligations.
+
+
+### func [WithPDPClient]()
+
+```go
+func WithPDPClient(client pip.PDPClient) GuardOption
+```
+
+WithPDPClient enables PDP\-based policy evaluation \(RFC\-005\). When set, the PDP replaces inline policy evaluation \(trust level \+ allowed tools\).
+
+
+## type [HealthInput]()
+
+HealthInput represents the input for health checks
+
+```go
+type HealthInput struct {
+ ClientVersion string
+}
+```
+
+
+## type [HealthStatus]()
+
+HealthStatus represents the health status of the MCP service
+
+```go
+type HealthStatus struct {
+ // Healthy indicates if the service is healthy
+ Healthy bool
+
+ // CoreVersion is the capiscio-core version
+ CoreVersion string
+
+ // ProtoVersion is the proto schema version
+ ProtoVersion string
+
+ // Compatible indicates if the client version is compatible
+ Compatible bool
+}
+```
+
+
+### func [CheckHealth]()
+
+```go
+func CheckHealth() *HealthStatus
+```
+
+CheckHealth performs a health check and returns the status
+
+
+## type [HybridEvidenceStore]()
+
+HybridEvidenceStore stores evidence both locally and to registry.
+
+```go
+type HybridEvidenceStore struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewHybridEvidenceStore]()
+
+```go
+func NewHybridEvidenceStore(localDir string, registryCfg RegistryEvidenceStoreConfig) (*HybridEvidenceStore, error)
+```
+
+NewHybridEvidenceStore creates a store that writes to both local and registry.
+
+
+### func \(\*HybridEvidenceStore\) [Close]()
+
+```go
+func (s *HybridEvidenceStore) Close() error
+```
+
+Close closes both stores.
+
+
+### func \(\*HybridEvidenceStore\) [Store]()
+
+```go
+func (s *HybridEvidenceStore) Store(ctx context.Context, record EvidenceRecord) error
+```
+
+Store writes to both local and registry stores.
+
+
+## type [LocalEvidenceStore]()
+
+LocalEvidenceStore stores evidence records to local JSON files. Each file is named by date \(YYYY\-MM\-DD.jsonl\) in JSONL format.
+
+```go
+type LocalEvidenceStore struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewLocalEvidenceStore]()
+
+```go
+func NewLocalEvidenceStore(dir string) (*LocalEvidenceStore, error)
+```
+
+NewLocalEvidenceStore creates a new local evidence store. If dir is empty, uses \~/.capiscio/evidence/
+
+
+### func \(\*LocalEvidenceStore\) [Close]()
+
+```go
+func (s *LocalEvidenceStore) Close() error
+```
+
+Close closes the local evidence store.
+
+
+### func \(\*LocalEvidenceStore\) [Store]()
+
+```go
+func (s *LocalEvidenceStore) Store(ctx context.Context, record EvidenceRecord) error
+```
+
+Store writes an evidence record to the local file.
+
+
+## type [NoOpEvidenceStore]()
+
+NoOpEvidenceStore is a no\-op evidence store for testing
+
+```go
+type NoOpEvidenceStore struct{}
+```
+
+
+### func \(\*NoOpEvidenceStore\) [Store]()
+
+```go
+func (n *NoOpEvidenceStore) Store(ctx context.Context, record EvidenceRecord) error
+```
+
+
+
+
+## type [ParsedIdentity]()
+
+ParsedIdentity holds parsed server identity information \(RFC\-007 §6\)
+
+```go
+type ParsedIdentity struct {
+ // ServerDID is the extracted server DID
+ ServerDID string
+
+ // ServerBadgeJWS is the extracted server Trust Badge (JWS)
+ ServerBadgeJWS string
+}
+```
+
+
+### func [ParseHTTPHeaders]()
+
+```go
+func ParseHTTPHeaders(headers map[string]string) *ParsedIdentity
+```
+
+ParseHTTPHeaders extracts server identity from HTTP headers \(RFC\-007 §6.1\) Standard headers: \- Capiscio\-Server\-DID: The server's DID \- Capiscio\-Server\-Badge: The server's Trust Badge \(JWS\)
+
+
+### func [ParseJSONRPCMeta]()
+
+```go
+func ParseJSONRPCMeta(meta map[string]interface{}) *ParsedIdentity
+```
+
+ParseJSONRPCMeta extracts server identity from JSON\-RPC \_meta object \(RFC\-007 §6.2\) Standard fields: \- capiscio\_server\_did: The server's DID \- capiscio\_server\_badge: The server's Trust Badge \(JWS\) \- capiscio\_pop\_nonce: Client's PoP challenge \(in request\) \- capiscio\_pop\_signature: Server's PoP response \(in response\)
+
+
+## type [RegistryEvidenceStore]()
+
+RegistryEvidenceStore streams evidence to the registry server's events endpoint. It implements batching and rate limiting to avoid overwhelming the server.
+
+```go
+type RegistryEvidenceStore struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewRegistryEvidenceStore]()
+
+```go
+func NewRegistryEvidenceStore(cfg RegistryEvidenceStoreConfig) *RegistryEvidenceStore
+```
+
+NewRegistryEvidenceStore creates a new registry streaming evidence store.
+
+
+### func \(\*RegistryEvidenceStore\) [Close]()
+
+```go
+func (s *RegistryEvidenceStore) Close() error
+```
+
+Close stops the registry evidence store.
+
+
+### func \(\*RegistryEvidenceStore\) [Store]()
+
+```go
+func (s *RegistryEvidenceStore) Store(ctx context.Context, record EvidenceRecord) error
+```
+
+Store adds an evidence record to the buffer for streaming.
+
+
+## type [RegistryEvidenceStoreConfig]()
+
+RegistryEvidenceStoreConfig configures the registry evidence store
+
+```go
+type RegistryEvidenceStoreConfig struct {
+ // Endpoint is the registry events endpoint URL
+ Endpoint string
+
+ // APIKey for authentication
+ APIKey string
+
+ // BatchSize is the number of records to batch before flushing (default: 100)
+ BatchSize int
+
+ // FlushInterval is the max time between flushes (default: 5s)
+ FlushInterval time.Duration
+
+ // RateLimitWindow is the deduplication window (default: 60s)
+ RateLimitWindow time.Duration
+
+ // RateLimitMaxPerWindow is max events per fingerprint per window (default: 10)
+ RateLimitMaxPerWindow int
+}
+```
+
+
+## type [ServerErrorCode]()
+
+ServerErrorCode represents server verification error codes \(RFC\-007 §8\) These codes align with RFC\-006 error conventions for consistency.
+
+```go
+type ServerErrorCode int
+```
+
+
+
+```go
+const (
+ ServerErrorNone ServerErrorCode = iota
+ // SERVER_IDENTITY_MISSING - No server identity disclosed (UNVERIFIED_ORIGIN)
+ ServerErrorCodeDIDMissing
+ // SERVER_BADGE_MISSING - DID disclosed but no badge (DECLARED_PRINCIPAL)
+ ServerErrorCodeBadgeMissing
+ // SERVER_BADGE_INVALID - Badge signature or expiry verification failed
+ ServerErrorCodeBadgeInvalid
+ // SERVER_BADGE_REVOKED - Server badge has been revoked
+ ServerErrorCodeBadgeRevoked
+ // SERVER_TRUST_INSUFFICIENT - Trust level below required min_trust_level
+ ServerErrorCodeTrustInsufficient
+ // SERVER_DID_MISMATCH - Badge subject does not match disclosed DID
+ ServerErrorCodeDIDMismatch
+ // SERVER_ISSUER_UNTRUSTED - Badge issuer not in trusted_issuers
+ ServerErrorCodeIssuerUntrusted
+ // SERVER_DOMAIN_MISMATCH - did:web host does not match transport origin
+ ServerErrorCodeOriginMismatch
+ // SERVER_PATH_MISMATCH - did:web path does not match MCP endpoint path
+ ServerErrorCodePathMismatch
+ // SERVER_DID_RESOLUTION_FAILED - Could not resolve DID document
+ ServerErrorCodeDIDResolutionFailed
+ // SERVER_POP_FAILED - Proof of Possession verification failed
+ ServerErrorCodePoPFailed
+ // SERVER_POP_EXPIRED - PoP challenge expired
+ ServerErrorCodePoPExpired
+ // SERVER_KEY_FETCH_FAILED - Could not fetch server public key
+ ServerErrorCodeKeyFetchFailed
+)
+```
+
+
+### func [ErrorToServerErrorCode]()
+
+```go
+func ErrorToServerErrorCode(err error) ServerErrorCode
+```
+
+ErrorToServerErrorCode converts an error to a ServerErrorCode
+
+
+### func \(ServerErrorCode\) [String]()
+
+```go
+func (c ServerErrorCode) String() string
+```
+
+String returns the string representation of the server error code These match the RFC\-007 §8 error code names
+
+
+## type [ServerIdentityVerifier]()
+
+ServerIdentityVerifier implements RFC\-007 server identity verification. It uses the same badge.Verifier as agent identity verification for consistency.
+
+Per RFC\-007 §3: A Server Badge is a Trust Badge \(RFC\-002\) issued for a server DID. This means MCP servers use the SAME identity infrastructure as agents: \- Same DID patterns \(did:web:domain:servers:id vs did:web:domain:agents:id\) \- Same Trust Badge format \- Same verification workflow via badge.Verifier
+
+The verification has two phases: 1. Badge verification: Verify the badge is valid and signed by trusted CA 2. PoP verification: Verify the server controls the DID's private key
+
+RFC\-007 PoP is embedded in the MCP handshake \(initialize\), NOT via CA endpoints: \- Client sends nonce in initialize request \_meta \- Server returns signature in initialize response \_meta \- No dependency on /badge/challenge endpoints
+
+```go
+type ServerIdentityVerifier struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewServerIdentityVerifier]()
+
+```go
+func NewServerIdentityVerifier(badgeVerifier *badge.Verifier) *ServerIdentityVerifier
+```
+
+NewServerIdentityVerifier creates a new server identity verifier. The badgeVerifier is the same verifier used for agent badges \- this ensures consistent identity verification across both agents and MCP servers.
+
+
+### func [NewServerIdentityVerifierWithConfig]()
+
+```go
+func NewServerIdentityVerifierWithConfig(badgeVerifier *badge.Verifier, cacheConfig *pop.CacheConfig) *ServerIdentityVerifier
+```
+
+NewServerIdentityVerifierWithConfig creates a verifier with custom cache config
+
+
+### func \(\*ServerIdentityVerifier\) [GetCachedSession]()
+
+```go
+func (v *ServerIdentityVerifier) GetCachedSession(serverDID string) (*pop.CacheEntry, bool)
+```
+
+GetCachedSession retrieves a previously verified session Use this to avoid re\-verifying on every request within a session
+
+
+### func \(\*ServerIdentityVerifier\) [InvalidateByTrustLevel]()
+
+```go
+func (v *ServerIdentityVerifier) InvalidateByTrustLevel(minLevelStr string)
+```
+
+InvalidateByTrustLevel removes all sessions below a trust level Use when trust requirements increase minLevelStr should be "0", "1", "2", "3", or "4" per RFC\-002 §5
+
+
+### func \(\*ServerIdentityVerifier\) [InvalidateSession]()
+
+```go
+func (v *ServerIdentityVerifier) InvalidateSession(serverDID string)
+```
+
+InvalidateSession removes a cached session \(e.g., on disconnect\)
+
+
+### func \(\*ServerIdentityVerifier\) [VerifyPoP]()
+
+```go
+func (v *ServerIdentityVerifier) VerifyPoP(ctx context.Context, result *VerifyResult, popRequest *pop.MCPPoPRequest, popResponse *pop.MCPPoPResponse, publicKey ed25519.PublicKey, maxAge time.Duration) (*VerifyResult, error)
+```
+
+VerifyPoP verifies a server's Proof of Possession response.
+
+This is called AFTER VerifyServerIdentity succeeds \(returns DECLARED\_PRINCIPAL\). The PoP data comes from the MCP initialize handshake: \- Client sent nonce in request \_meta \(capiscio\_pop\_nonce\) \- Server returned signature in response \_meta \(capiscio\_pop\_signature\)
+
+Returns updated result with VERIFIED\_PRINCIPAL if PoP succeeds.
+
+
+### func \(\*ServerIdentityVerifier\) [VerifyServerIdentity]()
+
+```go
+func (v *ServerIdentityVerifier) VerifyServerIdentity(ctx context.Context, serverDID string, serverBadgeJWS string, transportOrigin string, config *VerifyConfig) (*VerifyResult, error)
+```
+
+VerifyServerIdentity implements RFC\-007 §7.2 server identity verification algorithm.
+
+RFC\-007 defines Server Badges as Trust Badges where sub = server DID. This method verifies the server badge using the same badge.Verifier as agents.
+
+The algorithm classifies servers into THREE states: \- VERIFIED\_PRINCIPAL: DID \+ badge verified \+ PoP verified \(full trust\) \- DECLARED\_PRINCIPAL: DID \+ badge verified, PoP not performed \(partial trust\) \- UNVERIFIED\_ORIGIN: Missing DID, missing badge, or verification failed
+
+For VERIFIED\_PRINCIPAL, also call VerifyPoP with the PoP data from initialize.
+
+
+### func \(\*ServerIdentityVerifier\) [VerifyWithCache]()
+
+```go
+func (v *ServerIdentityVerifier) VerifyWithCache(ctx context.Context, serverDID string, serverBadgeJWS string, transportOrigin string, popRequest *pop.MCPPoPRequest, popResponse *pop.MCPPoPResponse, publicKey ed25519.PublicKey, config *VerifyConfig) (*VerifyResult, error)
+```
+
+VerifyWithCache checks cache first, then performs full verification if needed. This is the recommended entry point for verifying server identity.
+
+
+## type [ServerState]()
+
+ServerState represents the server classification state \(RFC\-007 §5.2\) Three distinct states reflect the verification depth: \- VERIFIED\_PRINCIPAL: Badge \+ PoP verified \(full trust\) \- DECLARED\_PRINCIPAL: Badge verified, PoP not performed \(partial trust\) \- UNVERIFIED\_ORIGIN: No identity disclosed or verification failed
+
+```go
+type ServerState int
+```
+
+
+
+```go
+const (
+ ServerStateUnspecified ServerState = iota
+ // ServerStateVerifiedPrincipal indicates full verification:
+ // - Server DID disclosed
+ // - Server badge verified by trusted CA
+ // - PoP verified (server proved key ownership)
+ ServerStateVerifiedPrincipal
+
+ // ServerStateDeclaredPrincipal indicates partial verification:
+ // - Server DID disclosed
+ // - Server badge verified by trusted CA
+ // - PoP NOT performed (key ownership not proven)
+ ServerStateDeclaredPrincipal
+
+ // ServerStateUnverifiedOrigin indicates no verification:
+ // - No DID disclosed, OR
+ // - No badge provided, OR
+ // - Badge verification failed
+ // Note: This is distinct from Trust Level 0 (self-signed did:key)
+ ServerStateUnverifiedOrigin
+)
+```
+
+
+### func \(ServerState\) [String]()
+
+```go
+func (s ServerState) String() string
+```
+
+String returns the string representation of the server state
+
+
+## type [Service]()
+
+Service implements the MCP service logic Note: gRPC integration requires running \`make proto\` first to generate pkg/rpc/gen/capiscio/v1/mcp.pb.go and mcp\_grpc.pb.go
+
+```go
+type Service struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewService]()
+
+```go
+func NewService(deps *Dependencies) *Service
+```
+
+NewService creates a new MCP service instance
+
+
+### func \(\*Service\) [EvaluateToolAccess]()
+
+```go
+func (s *Service) EvaluateToolAccess(ctx context.Context, input *EvaluateToolAccessInput) (*EvaluateResult, error)
+```
+
+EvaluateToolAccess evaluates tool access using RFC\-006 §6.2\-6.4
+
+
+### func \(\*Service\) [Health]()
+
+```go
+func (s *Service) Health(ctx context.Context, input *HealthInput) *HealthStatus
+```
+
+Health performs a health check
+
+
+### func \(\*Service\) [ParseServerIdentityFromHTTP]()
+
+```go
+func (s *Service) ParseServerIdentityFromHTTP(headers map[string]string) *ParsedIdentity
+```
+
+ParseServerIdentityFromHTTP parses server identity from HTTP headers
+
+
+### func \(\*Service\) [ParseServerIdentityFromJSONRPC]()
+
+```go
+func (s *Service) ParseServerIdentityFromJSONRPC(meta map[string]interface{}) *ParsedIdentity
+```
+
+ParseServerIdentityFromJSONRPC parses server identity from JSON\-RPC \_meta
+
+
+### func \(\*Service\) [VerifyServerIdentity]()
+
+```go
+func (s *Service) VerifyServerIdentity(ctx context.Context, input *VerifyServerIdentityInput) (*VerifyResult, error)
+```
+
+VerifyServerIdentity verifies server identity using RFC\-007 §7.2
+
+
+## type [VerifyConfig]()
+
+VerifyConfig holds configuration for server identity verification
+
+```go
+type VerifyConfig struct {
+ // AllowedDIDMethods is a list of allowed DID methods (e.g., "web", "key")
+ AllowedDIDMethods []string
+
+ // RequireOriginBinding enforces origin binding for did:web
+ RequireOriginBinding bool
+
+ // PoPMaxAge is the maximum age of a PoP nonce (default: 30 seconds)
+ PoPMaxAge time.Duration
+}
+```
+
+
+### func [DefaultVerifyConfig]()
+
+```go
+func DefaultVerifyConfig() *VerifyConfig
+```
+
+DefaultVerifyConfig returns the default verification configuration
+
+
+## type [VerifyResult]()
+
+VerifyResult holds the result of server identity verification
+
+```go
+type VerifyResult struct {
+ // State is the server classification state (RFC-007 §5.2)
+ // VERIFIED_PRINCIPAL, DECLARED_PRINCIPAL, or UNVERIFIED_ORIGIN
+ State ServerState
+
+ // ServerID is the confirmed server DID
+ ServerID string
+
+ // TrustLevelStr is the verified trust level from the server badge ("0"-"4")
+ // Per RFC-002 §5, trust levels are strings to avoid falsiness bugs
+ TrustLevelStr string
+
+ // BadgeJTI is the badge identifier for correlation
+ BadgeJTI string
+
+ // BadgeExpiresAt is when the server badge expires
+ BadgeExpiresAt time.Time
+
+ // PoPVerified is true if PoP verification succeeded
+ PoPVerified bool
+
+ // PoPRequired is true if PoP should be performed (badge valid, PoP not done)
+ PoPRequired bool
+
+ // ErrorCode is the error code (only set on failure)
+ ErrorCode ServerErrorCode
+
+ // ErrorDetail is a human-readable error detail
+ ErrorDetail string
+}
+```
+
+
+### func \(\*VerifyResult\) [GetServerID]()
+
+```go
+func (r *VerifyResult) GetServerID() string
+```
+
+GetServerID returns the server's DID
+
+
+### func \(\*VerifyResult\) [HasIdentity]()
+
+```go
+func (r *VerifyResult) HasIdentity() bool
+```
+
+HasIdentity returns true if any identity was verified \(not UNVERIFIED\_ORIGIN\)
+
+
+### func \(\*VerifyResult\) [IsDeclared]()
+
+```go
+func (r *VerifyResult) IsDeclared() bool
+```
+
+IsDeclared returns true if the server is partially verified \(DECLARED\_PRINCIPAL\)
+
+
+### func \(\*VerifyResult\) [IsVerified]()
+
+```go
+func (r *VerifyResult) IsVerified() bool
+```
+
+IsVerified returns true if the server is fully verified \(VERIFIED\_PRINCIPAL\)
+
+
+### func \(\*VerifyResult\) [TrustLevel]()
+
+```go
+func (r *VerifyResult) TrustLevel() int
+```
+
+TrustLevel returns the trust level as an int \(for convenience\) Returns 0 if the trust level string is empty or invalid
+
+
+## type [VerifyServerIdentityInput]()
+
+VerifyServerIdentityInput represents the input for server identity verification
+
+```go
+type VerifyServerIdentityInput struct {
+ ServerDID string
+ ServerBadgeJWS string
+ Origin string
+ Config *VerifyConfig
+}
+```
+
+# pip
+
+```go
+import "github.com/capiscio/capiscio-core/v2/pkg/pip"
+```
+
+## Index
+
+- [Constants](<#constants>)
+- [func CacheKeyComponents\(did, badgeJTI, operation, resourceID string, extra ...string\) string](<#CacheKeyComponents>)
+- [func ValidDecision\(d string\) bool](<#ValidDecision>)
+- [type ActionAttributes](<#ActionAttributes>)
+- [type BreakGlassScope](<#BreakGlassScope>)
+- [type BreakGlassToken](<#BreakGlassToken>)
+ - [func ParseBreakGlassJWS\(compact string, publicKey crypto.PublicKey\) \(\*BreakGlassToken, error\)](<#ParseBreakGlassJWS>)
+- [type BreakGlassValidator](<#BreakGlassValidator>)
+ - [func NewBreakGlassValidator\(publicKey crypto.PublicKey\) \*BreakGlassValidator](<#NewBreakGlassValidator>)
+ - [func \(v \*BreakGlassValidator\) MatchesScope\(token \*BreakGlassToken, method, route string\) bool](<#BreakGlassValidator.MatchesScope>)
+ - [func \(v \*BreakGlassValidator\) PublicKey\(\) crypto.PublicKey](<#BreakGlassValidator.PublicKey>)
+ - [func \(v \*BreakGlassValidator\) ValidateToken\(token \*BreakGlassToken\) error](<#BreakGlassValidator.ValidateToken>)
+- [type ContextAttributes](<#ContextAttributes>)
+- [type DecisionCache](<#DecisionCache>)
+- [type DecisionRequest](<#DecisionRequest>)
+- [type DecisionResponse](<#DecisionResponse>)
+- [type EnforcementMode](<#EnforcementMode>)
+ - [func EnforcementModeFromEnv\(\) \(EnforcementMode, error\)](<#EnforcementModeFromEnv>)
+ - [func ParseEnforcementMode\(s string\) \(EnforcementMode, error\)](<#ParseEnforcementMode>)
+ - [func \(em EnforcementMode\) StricterThan\(other EnforcementMode\) bool](<#EnforcementMode.StricterThan>)
+ - [func \(em EnforcementMode\) String\(\) string](<#EnforcementMode.String>)
+- [type EnvironmentAttrs](<#EnvironmentAttrs>)
+- [type HTTPPDPClient](<#HTTPPDPClient>)
+ - [func NewHTTPPDPClient\(endpoint string, timeout time.Duration, opts ...HTTPPDPClientOption\) \*HTTPPDPClient](<#NewHTTPPDPClient>)
+ - [func \(c \*HTTPPDPClient\) Evaluate\(ctx context.Context, req \*DecisionRequest\) \(\*DecisionResponse, error\)](<#HTTPPDPClient.Evaluate>)
+- [type HTTPPDPClientOption](<#HTTPPDPClientOption>)
+ - [func WithHTTPClient\(hc \*http.Client\) HTTPPDPClientOption](<#WithHTTPClient>)
+ - [func WithPEPID\(id string\) HTTPPDPClientOption](<#WithPEPID>)
+- [type InMemoryCache](<#InMemoryCache>)
+ - [func NewInMemoryCache\(opts ...InMemoryCacheOption\) \*InMemoryCache](<#NewInMemoryCache>)
+ - [func \(c \*InMemoryCache\) Get\(key string\) \(\*DecisionResponse, bool\)](<#InMemoryCache.Get>)
+ - [func \(c \*InMemoryCache\) Put\(key string, resp \*DecisionResponse, maxTTL time.Duration\)](<#InMemoryCache.Put>)
+- [type InMemoryCacheOption](<#InMemoryCacheOption>)
+ - [func WithCacheDeny\(enabled bool\) InMemoryCacheOption](<#WithCacheDeny>)
+- [type Obligation](<#Obligation>)
+- [type ObligationError](<#ObligationError>)
+- [type ObligationHandler](<#ObligationHandler>)
+- [type ObligationRegistry](<#ObligationRegistry>)
+ - [func NewObligationRegistry\(logger \*slog.Logger\) \*ObligationRegistry](<#NewObligationRegistry>)
+ - [func \(r \*ObligationRegistry\) Enforce\(ctx context.Context, mode EnforcementMode, obligations \[\]Obligation\) ObligationResult](<#ObligationRegistry.Enforce>)
+ - [func \(r \*ObligationRegistry\) Register\(handler ObligationHandler\)](<#ObligationRegistry.Register>)
+- [type ObligationResult](<#ObligationResult>)
+- [type PDPClient](<#PDPClient>)
+- [type ResourceAttributes](<#ResourceAttributes>)
+- [type SubjectAttributes](<#SubjectAttributes>)
+
+
+## Constants
+
+Policy telemetry field constants \(RFC\-005 §10\). These MUST be emitted on every policy enforcement event.
+
+```go
+const (
+ // TelemetryDecisionID is REQUIRED on every policy enforcement event.
+ TelemetryDecisionID = "capiscio.policy.decision_id"
+
+ // TelemetryDecision is REQUIRED on every policy enforcement event.
+ // Values: "ALLOW", "DENY", or "ALLOW_OBSERVE"
+ TelemetryDecision = "capiscio.policy.decision"
+
+ // TelemetryOverride indicates break-glass was used.
+ TelemetryOverride = "capiscio.policy.override"
+
+ // TelemetryOverrideJTI is the break-glass token JTI.
+ TelemetryOverrideJTI = "capiscio.policy.override_jti"
+
+ // TelemetryErrorCode is REQUIRED when PDP is unavailable.
+ TelemetryErrorCode = "capiscio.policy.error_code"
+
+ // PolicyEventName is the RECOMMENDED event name.
+ PolicyEventName = "capiscio.policy_enforced"
+
+ // ErrorCodePDPUnavailable indicates PDP could not be reached.
+ ErrorCodePDPUnavailable = "PDP_UNAVAILABLE"
+)
+```
+
+DecisionAllow and DecisionDeny are the only valid PDP response values. ALLOW\_OBSERVE is a PEP telemetry value \(§7.4\), NOT a PDP response.
+
+```go
+const (
+ DecisionAllow = "ALLOW"
+ DecisionDeny = "DENY"
+ DecisionObserve = "ALLOW_OBSERVE" // PEP-only: emitted when EM-OBSERVE falls back on PDP unavailability
+)
+```
+
+DefaultPDPTimeout is the recommended PDP query timeout.
+
+```go
+const DefaultPDPTimeout = 500 * time.Millisecond
+```
+
+PIPVersion is the protocol version identifier. PEPs MUST include this in every request. PEPs MUST reject responses from PDPs that do not recognize the version.
+
+```go
+const PIPVersion = "capiscio.pip.v1"
+```
+
+TxnIDHeader is the HTTP header for transaction ID propagation \(RFC\-004\).
+
+```go
+const TxnIDHeader = "X-Capiscio-Txn"
+```
+
+
+## func [CacheKeyComponents]()
+
+```go
+func CacheKeyComponents(did, badgeJTI, operation, resourceID string, extra ...string) string
+```
+
+CacheKeyComponents builds a deterministic cache key from PIP request fields. Key includes: subject.did \+ subject.badge\_jti \+ action.operation \+ resource.identifier \+ enforcement\_mode.
+
+
+## func [ValidDecision]()
+
+```go
+func ValidDecision(d string) bool
+```
+
+ValidDecision returns true if d is a valid PDP response decision value.
+
+
+## type [ActionAttributes]()
+
+ActionAttributes identify what is being attempted.
+
+```go
+type ActionAttributes struct {
+ CapabilityClass *string `json:"capability_class"` // null in badge-only mode
+ Operation string `json:"operation"` // tool name, HTTP method+route, etc.
+}
+```
+
+
+## type [BreakGlassScope]()
+
+BreakGlassScope defines what the override token permits.
+
+```go
+type BreakGlassScope struct {
+ Methods []string `json:"methods"` // supports "*"
+ Routes []string `json:"routes"` // supports "*" and prefix matching
+}
+```
+
+
+## type [BreakGlassToken]()
+
+BreakGlassToken represents a break\-glass override token \(RFC\-005 §9\). Break\-glass tokens bypass PDP authorization \(not authentication\).
+
+```go
+type BreakGlassToken struct {
+ JTI string `json:"jti"`
+ IAT int64 `json:"iat"`
+ EXP int64 `json:"exp"`
+ ISS string `json:"iss"` // root admin issuer, NOT an agent DID
+ SUB string `json:"sub"` // operator identity
+ Scope BreakGlassScope `json:"scope"`
+ Reason string `json:"reason"` // human-readable justification
+}
+```
+
+
+### func [ParseBreakGlassJWS]()
+
+```go
+func ParseBreakGlassJWS(compact string, publicKey crypto.PublicKey) (*BreakGlassToken, error)
+```
+
+ParseBreakGlassJWS verifies a compact JWS break\-glass token and extracts claims. The publicKey MUST be the dedicated break\-glass key, not the CA badge\-signing key.
+
+
+## type [BreakGlassValidator]()
+
+BreakGlassValidator validates break\-glass override tokens.
+
+```go
+type BreakGlassValidator struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewBreakGlassValidator]()
+
+```go
+func NewBreakGlassValidator(publicKey crypto.PublicKey) *BreakGlassValidator
+```
+
+NewBreakGlassValidator creates a new break\-glass validator. publicKey MUST be the dedicated break\-glass verification key, NOT the CA key used for badge signing.
+
+
+### func \(\*BreakGlassValidator\) [MatchesScope]()
+
+```go
+func (v *BreakGlassValidator) MatchesScope(token *BreakGlassToken, method, route string) bool
+```
+
+MatchesScope checks if the token's scope covers the given method and route. Scope matching rules \(§9.2\): \- "\*" matches everything \- Exact match wins \- Routes support prefix matching
+
+
+### func \(\*BreakGlassValidator\) [PublicKey]()
+
+```go
+func (v *BreakGlassValidator) PublicKey() crypto.PublicKey
+```
+
+PublicKey returns the configured break\-glass public key for external use.
+
+
+### func \(\*BreakGlassValidator\) [ValidateToken]()
+
+```go
+func (v *BreakGlassValidator) ValidateToken(token *BreakGlassToken) error
+```
+
+ValidateToken validates a break\-glass token's claims \(not signature — see note\).
+
+In production, the token would arrive as a signed JWS. Signature verification requires the go\-jose library which is already a dependency in pkg/badge. This method validates the claims after JWS verification has extracted them.
+
+
+## type [ContextAttributes]()
+
+ContextAttributes provide correlation and authority context.
+
+```go
+type ContextAttributes struct {
+ TxnID string `json:"txn_id"`
+ HopID *string `json:"hop_id"` // OPTIONAL
+ EnvelopeID *string `json:"envelope_id"` // null in badge-only
+ DelegationDepth *int `json:"delegation_depth"` // null in badge-only
+ Constraints json.RawMessage `json:"constraints"` // null in badge-only; see §3.1.9
+ ParentConstraints json.RawMessage `json:"parent_constraints"` // null in badge-only; see §3.1.9
+ EnforcementMode string `json:"enforcement_mode"` // PEP-level config
+}
+```
+
+
+## type [DecisionCache]()
+
+DecisionCache provides temporal\-bounded caching for PDP decisions. RFC\-005 §6.3: PEPs MUST NOT cache a decision beyond the earliest of: \- The ttl value from the PDP response \- The governing Envelope's expires\_at \(N/A in badge\-only mode\) \- The Badge's expiration \(exp claim\)
+
+```go
+type DecisionCache interface {
+ // Get retrieves a cached decision. Returns nil, false on miss or expiry.
+ Get(key string) (*DecisionResponse, bool)
+
+ // Put stores a decision with a maximum TTL.
+ // The cache MUST NOT serve this entry after maxTTL elapses.
+ Put(key string, resp *DecisionResponse, maxTTL time.Duration)
+}
+```
+
+
+## type [DecisionRequest]()
+
+DecisionRequest is the canonical PDP query \(RFC\-005 §5.1\).
+
+```go
+type DecisionRequest struct {
+ PIPVersion string `json:"pip_version"`
+ Subject SubjectAttributes `json:"subject"`
+ Action ActionAttributes `json:"action"`
+ Resource ResourceAttributes `json:"resource"`
+ Context ContextAttributes `json:"context"`
+ Environment EnvironmentAttrs `json:"environment"`
+}
+```
+
+
+## type [DecisionResponse]()
+
+DecisionResponse is the canonical PDP response \(RFC\-005 §6.1\).
+
+```go
+type DecisionResponse struct {
+ Decision string `json:"decision"` // "ALLOW" or "DENY"
+ DecisionID string `json:"decision_id"` // globally unique
+ Obligations []Obligation `json:"obligations"` // may be empty
+ Reason string `json:"reason,omitempty"` // human-readable
+ TTL *int `json:"ttl,omitempty"` // cache lifetime seconds
+}
+```
+
+
+## type [EnforcementMode]()
+
+EnforcementMode represents the PEP enforcement strictness level. RFC\-008 §10.5 defines the strict total order: EM\-OBSERVE \< EM\-GUARD \< EM\-DELEGATE \< EM\-STRICT.
+
+NOTE: The iota integer values are an implementation detail, not a stable API. Comparisons MUST use the enum constants \(EMObserve \< EMStrict\), never numeric literals.
+
+```go
+type EnforcementMode int
+```
+
+
+
+```go
+const (
+ EMObserve EnforcementMode = iota // log only, never block
+ EMGuard // block on verification failure, log PDP denials
+ EMDelegate // block on verification + PDP deny, best-effort obligations
+ EMStrict // block on everything including obligation failures
+)
+```
+
+
+### func [EnforcementModeFromEnv]()
+
+```go
+func EnforcementModeFromEnv() (EnforcementMode, error)
+```
+
+EnforcementModeFromEnv reads the enforcement mode from the environment variable. Returns EMObserve \(the safe default for rollout\) if the variable is not set. Returns an error if the variable is set but not a valid mode.
+
+
+### func [ParseEnforcementMode]()
+
+```go
+func ParseEnforcementMode(s string) (EnforcementMode, error)
+```
+
+ParseEnforcementMode parses an RFC enforcement mode string. Returns an error if the string is not a recognized mode.
+
+
+### func \(EnforcementMode\) [StricterThan]()
+
+```go
+func (em EnforcementMode) StricterThan(other EnforcementMode) bool
+```
+
+StricterThan returns true if em is stricter than other.
+
+
+### func \(EnforcementMode\) [String]()
+
+```go
+func (em EnforcementMode) String() string
+```
+
+String returns the RFC string representation of the enforcement mode.
+
+
+## type [EnvironmentAttrs]()
+
+EnvironmentAttrs provide PEP context.
+
+```go
+type EnvironmentAttrs struct {
+ Workspace *string `json:"workspace,omitempty"` // OPTIONAL
+ PEPID *string `json:"pep_id,omitempty"` // OPTIONAL
+ Time *string `json:"time,omitempty"` // RECOMMENDED, ISO 8601
+}
+```
+
+
+## type [HTTPPDPClient]()
+
+HTTPPDPClient is the reference implementation of PDPClient for any REST\-based PDP.
+
+```go
+type HTTPPDPClient struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewHTTPPDPClient]()
+
+```go
+func NewHTTPPDPClient(endpoint string, timeout time.Duration, opts ...HTTPPDPClientOption) *HTTPPDPClient
+```
+
+NewHTTPPDPClient creates an HTTP\-based PDP client. endpoint is the PDP evaluation URL. timeout controls the HTTP client timeout \(use DefaultPDPTimeout if unsure\). If timeout is \<= 0, DefaultPDPTimeout is used to prevent indefinite hangs.
+
+
+### func \(\*HTTPPDPClient\) [Evaluate]()
+
+```go
+func (c *HTTPPDPClient) Evaluate(ctx context.Context, req *DecisionRequest) (*DecisionResponse, error)
+```
+
+Evaluate sends a PIP decision request to the HTTP PDP and returns the response.
+
+
+## type [HTTPPDPClientOption]()
+
+HTTPPDPClientOption configures an HTTPPDPClient.
+
+```go
+type HTTPPDPClientOption func(*HTTPPDPClient)
+```
+
+
+### func [WithHTTPClient]()
+
+```go
+func WithHTTPClient(hc *http.Client) HTTPPDPClientOption
+```
+
+WithHTTPClient sets a custom HTTP client \(e.g., for custom TLS or timeouts\).
+
+
+### func [WithPEPID]()
+
+```go
+func WithPEPID(id string) HTTPPDPClientOption
+```
+
+WithPEPID sets the PEP identifier included in requests.
+
+
+## type [InMemoryCache]()
+
+InMemoryCache is a simple in\-memory DecisionCache. Suitable for single\-instance deployments. For multi\-instance, use a shared cache.
+
+```go
+type InMemoryCache struct {
+ // contains filtered or unexported fields
+}
+```
+
+
+### func [NewInMemoryCache]()
+
+```go
+func NewInMemoryCache(opts ...InMemoryCacheOption) *InMemoryCache
+```
+
+NewInMemoryCache creates a new in\-memory decision cache.
+
+
+### func \(\*InMemoryCache\) [Get]()
+
+```go
+func (c *InMemoryCache) Get(key string) (*DecisionResponse, bool)
+```
+
+Get retrieves a cached decision if it exists and has not expired. Expired entries are evicted on read to prevent unbounded memory growth.
+
+
+### func \(\*InMemoryCache\) [Put]()
+
+```go
+func (c *InMemoryCache) Put(key string, resp *DecisionResponse, maxTTL time.Duration)
+```
+
+Put stores a decision with a bounded TTL. Skips DENY decisions unless cacheDeny is enabled. Skips if maxTTL is zero or negative \(badge already expired\).
+
+
+## type [InMemoryCacheOption]()
+
+InMemoryCacheOption configures an InMemoryCache.
+
+```go
+type InMemoryCacheOption func(*InMemoryCache)
+```
+
+
+### func [WithCacheDeny]()
+
+```go
+func WithCacheDeny(enabled bool) InMemoryCacheOption
+```
+
+WithCacheDeny enables caching of DENY decisions. WARNING: Caching DENY can cause persistent blocks after PDP recovery \("deny storm"\).
+
+
+## type [Obligation]()
+
+Obligation is a conditional contract per RFC\-005 §7.1.
+
+```go
+type Obligation struct {
+ Type string `json:"type"`
+ Params json.RawMessage `json:"params"` // opaque JSON — PEP passes to handler without interpretation
+}
+```
+
+