@aporthq/aport-agent-guardrails 1.0.19 → 1.0.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -91,13 +91,13 @@ The security concern is that agent tools and skills can execute sensitive action
91
91
 
92
92
  **APort Agent Guardrail** adapters are available per framework; the same passport and policies apply. **Node users:** `npx @aporthq/aport-agent-guardrails` (then choose framework) or `npx @aporthq/aport-agent-guardrails <framework>`. **Python users (LangChain/CrewAI/DeerFlow):** run the same CLI for the wizard and config, then install the framework adapter/provider package shown in the framework doc.
93
93
 
94
- **Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the evaluator or framework callback in your app so each tool call is verified. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
94
+ **Two ways to use APort:** (1) **Guardrails (CLI/setup)** — run the installer to create your passport and config; (2) **Core (library)** — use the `OAPGuardrailProvider` ([docs/PROVIDER.md](docs/PROVIDER.md)) in your app so each tool call is verified. One provider per language (Python + TypeScript), works with any framework. Framework docs: [OpenClaw](docs/frameworks/openclaw.md), [Cursor](docs/frameworks/cursor.md), [Claude Code](docs/frameworks/claude-code.md), [LangChain](docs/frameworks/langchain.md), [CrewAI](docs/frameworks/crewai.md), [DeerFlow](docs/frameworks/deerflow.md), [n8n](docs/frameworks/n8n.md).
95
95
 
96
96
  **CLI-supported frameworks:** `openclaw`, `langchain`, `crewai`, `cursor`, `claude-code`, `deerflow`, `n8n`. OpenClaw/Cursor/Claude Code include runtime-specific integration scripts; DeerFlow/LangChain/CrewAI use framework docs plus generic setup output from the CLI. See [Deployment readiness](docs/DEPLOYMENT_READINESS.md).
97
97
 
98
98
  | Framework | Doc | Integration | Install |
99
99
  |-----------|-----|--------------|--------|
100
- | **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | `before_tool_call` plugin | `npx @aporthq/aport-agent-guardrails openclaw` |
100
+ | **OpenClaw** | [docs/frameworks/openclaw.md](docs/frameworks/openclaw.md) | Native `GuardrailProvider` or plugin | `npm i @aporthq/aport-agent-guardrails-core && npx @aporthq/aport-agent-guardrails openclaw` |
101
101
  | **Cursor** | [docs/frameworks/cursor.md](docs/frameworks/cursor.md) | `beforeShellExecution` / `preToolUse` hooks → writes `~/.cursor/hooks.json`. **Runtime enforcement is the bash hook;** the Node package `@aporthq/aport-agent-guardrails-cursor` is a helper only (Evaluator, `getHookPath()`). | `npx @aporthq/aport-agent-guardrails cursor` |
102
102
  | **Claude Code** | [docs/frameworks/claude-code.md](docs/frameworks/claude-code.md) | PreToolUse hook → writes `~/.claude/settings.json` (Claude Code format; not Cursor). | `npx @aporthq/aport-agent-guardrails claude-code` |
103
103
  | **LangChain / LangGraph** | [docs/frameworks/langchain.md](docs/frameworks/langchain.md) | **Python:** `APortCallback` (`on_tool_start`) | `npx @aporthq/aport-agent-guardrails langchain` then `pip install aport-agent-guardrails-langchain` + `aport-langchain setup` |
@@ -0,0 +1,90 @@
1
+ # OAPGuardrailProvider
2
+
3
+ APort ships a generic `OAPGuardrailProvider` for both Python and TypeScript. It implements whatever `GuardrailProvider` interface the target framework defines, wraps the core `Evaluator`, and works with any framework — no framework-specific code.
4
+
5
+ ## Architecture
6
+
7
+ ```
8
+ Framework defines interface APort implements it
9
+ ──────────────────────── ───────────────────
10
+ DeerFlow: GuardrailProvider ←── Python OAPGuardrailProvider
11
+ OpenClaw: GuardrailProvider ←── TypeScript OAPGuardrailProvider
12
+ Your framework: same shape ←── Same class, same language
13
+ ```
14
+
15
+ One provider per language. The core evaluation logic (tool mapping, passport loading, local/API mode, audit logging) is shared.
16
+
17
+ ## Python
18
+
19
+ **Package:** `aport-agent-guardrails` (pip/uv)
20
+
21
+ ```python
22
+ from aport_guardrails.providers import OAPGuardrailProvider
23
+
24
+ provider = OAPGuardrailProvider(framework="deerflow")
25
+ result = provider.evaluate(request) # sync
26
+ result = await provider.aevaluate(request) # async
27
+ ```
28
+
29
+ **Supported frameworks:** DeerFlow, any Python framework with a `GuardrailProvider` protocol.
30
+
31
+ **Config:** `~/.aport/<framework>/config.yaml` (created by `aport setup --framework <name>`)
32
+
33
+ **DeerFlow config.yaml:**
34
+ ```yaml
35
+ guardrails:
36
+ enabled: true
37
+ provider:
38
+ use: aport_guardrails.providers.generic:OAPGuardrailProvider
39
+ ```
40
+
41
+ ## TypeScript
42
+
43
+ **Package:** `@aporthq/aport-agent-guardrails-core` (npm)
44
+
45
+ ```typescript
46
+ import { OAPGuardrailProvider } from "@aporthq/aport-agent-guardrails-core";
47
+
48
+ const provider = new OAPGuardrailProvider({ framework: "openclaw" });
49
+ const decision = await provider.evaluate(request); // async
50
+ const decision = provider.evaluateSync(request); // sync
51
+ ```
52
+
53
+ **Supported frameworks:** OpenClaw, any TypeScript framework with a `GuardrailProvider` interface.
54
+
55
+ **Config:** `~/.openclaw/aport/config.yaml` or `~/.aport/<framework>/config.yaml`
56
+
57
+ **OpenClaw config.yaml:**
58
+ ```yaml
59
+ guardrails:
60
+ enabled: true
61
+ provider:
62
+ use: "@aporthq/aport-agent-guardrails-core"
63
+ config:
64
+ framework: "openclaw"
65
+ ```
66
+
67
+ ## What the provider does
68
+
69
+ 1. **Receives** `{ toolName, toolInput }` from the framework
70
+ 2. **Maps** tool name → OAP policy pack ID (e.g. `exec` → `system.command.execute.v1`)
71
+ 3. **Loads** passport from local file or hosted agent ID
72
+ 4. **Evaluates** locally (bash script, zero network) or via API (hosted evaluator)
73
+ 5. **Returns** `{ allow, reasons, policyId }` to the framework
74
+
75
+ ## Evaluation modes
76
+
77
+ | Mode | Network | Latency | Use case |
78
+ |---|---|---|---|
79
+ | **Local** (default) | None | ~300ms | Development, CI, air-gapped |
80
+ | **API** | Yes | ~65ms | Production, signed decisions, centralized dashboards |
81
+
82
+ ## Adding a new framework
83
+
84
+ 1. Check if the framework has a `GuardrailProvider` interface (or equivalent hook)
85
+ 2. Use the existing Python or TypeScript `OAPGuardrailProvider` — it's framework-agnostic
86
+ 3. Point the framework config at the provider class/package
87
+ 4. Run `npx @aporthq/aport-agent-guardrails <framework>` to create a passport
88
+ 5. Add a doc at `docs/frameworks/<framework>.md`
89
+
90
+ No new provider code needed. The same `OAPGuardrailProvider` works for any framework in the same language.
@@ -1,40 +1,69 @@
1
1
  # APort Guardrails — OpenClaw
2
2
 
3
- **How guardrails work:** OpenClaw’s plugin API provides a `before_tool_call` hook that runs **before every tool execution**. The APort plugin registers this hook and calls the shared evaluator (API or local script); the platform blocks the tool if the evaluator returns deny. The model cannot skip it.
3
+ OpenClaw is an open-source AI agent platform with a plugin system and built-in `before_tool_call` hooks. APort integrates via two paths:
4
4
 
5
- - **Integration:** `before_tool_call` plugin (`extensions/openclaw-aport`)
6
- - **Config:** `~/.openclaw/config.yaml`
5
+ - **Native `guardrails:` config** (recommended) — OpenClaw's built-in `GuardrailProvider` interface loads `OAPGuardrailProvider` directly. No plugin needed.
6
+ - **Plugin** (legacy, still works) — The `openclaw-aport` plugin registers a `before_tool_call` hook.
7
7
 
8
- ## Two ways to use APort
8
+ Both paths use the same evaluator, passport, and policies.
9
9
 
10
- | Use case | What it is | When to use it |
11
- |----------|------------|----------------|
12
- | **Guardrails (CLI/setup)** | Full installer: runs the **passport wizard**, writes config, installs the **OpenClaw plugin** so every tool call goes through the evaluator. | Getting started: one command sets up config, passport, and the plugin. |
13
- | **Core (runtime)** | The **evaluator** (bash script or API) that the plugin calls before each tool run. Same policy + passport as other frameworks. For **programmatic** use (e.g. custom scripts), you can use the **Python** (`aport_guardrails`) or **Node** (`@aporthq/aport-agent-guardrails-core`) library. | Guardrails = the plugin uses the evaluator automatically. Use the library only if you're building custom tooling. |
10
+ ## Quick start
14
11
 
15
- For OpenClaw, you use **Guardrails (CLI)** once to install the plugin; the **Core** (evaluator) then runs automatically on every tool call.
12
+ ```bash
13
+ npm install @aporthq/aport-agent-guardrails-core
14
+ npx @aporthq/aport-agent-guardrails openclaw
15
+ ```
16
16
 
17
- ---
17
+ The first command installs the provider. The second runs the passport wizard.
18
18
 
19
- ## Setup
19
+ ## Config (native — recommended)
20
20
 
21
- ```bash
22
- npx @aporthq/aport-agent-guardrails openclaw
23
- # or
24
- npx @aporthq/aport-agent-guardrails
25
- # then choose openclaw
21
+ Add to your OpenClaw `config.yaml`:
22
+
23
+ ```yaml
24
+ guardrails:
25
+ enabled: true
26
+ provider:
27
+ use: "@aporthq/aport-agent-guardrails-core"
28
+ config:
29
+ framework: "openclaw"
26
30
  ```
27
31
 
28
- ## Config
32
+ This loads `OAPGuardrailProvider` as a core guardrail service — runs before plugin hooks, cannot be bypassed by disabling a plugin.
33
+
34
+ ## Config (plugin — legacy)
35
+
36
+ The setup wizard (`npx @aporthq/aport-agent-guardrails openclaw`) installs the `openclaw-aport` plugin automatically. This registers a `before_tool_call` hook that calls the same evaluator.
37
+
38
+ Both approaches are supported. The native config is recommended for new deployments.
39
+
40
+ ## How it works
41
+
42
+ ```
43
+ Agent decides to use a tool
44
+
45
+
46
+ GuardrailService (native) or Plugin hook (legacy)
47
+ │ │
48
+ ▼ ▼
49
+ OAPGuardrailProvider ──────────────── same Evaluator
50
+
51
+ ┌─────┴─────┐
52
+ │ │
53
+ ALLOW DENY
54
+ │ │
55
+ Tool runs Agent sees denial reason
56
+ ```
29
57
 
30
- - **Config dir:** `~/.openclaw` (or `OPENCLAW_HOME`)
31
- - **Passport:** Created by wizard or use hosted `agent_id`
32
- - **Plugin:** `extensions/openclaw-aport`
58
+ - **Provider class:** `OAPGuardrailProvider` from `@aporthq/aport-agent-guardrails-core`
59
+ - **Passport:** `~/.openclaw/aport/passport.json` (created by wizard)
60
+ - **Config:** `~/.openclaw/aport/config.yaml`
61
+ - **Audit log:** `~/.openclaw/aport/audit.log`
33
62
 
34
63
  ## Suspend (kill switch)
35
64
 
36
- Same standard as all frameworks: **passport is the source of truth**—no separate file. Local: set passport `status` to `suspended` (or `active` to resume). Remote: use API mode and suspend in [APort](https://aport.io); all agents using that passport deny within ≤30s.
65
+ Local: set passport `status` to `suspended`. Remote: use API mode and suspend at [aport.io](https://aport.io).
37
66
 
38
67
  ## Status
39
68
 
40
- Shipped; in production.
69
+ Shipped; in production. Native `guardrails:` config available when [OpenClaw #46441](https://github.com/openclaw/openclaw/issues/46441) merges.
@@ -2,7 +2,7 @@
2
2
  "id": "openclaw-aport",
3
3
  "name": "APort Guardrails",
4
4
  "description": "Deterministic pre-action authorization via APort policy enforcement. Registers before_tool_call to block disallowed tools.",
5
- "version": "1.0.19",
5
+ "version": "1.0.20",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aporthq/openclaw-aport",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "OpenClaw plugin for deterministic pre-action authorization via APort guardrails",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 LiftRails Inc.
3
+ Copyright (c) 2025 APort Technologies Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -39,6 +39,7 @@ OAP provides the **runtime trust layer** that makes agentic commerce safe and sc
39
39
  - **[Passport Schema](./oap/passport-schema.json)** — Agent identity and capabilities
40
40
  - **[Decision Schema](./oap/decision-schema.json)** — Authorization decisions
41
41
  - **[Security Model](./oap/security.md)** — Cryptographic verification
42
+ - **[Service Discovery](./well-known.md)** — `.well-known/oap/` endpoint specification
42
43
 
43
44
  ### 🎯 Policy Framework
44
45
  - **[Capability Registry](./oap/capability-registry.md)** — Standardized capabilities and limits
@@ -144,7 +144,7 @@ export class Ed25519 {
144
144
  async resolveKey(kid: string): Promise<string | null> {
145
145
  try {
146
146
  // For conformance testing, we'll use test keys
147
- // In production, this would resolve keys from /.well-known/oap/keys.json
147
+ // In production, this would resolve keys from /.well-known/oap/jwks.json
148
148
 
149
149
  if (kid.startsWith("oap:registry:test-key")) {
150
150
  const { publicKey } = await this.generateTestKeyPair();
@@ -26,7 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
26
26
 
27
27
  - Ed25519 signature scheme for decision signing
28
28
  - JCS (RFC 8785) canonicalization for deterministic hashing
29
- - Key resolution via `/.well-known/oap/keys.json`
29
+ - Key resolution via `/.well-known/oap/jwks.json`
30
30
  - Suspend semantics with 30-second global invalidation
31
31
  - Passport digest verification for decision integrity
32
32
 
@@ -21,7 +21,7 @@ Full conformance requires all basic conformance requirements plus:
21
21
 
22
22
  1. **Policy evaluation**: Implement policy pack evaluation logic
23
23
  2. **JCS canonicalization**: Use RFC 8785 for canonicalization
24
- 3. **Key resolution**: Resolve keys via `/.well-known/oap/keys.json`
24
+ 3. **Key resolution**: Resolve keys via `/.well-known/oap/jwks.json`
25
25
  4. **Suspend semantics**: Implement 30-second global invalidation
26
26
  5. **Performance**: Meet performance requirements (see below)
27
27
 
@@ -73,7 +73,7 @@ Implementations MUST:
73
73
  Implementations MUST:
74
74
 
75
75
  1. **Parse signature**: Extract Ed25519 signature from decision
76
- 2. **Resolve key**: Fetch public key using `kid` and `/.well-known/oap/keys.json`
76
+ 2. **Resolve key**: Fetch public key using `kid` and `/.well-known/oap/jwks.json`
77
77
  3. **Canonicalize**: Apply JCS canonicalization to decision payload
78
78
  4. **Verify signature**: Use Ed25519 to verify signature
79
79
  5. **Check expiration**: Ensure decision hasn't expired
@@ -207,7 +207,7 @@ All objects MUST be canonicalized using [RFC 8785 JCS](https://tools.ietf.org/ht
207
207
 
208
208
  - All decisions MUST be signed with Ed25519
209
209
  - Signatures are computed over JCS-canonicalized decision payloads
210
- - Key identifiers (kid) MUST be resolvable via `/.well-known/oap/keys.json`
210
+ - Key identifiers (kid) MUST be resolvable via `/.well-known/oap/jwks.json`
211
211
 
212
212
  ### Key Resolution
213
213
 
@@ -391,7 +391,7 @@ Custom validators MUST be:
391
391
  ### Key Management
392
392
 
393
393
  - Ed25519 keys for all signatures
394
- - Registry keys published at `https://api.yourdomain/.well-known/oap/keys.json`
394
+ - Registry keys published at `https://api.yourdomain/.well-known/oap/jwks.json`
395
395
  - Owner keys MAY be published at their domain
396
396
 
397
397
  ### Receipt Verification
@@ -42,7 +42,7 @@ All objects are canonicalized using RFC 8785 JCS before signing:
42
42
  Registry keys are used by the OAP registry to sign decisions:
43
43
 
44
44
  - **Format**: `oap:registry:<keyid>`
45
- - **Location**: `https://api.yourdomain/.well-known/oap/keys.json`
45
+ - **Location**: `https://api.yourdomain/.well-known/oap/jwks.json`
46
46
  - **Rotation**: Regular rotation schedule (e.g., quarterly)
47
47
  - **Backup**: Multiple keys for high availability
48
48
 
@@ -51,7 +51,7 @@ Registry keys are used by the OAP registry to sign decisions:
51
51
  Owner keys are used by passport owners for additional verification:
52
52
 
53
53
  - **Format**: `oap:owner:<domain>:<keyid>`
54
- - **Location**: `https://<domain>/.well-known/oap/keys.json`
54
+ - **Location**: `https://<domain>/.well-known/oap/jwks.json`
55
55
  - **Optional**: Not required for basic OAP compliance
56
56
  - **Use Case**: Additional verification layers
57
57
 
@@ -68,8 +68,8 @@ Keys are resolved using the following process:
68
68
  #### Key Resolution URLs
69
69
 
70
70
  ```
71
- Registry keys: https://api.yourdomain/.well-known/oap/keys.json
72
- Owner keys: https://<domain>/.well-known/oap/keys.json
71
+ Registry keys: https://api.yourdomain/.well-known/oap/jwks.json
72
+ Owner keys: https://<domain>/.well-known/oap/jwks.json
73
73
  ```
74
74
 
75
75
  #### Key Format
@@ -30,7 +30,7 @@
30
30
  "proof": {
31
31
  "type": "Ed25519Signature2020",
32
32
  "created": "2024-01-15T10:30:00Z",
33
- "verificationMethod": "https://aport.io/.well-known/oap/keys.json#ap_registry_ed25519_2024",
33
+ "verificationMethod": "https://aport.io/.well-known/oap/jwks.json#ap_registry_ed25519_2024",
34
34
  "proofPurpose": "assertionMethod",
35
35
  "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJjcmVkZW50aWFsU3ViamVjdCI6eyJkZWNpc2lvbl9pZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMiIsInBvbGljeV9pZCI6InBheW1lbnRzLnJlZnVuZC52MSIsInBhc3Nwb3J0X2lkIjoiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwIiwib3duZXJfaWQiOiJvcmdfMTIzNDU2NzgiLCJhc3N1cmFuY2VfbGV2ZWwiOiJMMiIsImFsbG93Ijp0cnVlLCJyZWFzb25zIjpbeyJjb2RlIjoib2FwLmFsbG93ZWQiLCJtZXNzYWdlIjoiVHJhbnNhY3Rpb24gd2l0aGluIGxpbWl0cyBhbmQgcG9saWN5IHJlcXVpcmVtZW50cyJ9XSwiaXNzdWVkX2F0IjoiMjAyNC0wMS0xNVQxMDozMDowMFoiLCJleHBpcmVzX2F0IjoiMjAyNC0wMS0xNVQxMTozMDowMFoiLCJwYXNzcG9ydF9kaWdlc3QiOiJzaGEyNTY6YWJjZDEyMzRlZmdoNTY3OGlqa2w5MDEybW5vcDM0NTZxcnN0Nzg5MHV2d3gxMjM0eXphYjU2NzhjZGVmIn0sImlzc3VlciI6Imh0dHBzOi8vYXBpLmFwb3J0LmRldiIsImlzc3VhbmNlRGF0ZSI6IjIwMjQtMDEtMTVUMTA6MzA6MDBaIiwiZXhwaXJhdGlvbkRhdGUiOiIyMDI0LTAxLTE1VDExOjMwOjAwWiJ9.signature"
36
36
  }
@@ -61,7 +61,7 @@
61
61
  "proof": {
62
62
  "type": "Ed25519Signature2020",
63
63
  "created": "2024-01-01T00:00:00Z",
64
- "verificationMethod": "https://aport.io/.well-known/oap/keys.json#ap_registry_ed25519_2024",
64
+ "verificationMethod": "https://aport.io/.well-known/oap/jwks.json#ap_registry_ed25519_2024",
65
65
  "proofPurpose": "assertionMethod",
66
66
  "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJjcmVkZW50aWFsU3ViamVjdCI6eyJwYXNzcG9ydF9pZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCIsImtpbmQiOiJ0ZW1wbGF0ZSIsInNwZWNfdmVyc2lvbiI6Im9hcC8xLjAiLCJvd25lcl9pZCI6Im9yZ18xMjM0NTY3OCIsIm93bmVyX3R5cGUiOiJvcmciLCJhc3N1cmFuY2VfbGV2ZWwiOiJMMiIsInN0YXR1cyI6ImFjdGl2ZSIsImNhcGFiaWxpdGllcyI6WyJwYXltZW50cy5yZWZ1bmQiLCJkYXRhLmV4cG9ydCIsInJlcG8ucmVsZWFzZS5wdWJsaXNoIl0sImxpbWl0cyI6eyJwYXltZW50cy5yZWZ1bmQiOnsiY3VycmVuY3lfbGltaXRzIjp7IlVTRCI6eyJtYXhfcGVyX3R4Ijo1MDAwLCJkYWlseV9jYXAiOjUwMDAwfSwiRVVSIjp7Im1heF9wZXJfdHgiOjQ1MDAsImRhaWx5X2NhcCI6NDUwMDB9fSwicmVhc29uX2NvZGVzIjpbImN1c3RvbWVyX3JlcXVlc3QiLCJkZWZlY3RpdmVfcHJvZHVjdCIsImZyYXVkIl0sImlkZW1wb3RlbmN5X3JlcXVpcmVkIjp0cnVlfSwiZGF0YS5leHBvcnQiOnsibWF4X3Jvd3MiOjEwMDAwMCwiYWxsb3dfcGlpIjpmYWxzZSwiYWxsb3dlZF9jb2xsZWN0aW9ucyI6WyJ1c2VycyIsIm9yZGVycyIsInByb2R1Y3RzIl19LCJyZXBvLnJlbGVhc2UucHVibGlzaCI6eyJhbGxvd2VkX2JyYW5jaGVzIjpbIm1haW4iLCJkZXZlbG9wIl0sIm1heF9yZWxlYXNlc19wZXJfZGF5IjoxMCwicmVxdWlyZV9zaWduZWRfYXJ0aWZhY3RzIjp0cnVlfX0sInJlZ2lvbnMiOlsiVVMiLCJDQSIsIkVVIl0sIm1ldGFkYXRhIjp7Im5hbWUiOiJDdXN0b21lciBTdXBwb3J0IEFJIiwiZGVzY3JpcHRpb24iOiJBSSBhZ2VudCBmb3IgY3VzdG9tZXIgc3VwcG9ydCBvcGVyYXRpb25zIiwidmVyc2lvbiI6IjEuMC4wIiwiY29udGFjdCI6InN1cHBvcnRAZXhhbXBsZS5jb20iLCJob21lcGFnZSI6Imh0dHBzOi8vZXhhbXBsZS5jb20vYWkvc3VwcG9ydCJ9LCJjcmVhdGVkX2F0IjoiMjAyNC0wMS0wMVQwMDowMDowMFoiLCJ1cGRhdGVkX2F0IjoiMjAyNC0wMS0xNVQxMDozMDowMFoiLCJ2ZXJzaW9uIjoxfX0sImlzc3VlciI6Imh0dHBzOi8vYXBpLmFwb3J0LmRldiIsImlzc3VhbmNlRGF0ZSI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIiwiZXhwaXJhdGlvbkRhdGUiOiIyMDI1LTAxLTAxVDAwOjAwOjAwWiJ9.signature"
67
67
  }
@@ -42,7 +42,7 @@ const vc = {
42
42
  type: "Ed25519Signature2020",
43
43
  created: "2024-01-15T10:30:00Z",
44
44
  verificationMethod:
45
- "https://aport.io/.well-known/oap/keys.json#ap_registry_ed25519_2024",
45
+ "https://aport.io/.well-known/oap/jwks.json#ap_registry_ed25519_2024",
46
46
  proofPurpose: "assertionMethod",
47
47
  jws: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJjcmVkZW50aWFsU3ViamVjdCI6eyJkZWNpc2lvbl9pZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMiIsInBvbGljeV9pZCI6InBheW1lbnRzLnJlZnVuZC52MSIsImFnZW50X2lkIjoiNTUwZTg0MDAtZTI5Yi00MWQ0LWE3MTYtNDQ2NjU1NDQwMDAwIiwib3duZXJfaWQiOiJvcmdfMTIzNDU2NzgiLCJhc3N1cmFuY2VfbGV2ZWwiOiJMMiIsImFsbG93Ijp0cnVlLCJyZWFzb25zIjpbeyJjb2RlIjoib2FwLmFsbG93ZWQiLCJtZXNzYWdlIjoiVHJhbnNhY3Rpb24gd2l0aGluIGxpbWl0cyJ9XSwicGFzc3BvcnRfZGlnZXN0Ijoic2hhMjU2OmFiY2QxMjM0ZWZnaDU2NzhpamtsOTAxMm1ub3AzNDU2cXJzdDc4OTB1dnd4MTIzNHl6YWI1Njc4Y2RlZiIsInNpZ25hdHVyZSI6ImV5SmhiR2NpT2lKU1V6STFOaUlzSW5SNWNDSTZJa3BYVkNKOS5leUpoYkdjaU9pSlNVekkxTmlJc0luUjVjQ0k2SWtwWFZDSjkucGxhY2Vob2xkZXIifQ.signature",
48
48
  },
@@ -59,7 +59,7 @@ const vc = {
59
59
  type: "Ed25519Signature2020",
60
60
  created: "2024-01-01T00:00:00Z",
61
61
  verificationMethod:
62
- "https://aport.io/.well-known/oap/keys.json#ap_registry_ed25519_2024",
62
+ "https://aport.io/.well-known/oap/jwks.json#ap_registry_ed25519_2024",
63
63
  proofPurpose: "assertionMethod",
64
64
  jws: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJjcmVkZW50aWFsU3ViamVjdCI6eyJhZ2VudF9pZCI6IjU1MGU4NDAwLWUyOWItNDFkNC1hNzE2LTQ0NjY1NTQ0MDAwMCIsImtpbmQiOiJ0ZW1wbGF0ZSIsInNwZWNfdmVyc2lvbiI6Im9hcC8xLjAiLCJvd25lcl9pZCI6Im9yZ18xMjM0NTY3OCIsIm93bmVyX3R5cGUiOiJvcmciLCJhc3N1cmFuY2VfbGV2ZWwiOiJMMiIsInN0YXR1cyI6ImFjdGl2ZSIsImNhcGFiaWxpdGllcyI6W3siaWQiOiJwYXltZW50cy5yZWZ1bmQiLCJwYXJhbXMiOnsiY3VycmVuY3lfbGltaXRzIjp7IlVTRCI6eyJtYXhfcGVyX3R4Ijo1MDAwfX19fSx7ImlkIjoiZGF0YS5leHBvcnQiLCJwYXJhbXMiOnsibWF4X3Jvd3MiOjEwMDAwMH19XSwibGltaXRzIjp7InBheW1lbnRzLnJlZnVuZCI6eyJjdXJyZW5jeV9saW1pdHMiOnsiVVNEIjp7Im1heF9wZXJfdHgiOjUwMDAsImRhaWx5X2NhcCI6NTAwMDB9fSwicmVhc29uX2NvZGVzIjpbImN1c3RvbWVyX3JlcXVlc3QiLCJkZWZlY3RpdmVfcHJvZHVjdCJdLCJpZGVtcG90ZW5jeV9yZXF1aXJlZCI6dHJ1ZX19LCJyZWdpb25zIjpbIlVTIiwiQ0EiXSwibWV0YWRhdGEiOnsibmFtZSI6IkN1c3RvbWVyIFN1cHBvcnQgQUkiLCJkZXNjcmlwdGlvbiI6IkFJIGFnZW50IGZvciBjdXN0b21lciBzdXBwb3J0IG9wZXJhdGlvbnMifSwiY3JlYXRlZF9hdCI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIiwidXBkYXRlZF9hdCI6IjIwMjQtMDEtMTVUMTA6MzA6MDBaIiwidmVyc2lvbiI6IjEuMC4wIn0sImlzc3VlciI6Imh0dHBzOi8vYXBpLmFwb3J0LmRldiIsImlzc3VhbmNlRGF0ZSI6IjIwMjQtMDEtMDFUMDA6MDA6MDBaIiwiZXhwaXJhdGlvbkRhdGUiOiIyMDI1LTAxLTAxVDAwOjAwOjAwWiJ9.signature",
65
65
  },
@@ -285,8 +285,8 @@ export async function exportDecisionToVC(
285
285
  type: "Ed25519Signature2020",
286
286
  created: decision.created_at,
287
287
  // For decisions, use registry's well-known endpoint
288
- // This should resolve to: https://aport.io/.well-known/oap/keys.json
289
- verificationMethod: `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
288
+ // This should resolve to: https://aport.io/.well-known/oap/jwks.json
289
+ verificationMethod: `${registryKey.issuer}/.well-known/oap/jwks.json#${registryKey.kid}`,
290
290
  proofPurpose: "assertionMethod",
291
291
  jws: jws,
292
292
  },
@@ -21,7 +21,7 @@ function exportPassportToVC(passport, registryKey) {
21
21
  proof: {
22
22
  type: "Ed25519Signature2020",
23
23
  created: passport.created_at,
24
- verificationMethod: `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
24
+ verificationMethod: `${registryKey.issuer}/.well-known/oap/jwks.json#${registryKey.kid}`,
25
25
  proofPurpose: "assertionMethod",
26
26
  jws: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
27
27
  },
@@ -44,7 +44,7 @@ function exportDecisionToVC(decision, registryKey) {
44
44
  proof: {
45
45
  type: "Ed25519Signature2020",
46
46
  created: decision.created_at,
47
- verificationMethod: `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
47
+ verificationMethod: `${registryKey.issuer}/.well-known/oap/jwks.json#${registryKey.kid}`,
48
48
  proofPurpose: "assertionMethod",
49
49
  jws: "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
50
50
  },
@@ -60,7 +60,7 @@ An OAP passport can be exported as a Verifiable Credential with the following st
60
60
  "proof": {
61
61
  "type": "Ed25519Signature2020",
62
62
  "created": "2024-01-01T00:00:00Z",
63
- "verificationMethod": "https://aport.io/.well-known/oap/keys.json#key-2025-01",
63
+ "verificationMethod": "https://aport.io/.well-known/oap/jwks.json#key-2025-01",
64
64
  "proofPurpose": "assertionMethod",
65
65
  "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
66
66
  }
@@ -112,7 +112,7 @@ An OAP decision can be exported as a Verifiable Credential receipt:
112
112
  "proof": {
113
113
  "type": "Ed25519Signature2020",
114
114
  "created": "2024-01-15T10:30:00Z",
115
- "verificationMethod": "https://aport.io/.well-known/oap/keys.json#key-2025-01",
115
+ "verificationMethod": "https://aport.io/.well-known/oap/jwks.json#key-2025-01",
116
116
  "proofPurpose": "assertionMethod",
117
117
  "jws": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9..."
118
118
  }
@@ -280,7 +280,7 @@ function exportPassportToVC(passport, registryKey) {
280
280
  "proof": {
281
281
  "type": "Ed25519Signature2020",
282
282
  "created": passport.created_at,
283
- "verificationMethod": `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
283
+ "verificationMethod": `${registryKey.issuer}/.well-known/oap/jwks.json#${registryKey.kid}`,
284
284
  "proofPurpose": "assertionMethod",
285
285
  "jws": signCredential(passport, registryKey)
286
286
  }
@@ -329,7 +329,7 @@ function exportDecisionToVC(decision, registryKey) {
329
329
  "proof": {
330
330
  "type": "Ed25519Signature2020",
331
331
  "created": decision.created_at,
332
- "verificationMethod": `${registryKey.issuer}/.well-known/oap/keys.json#${registryKey.kid}`,
332
+ "verificationMethod": `${registryKey.issuer}/.well-known/oap/jwks.json#${registryKey.kid}`,
333
333
  "proofPurpose": "assertionMethod",
334
334
  "jws": signCredential(decision, registryKey)
335
335
  }
@@ -0,0 +1,85 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://aport.io/oap/well-known-schema.json",
4
+ "title": "OAP Service Discovery Response",
5
+ "description": "JSON Schema for the /.well-known/oap/ discovery endpoint response. Conforms to RFC 8615 and mirrors OAuth 2.0 Authorization Server Metadata (RFC 8414).",
6
+ "type": "object",
7
+ "required": [
8
+ "oap_version",
9
+ "authorization_endpoint",
10
+ "passport_endpoint",
11
+ "policy_endpoint"
12
+ ],
13
+ "properties": {
14
+ "oap_version": {
15
+ "type": "string",
16
+ "description": "OAP specification version implemented.",
17
+ "pattern": "^\\d+\\.\\d+$",
18
+ "examples": ["1.0"]
19
+ },
20
+ "authorization_endpoint": {
21
+ "type": "string",
22
+ "format": "uri-template",
23
+ "description": "Passport verification endpoint. Accepts GET with an {agent_id} path parameter.",
24
+ "examples": ["https://api.example.com/api/verify/{agent_id}"]
25
+ },
26
+ "passport_endpoint": {
27
+ "type": "string",
28
+ "format": "uri-template",
29
+ "description": "Endpoint for retrieving and managing OAP passports.",
30
+ "examples": ["https://api.example.com/api/passports/{agent_id}"]
31
+ },
32
+ "policy_endpoint": {
33
+ "type": "string",
34
+ "format": "uri-template",
35
+ "description": "Endpoint for retrieving policy packs by name. Accepts {policy_name} in dot-separated format.",
36
+ "examples": ["https://api.example.com/api/policies/{policy_name}"]
37
+ },
38
+ "decision_endpoint": {
39
+ "type": "string",
40
+ "format": "uri-template",
41
+ "description": "Endpoint for retrieving individual authorization decisions by {decision_id}.",
42
+ "examples": ["https://api.example.com/api/verify/decisions/get/{decision_id}"]
43
+ },
44
+ "jwks_uri": {
45
+ "type": "string",
46
+ "format": "uri",
47
+ "description": "JWKS endpoint (RFC 7517) for verifying OAP decision and VC signatures.",
48
+ "examples": ["https://api.example.com/.well-known/oap/jwks.json"]
49
+ },
50
+ "supported_capabilities": {
51
+ "type": "array",
52
+ "items": {
53
+ "type": "string",
54
+ "pattern": "^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)*$"
55
+ },
56
+ "description": "Dot-separated capability identifiers this server can enforce. Format: category.subcategory.action.",
57
+ "examples": [
58
+ ["finance.payment.refund", "finance.payment.charge", "data.export.create", "messaging.message.send"]
59
+ ]
60
+ },
61
+ "supported_assurance_levels": {
62
+ "type": "array",
63
+ "items": {
64
+ "type": "string",
65
+ "enum": ["L0", "L1", "L2", "L3", "L4KYC", "L4FIN"]
66
+ },
67
+ "description": "Assurance levels the issuer can credential and enforce."
68
+ },
69
+ "agent_manifest_endpoint": {
70
+ "type": "string",
71
+ "format": "uri",
72
+ "description": "Public agent registry scoped to this domain."
73
+ },
74
+ "spec_uri": {
75
+ "type": "string",
76
+ "format": "uri",
77
+ "description": "Link to the OAP specification document."
78
+ },
79
+ "contact": {
80
+ "type": "string",
81
+ "description": "Security or operations contact (email or URL)."
82
+ }
83
+ },
84
+ "additionalProperties": false
85
+ }
@@ -0,0 +1,203 @@
1
+ # OAP Service Discovery via `.well-known/oap/`
2
+
3
+ **Specification:** OAP v1.0
4
+ **Status:** Draft
5
+ **Last Updated:** 2026-03-26
6
+
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ The `.well-known/oap/` URI provides a standard discovery mechanism for the Open Agent Passport (OAP) authorization layer. Any HTTP server that supports OAP-based pre-action authorization MAY expose this endpoint to allow agent frameworks, orchestration platforms, and relying parties to automatically discover its OAP configuration without prior out-of-band setup.
12
+
13
+ This specification follows the conventions established by RFC 8615 (Well-Known URIs) and mirrors the discovery patterns used by:
14
+ - OAuth 2.0 Authorization Server Metadata ([RFC 8414](https://www.rfc-editor.org/rfc/rfc8414))
15
+ - OpenID Connect Discovery 1.0
16
+ - DIF Well-Known DID Configuration ([DIF](https://identity.foundation/.well-known/resources/did-configuration/))
17
+
18
+ ---
19
+
20
+ ## Discovery Request
21
+
22
+ A client wishing to discover OAP support for a domain sends:
23
+
24
+ ```http
25
+ GET /.well-known/oap/ HTTP/1.1
26
+ Host: example.com
27
+ Accept: application/json
28
+ ```
29
+
30
+ Servers MUST accept requests both with and without a trailing slash (`/.well-known/oap` and `/.well-known/oap/`). Servers MAY redirect one form to the other using HTTP 301, but MUST NOT return 404 for either form.
31
+
32
+ ---
33
+
34
+ ## Discovery Response
35
+
36
+ A server that supports OAP MUST respond with HTTP 200 and a JSON body conforming to the schema defined in [`well-known-schema.json`](./oap/well-known-schema.json).
37
+
38
+ ### Example Response
39
+
40
+ ```json
41
+ {
42
+ "oap_version": "1.0",
43
+ "authorization_endpoint": "https://api.example.com/api/verify/{agent_id}",
44
+ "passport_endpoint": "https://api.example.com/api/passports/{agent_id}",
45
+ "policy_endpoint": "https://api.example.com/api/policies/{policy_name}",
46
+ "decision_endpoint": "https://api.example.com/api/verify/decisions/get/{decision_id}",
47
+ "jwks_uri": "https://api.example.com/.well-known/oap/jwks.json",
48
+ "supported_capabilities": [
49
+ "finance.payment.refund",
50
+ "finance.payment.charge",
51
+ "data.export.create",
52
+ "messaging.message.send"
53
+ ],
54
+ "supported_assurance_levels": ["L0", "L1", "L2", "L3", "L4KYC", "L4FIN"],
55
+ "spec_uri": "https://github.com/aporthq/aport-spec/blob/main/oap/oap-spec.md",
56
+ "contact": "security@example.com"
57
+ }
58
+ ```
59
+
60
+ ### Response Field Reference
61
+
62
+ | Field | Type | Required | Description |
63
+ |-------|------|----------|-------------|
64
+ | `oap_version` | string | **REQUIRED** | OAP specification version implemented (e.g., `"1.0"`). |
65
+ | `authorization_endpoint` | URI template | **REQUIRED** | Passport verification endpoint. Accepts `GET` with an `{agent_id}` path parameter. Returns the passport with capabilities and assurance level. Equivalent to the OAP "evaluate" flow entry point. |
66
+ | `passport_endpoint` | URI template | **REQUIRED** | Endpoint for retrieving and managing OAP passports. |
67
+ | `policy_endpoint` | URI template | **REQUIRED** | Endpoint for retrieving policy packs by name. Accepts `{policy_name}` in dot-separated format (e.g., `finance.payment.refund.v1`). |
68
+ | `decision_endpoint` | URI template | RECOMMENDED | Endpoint for retrieving individual authorization decisions by `{decision_id}`. |
69
+ | `jwks_uri` | URI | RECOMMENDED | JWKS endpoint (RFC 7517) for verifying OAP decision and VC signatures. MUST point to `/.well-known/oap/jwks.json` on the same domain or a domain controlled by the same organization. |
70
+ | `supported_capabilities` | array of string | RECOMMENDED | Dot-separated capability identifiers this server can enforce (see [Capability Registry](./oap/capability-registry.md)). Format: `category.subcategory.action` (e.g., `finance.payment.refund`). |
71
+ | `supported_assurance_levels` | array of enum | RECOMMENDED | Assurance levels the issuer can credential and enforce. Valid values: `L0` (self-attested), `L1` (email verified), `L2` (GitHub verified), `L3` (domain verified), `L4KYC` (KYC/KYB verified), `L4FIN` (financial data verified). |
72
+ | `agent_manifest_endpoint` | URI | OPTIONAL | Public agent registry scoped to this domain. |
73
+ | `spec_uri` | URI | OPTIONAL | Link to the OAP specification document. |
74
+ | `contact` | string | OPTIONAL | Security or operations contact (email or URL). |
75
+
76
+ ### Field Naming Conventions
77
+
78
+ - Endpoint fields that accept path parameters use the `_endpoint` suffix and URI template syntax (`{param}`).
79
+ - The `jwks_uri` field follows the naming convention established by OpenID Connect Discovery (section 3) and OAuth 2.0 Authorization Server Metadata (RFC 8414, section 2).
80
+
81
+ ---
82
+
83
+ ## Sub-paths
84
+
85
+ Servers MAY serve additional resources under `.well-known/oap/`:
86
+
87
+ | Path | Content-Type | Purpose |
88
+ |------|-------------|---------|
89
+ | `/.well-known/oap/` | `application/json` | Discovery document (this spec) |
90
+ | `/.well-known/oap/jwks.json` | `application/json` | Domain-scoped JWKS (RFC 7517) for signature verification |
91
+ | `/.well-known/oap/agents/` | `application/json` | Domain-scoped agent manifest |
92
+
93
+ The `jwks_uri` field in the discovery document and the `/.well-known/oap/jwks.json` sub-path MUST resolve to the same key set. Servers SHOULD NOT serve keys at other paths to avoid ambiguity.
94
+
95
+ ---
96
+
97
+ ## Error Responses
98
+
99
+ If a server does not implement OAP, it SHOULD return HTTP 404.
100
+
101
+ Servers that wish to explicitly signal non-support MAY return HTTP 404 with:
102
+
103
+ ```json
104
+ {
105
+ "error": "oap_not_supported",
106
+ "error_description": "This server does not implement OAP authorization."
107
+ }
108
+ ```
109
+
110
+ Servers that are temporarily unavailable SHOULD return HTTP 503.
111
+
112
+ ---
113
+
114
+ ## Security Considerations
115
+
116
+ 1. **TLS required.** The `/.well-known/oap/` endpoint MUST be served over HTTPS. HTTP is not acceptable for production deployments — discovery over plaintext exposes the authorization endpoint to MITM substitution.
117
+
118
+ 2. **Validate the response.** Clients MUST validate that all URI values in the discovery document use HTTPS and belong to the expected domain or a trusted subdomain before using them.
119
+
120
+ 3. **No sensitive data in discovery.** The discovery document MUST NOT contain credentials, private keys, or tenant-specific sensitive data.
121
+
122
+ 4. **Cache with care.** Clients MAY cache discovery responses but SHOULD respect `Cache-Control` headers and re-fetch on authorization failures. Stale discovery responses can silently route traffic to outdated endpoints.
123
+
124
+ 5. **Endpoint ownership.** All endpoint URIs SHOULD be hosted on the same domain as `/.well-known/oap/` or on a domain explicitly controlled by the same organization. Clients SHOULD reject endpoints that point to unrelated domains.
125
+
126
+ ---
127
+
128
+ ## Use Cases
129
+
130
+ ### 1. Agent Framework Auto-Configuration
131
+
132
+ An agent framework (e.g., LangChain, CrewAI) integrating OAP enforcement discovers the authorization endpoint by resolving `/.well-known/oap/` on the target API's domain. No developer configuration required.
133
+
134
+ ```typescript
135
+ // Auto-discovery in an OAP-aware agent framework
136
+ const oap = await fetch(`https://${targetDomain}/.well-known/oap/`)
137
+ .then(r => r.json());
138
+
139
+ // Verify agent passport before action
140
+ const passport = await fetch(
141
+ oap.authorization_endpoint.replace('{agent_id}', agentId)
142
+ ).then(r => r.json());
143
+
144
+ if (passport.status === 'active') {
145
+ // Agent is verified — proceed with action
146
+ }
147
+ ```
148
+
149
+ ### 2. Relying Party Verification
150
+
151
+ A merchant or API gateway verifying an agent's OAP credential resolves the agent's issuer domain's `/.well-known/oap/` endpoint to obtain the JWKS for signature verification — the same pattern used in JWT issuer discovery.
152
+
153
+ ```typescript
154
+ // Fetch issuer's JWKS for signature verification
155
+ const oap = await fetch(`https://${issuerDomain}/.well-known/oap/`).then(r => r.json());
156
+ const jwks = await fetch(oap.jwks_uri).then(r => r.json());
157
+ const verified = verifyDecisionSignature(decision, jwks);
158
+ ```
159
+
160
+ ### 3. Multi-Agent Delegation
161
+
162
+ In a multi-agent pipeline, a sub-agent discovering the orchestrator's OAP endpoint to check delegation scope resolves `/.well-known/oap/` on the orchestrator's domain.
163
+
164
+ ---
165
+
166
+ ## IANA Considerations
167
+
168
+ A Well-Known URI registration for `oap` is planned per [RFC 8615 section 5](https://www.rfc-editor.org/rfc/rfc8615#section-5):
169
+
170
+ - **URI Suffix:** `oap`
171
+ - **Change Controller:** APort / LiftRails Inc.
172
+ - **Specification Document:** This document and [oap-spec.md](./oap/oap-spec.md)
173
+ - **Status:** Planned
174
+
175
+ Registration will be submitted once this specification reaches a stable draft.
176
+
177
+ ---
178
+
179
+ ## Conformance
180
+
181
+ An implementation is conformant with this specification if:
182
+
183
+ 1. It responds to `GET /.well-known/oap/` over HTTPS with HTTP 200.
184
+ 2. It accepts requests both with and without a trailing slash.
185
+ 3. The response `Content-Type` is `application/json`.
186
+ 4. The response body contains at minimum: `oap_version`, `authorization_endpoint`, `passport_endpoint`, `policy_endpoint`.
187
+ 5. All URI values in the response use HTTPS.
188
+ 6. Capability identifiers in `supported_capabilities` use dot-separated format matching the [Capability Registry](./oap/capability-registry.md).
189
+ 7. If `jwks_uri` is present, it resolves to a valid JWK Set (RFC 7517) containing Ed25519 public keys.
190
+
191
+ ---
192
+
193
+ ## Related Specifications
194
+
195
+ - [OAP Specification v1.0](./oap/oap-spec.md)
196
+ - [Passport Schema](./oap/passport-schema.json)
197
+ - [Decision Schema](./oap/decision-schema.json)
198
+ - [Capability Registry](./oap/capability-registry.md)
199
+ - [Security Model](./oap/security.md)
200
+ - RFC 7517 — JSON Web Key (JWK)
201
+ - RFC 8615 — Well-Known Uniform Resource Identifiers
202
+ - RFC 8414 — OAuth 2.0 Authorization Server Metadata
203
+ - OpenID Connect Discovery 1.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aporthq/aport-agent-guardrails",
3
- "version": "1.0.19",
3
+ "version": "1.0.20",
4
4
  "description": "Policy enforcement guardrails for OpenClaw-compatible agent frameworks",
5
5
  "workspaces": [
6
6
  "packages/*",