multi-agent-protocol 0.0.3 → 0.0.6
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 +4 -0
- package/docs/00-design-specification.md +36 -0
- package/docs/01-open-questions.md +0 -5
- package/docs/02-wire-protocol.md +1 -1
- package/docs/07-federation.md +81 -5
- package/docs/09-authentication.md +748 -0
- package/docs/10-environment-awareness.md +242 -0
- package/docs/10-mail-protocol.md +553 -0
- package/docs/11-anp-inspired-improvements.md +1079 -0
- package/docs/12-anp-implementation-plan.md +641 -0
- package/docs/agent-iam-integration.md +877 -0
- package/docs/agentic-mesh-integration-draft.md +459 -0
- package/docs/git-transport-draft.md +251 -0
- package/package.json +5 -4
- package/schema/meta.json +200 -2
- package/schema/schema.json +1252 -13
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
# Implementation Plan: ANP-Inspired MAP Improvements
|
|
2
|
+
|
|
3
|
+
## Proposals Covered
|
|
4
|
+
|
|
5
|
+
| # | Proposal | Effort | Depends On | Status |
|
|
6
|
+
|---|----------|--------|------------|--------|
|
|
7
|
+
| **P6** | Single-Request Federation Auth | Low | — | ✅ Implemented |
|
|
8
|
+
| **P3** | Linked Capability Documents | Medium | — | ✅ Implemented |
|
|
9
|
+
| **P1** | `did:wba` Decentralized Identity | High | P6 | ✅ Implemented |
|
|
10
|
+
|
|
11
|
+
Implementation order: **P6 → P3 → P1** (P3 and P6 are independent; P1 builds on P6). All three phases are complete.
|
|
12
|
+
|
|
13
|
+
### Backwards Compatibility
|
|
14
|
+
|
|
15
|
+
MAP is pre-release (v0.0.8). All changes are treated as non-breaking for semver purposes. However, for consumer awareness:
|
|
16
|
+
|
|
17
|
+
- **P6**: Fully additive — only optional fields added to existing interfaces.
|
|
18
|
+
- **P3**: Fully additive — only optional fields added to `Agent`, `AgentsListFilter`, register/spawn params.
|
|
19
|
+
- **P1**: Widens `FederationAuth.method` from `"bearer" | "api-key" | "mtls"` to `FederationAuthMethod` (includes `"did:wba"`, `"none"`, `"oauth2"`, `x-${string}`). Existing exhaustive switch statements on `method` will need a `default` case. Acceptable for pre-release.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Phase 1: Single-Request Federation Auth (P6) ✅
|
|
24
|
+
|
|
25
|
+
### Goal
|
|
26
|
+
|
|
27
|
+
When `auth` credentials are provided in the initial `map/federation/connect` request, the server authenticates immediately in the same round trip — reducing federation connection setup from 2 RTT to 1 RTT.
|
|
28
|
+
|
|
29
|
+
### Step 1.1: Widen Federation Auth Types
|
|
30
|
+
|
|
31
|
+
**File**: `ts-sdk/src/types/index.ts`
|
|
32
|
+
|
|
33
|
+
**Change A** — Widen `FederationAuth` (~line 1193):
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// BEFORE
|
|
37
|
+
export interface FederationAuth {
|
|
38
|
+
method: "bearer" | "api-key" | "mtls";
|
|
39
|
+
credentials?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// AFTER
|
|
43
|
+
export type FederationAuthMethod = "bearer" | "api-key" | "mtls" | "none" | "oauth2" | `x-${string}`;
|
|
44
|
+
|
|
45
|
+
export interface FederationAuth {
|
|
46
|
+
method: FederationAuthMethod;
|
|
47
|
+
credentials?: string;
|
|
48
|
+
/** Method-specific additional data */
|
|
49
|
+
metadata?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Change B** — Extend `FederationConnectRequestParams` (~line 2152):
|
|
54
|
+
|
|
55
|
+
Add:
|
|
56
|
+
```typescript
|
|
57
|
+
/** Pre-fetched server auth context (e.g., from .well-known discovery) */
|
|
58
|
+
authContext?: {
|
|
59
|
+
source: "well-known" | "cached" | "configured";
|
|
60
|
+
challenge?: string;
|
|
61
|
+
};
|
|
62
|
+
/** System info about the connecting peer */
|
|
63
|
+
systemInfo?: { name: string; version: string; endpoint: string };
|
|
64
|
+
/** MAP protocol version */
|
|
65
|
+
protocolVersion?: string;
|
|
66
|
+
/** What this peer exposes to the other side */
|
|
67
|
+
exposure?: Record<string, unknown>;
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Change C** — Extend `FederationConnectResponseResult` (~line 2164):
|
|
71
|
+
|
|
72
|
+
Add:
|
|
73
|
+
```typescript
|
|
74
|
+
/** Federation session ID */
|
|
75
|
+
sessionId?: string;
|
|
76
|
+
/** Authenticated principal (when single-request auth succeeds) */
|
|
77
|
+
principal?: { id: string; issuer?: string; claims?: Record<string, unknown> };
|
|
78
|
+
/** Auth negotiation fallback (when auth not provided or failed recoverably) */
|
|
79
|
+
authRequired?: {
|
|
80
|
+
methods: string[];
|
|
81
|
+
challenge?: string;
|
|
82
|
+
required: boolean;
|
|
83
|
+
};
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 1.2: Update JSON Schema
|
|
87
|
+
|
|
88
|
+
**File**: `schema/schema.json`
|
|
89
|
+
|
|
90
|
+
- Extend `FederationConnectRequest` params (~line 1824) with `authContext`, `systemInfo`, `protocolVersion`, `exposure`
|
|
91
|
+
- Extend `FederationConnectResponse` result (~line 1859) with `sessionId`, `principal`, `authRequired`
|
|
92
|
+
- Widen `auth.method` enum to include `"none"`, `"oauth2"`, and `x-` pattern
|
|
93
|
+
|
|
94
|
+
### Step 1.3: Extend Server Peer Connection Type
|
|
95
|
+
|
|
96
|
+
**File**: `ts-sdk/src/server/types.ts`
|
|
97
|
+
|
|
98
|
+
Extend `PeerConnection` (~line 1076) with:
|
|
99
|
+
```typescript
|
|
100
|
+
principal?: { id: string; issuer?: string; claims?: Record<string, unknown> };
|
|
101
|
+
sessionId?: string;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Step 1.4: Implement Single-Request Auth in Federation Handler
|
|
105
|
+
|
|
106
|
+
**File**: `ts-sdk/src/server/federation/handlers.ts`
|
|
107
|
+
|
|
108
|
+
Modify the `"map/federation/connect"` handler (~line 74):
|
|
109
|
+
|
|
110
|
+
1. Accept optional `authManager` in `FederationHandlerOptions`:
|
|
111
|
+
```typescript
|
|
112
|
+
export interface FederationHandlerOptions {
|
|
113
|
+
gateway: FederationGateway;
|
|
114
|
+
authManager?: AuthManager;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
2. In the handler body:
|
|
119
|
+
- If `params.auth` provided AND `authManager` exists → attempt immediate auth via `authManager.authenticate()`
|
|
120
|
+
- On success → return `{ connected: true, sessionId, principal, systemInfo }`
|
|
121
|
+
- On recoverable failure → return `{ connected: false, authRequired: { methods, challenge } }`
|
|
122
|
+
- If no auth provided AND `authManager.config.required` → return `authRequired`
|
|
123
|
+
- If no auth required → proceed with existing behavior (backwards-compatible)
|
|
124
|
+
|
|
125
|
+
### Step 1.5: Update Gateway Client Connection
|
|
126
|
+
|
|
127
|
+
**File**: `ts-sdk/src/connection/gateway.ts`
|
|
128
|
+
|
|
129
|
+
Modify `connectToSystem` (~line 257):
|
|
130
|
+
- Accept `authContext` in options
|
|
131
|
+
- Pass `authContext` through in request params
|
|
132
|
+
- Handle `authRequired` response variant (don't add to `#connectedSystems` if not connected)
|
|
133
|
+
- Widen `#lastConnectOptions.auth` type to `FederationAuth`
|
|
134
|
+
|
|
135
|
+
### Step 1.6: Add Challenge Nonce Utility
|
|
136
|
+
|
|
137
|
+
**New file**: `ts-sdk/src/federation/challenge.ts`
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
export function generateFederationChallenge(): string;
|
|
141
|
+
export function validateChallengeAge(challenge: string, maxAgeMs: number): boolean;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Simple utility — generates cryptographically random nonce with embedded timestamp for age validation.
|
|
145
|
+
|
|
146
|
+
### Step 1.7: Tests
|
|
147
|
+
|
|
148
|
+
**New file**: `ts-sdk/src/__tests__/federation-single-request-auth.test.ts`
|
|
149
|
+
|
|
150
|
+
| Test Case | What It Verifies |
|
|
151
|
+
|-----------|-----------------|
|
|
152
|
+
| Auth provided in connect → immediate success | Single-RTT happy path |
|
|
153
|
+
| Auth required but not provided → authRequired response | Negotiation fallback |
|
|
154
|
+
| Auth provided but fails → authRequired with challenge | Recoverable failure |
|
|
155
|
+
| Auth provided, hard failure → error thrown | Non-recoverable failure |
|
|
156
|
+
| No auth required, no auth provided → connects | Backwards compatibility |
|
|
157
|
+
|
|
158
|
+
### Step 1.8: Documentation
|
|
159
|
+
|
|
160
|
+
- `docs/07-federation.md`: Add "Single-Request Authentication" section after "Connection Establishment"
|
|
161
|
+
- `docs/09-authentication.md`: Add "Federation Authentication Optimization" section
|
|
162
|
+
- `schema/meta.json`: Update `map/federation/connect` description
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Phase 2: Linked Capability Documents (P3) ✅
|
|
167
|
+
|
|
168
|
+
### Goal
|
|
169
|
+
|
|
170
|
+
Add an optional `MAPAgentCapabilityDescriptor` that agents publish at registration time, and extend `map/agents/list` to filter by capability ID, tags, and content type.
|
|
171
|
+
|
|
172
|
+
### Step 2.1: Define Capability Descriptor Types
|
|
173
|
+
|
|
174
|
+
**File**: `ts-sdk/src/types/index.ts`
|
|
175
|
+
|
|
176
|
+
Add after `AgentEnvironment` (~line 131):
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
export interface MAPAgentCapabilityDescriptor {
|
|
180
|
+
version: 1;
|
|
181
|
+
description: string;
|
|
182
|
+
capabilities: MAPCapabilityDeclaration[];
|
|
183
|
+
accepts?: MAPInterfaceSpec[];
|
|
184
|
+
produces?: MAPInterfaceSpec[];
|
|
185
|
+
documentationUrl?: string;
|
|
186
|
+
tags?: string[];
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export interface MAPCapabilityDeclaration {
|
|
190
|
+
id: string; // e.g., "doc:summarize"
|
|
191
|
+
name: string; // e.g., "Document Summarization"
|
|
192
|
+
description: string;
|
|
193
|
+
interfaceRef?: string; // URL to detailed spec
|
|
194
|
+
interface?: MAPInterfaceSpec; // Inline spec
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface MAPInterfaceSpec {
|
|
198
|
+
contentType: string; // e.g., "application/json"
|
|
199
|
+
schema?: Record<string, unknown>; // JSON Schema
|
|
200
|
+
schemaRef?: string; // URL to external schema
|
|
201
|
+
example?: unknown;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Step 2.2: Extend Agent Model
|
|
206
|
+
|
|
207
|
+
**File**: `ts-sdk/src/types/index.ts`
|
|
208
|
+
|
|
209
|
+
Add to `Agent` interface (~line 352, after `environment`):
|
|
210
|
+
```typescript
|
|
211
|
+
capabilityDescriptor?: MAPAgentCapabilityDescriptor;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Add to `AgentsRegisterRequestParams` (~line 1391):
|
|
215
|
+
```typescript
|
|
216
|
+
capabilityDescriptor?: MAPAgentCapabilityDescriptor;
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Add to `AgentsSpawnRequestParams` (~line 1458):
|
|
220
|
+
```typescript
|
|
221
|
+
capabilityDescriptor?: MAPAgentCapabilityDescriptor;
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Step 2.3: Extend List Filters
|
|
225
|
+
|
|
226
|
+
**File**: `ts-sdk/src/types/index.ts`
|
|
227
|
+
|
|
228
|
+
Add to `AgentsListFilter` (~line 1333):
|
|
229
|
+
```typescript
|
|
230
|
+
capabilityId?: string; // Filter by capability declaration ID
|
|
231
|
+
tags?: string[]; // Filter by semantic tags
|
|
232
|
+
accepts?: string; // Filter by accepted content type
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Step 2.4: Extend Server Types
|
|
236
|
+
|
|
237
|
+
**File**: `ts-sdk/src/server/types.ts`
|
|
238
|
+
|
|
239
|
+
Add to `RegisteredAgent` (~line 142):
|
|
240
|
+
```typescript
|
|
241
|
+
capabilityDescriptor?: import('../types').MAPAgentCapabilityDescriptor;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Add to `AgentFilter` (~line 170):
|
|
245
|
+
```typescript
|
|
246
|
+
capabilityId?: string;
|
|
247
|
+
tag?: string;
|
|
248
|
+
accepts?: string;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Extend `AgentRegistry.register()` signature (~line 211) to accept `capabilityDescriptor`.
|
|
252
|
+
|
|
253
|
+
### Step 2.5: Implement Storage Filtering
|
|
254
|
+
|
|
255
|
+
**File**: `ts-sdk/src/server/agents/stores/in-memory.ts`
|
|
256
|
+
|
|
257
|
+
Extend `list()` method (~line 36) with three new filter branches:
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
// After existing role/state/scope filters:
|
|
261
|
+
|
|
262
|
+
if (filter?.capabilityId) {
|
|
263
|
+
const match = agent.capabilityDescriptor?.capabilities?.some(
|
|
264
|
+
(cap) => cap.id === filter.capabilityId
|
|
265
|
+
);
|
|
266
|
+
if (!match) continue;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (filter?.tag) {
|
|
270
|
+
if (!agent.capabilityDescriptor?.tags?.includes(filter.tag)) continue;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (filter?.accepts) {
|
|
274
|
+
const match = agent.capabilityDescriptor?.accepts?.some(
|
|
275
|
+
(spec) => spec.contentType === filter.accepts
|
|
276
|
+
);
|
|
277
|
+
if (!match) continue;
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Step 2.6: Implement Handler Changes
|
|
282
|
+
|
|
283
|
+
**File**: `ts-sdk/src/server/agents/handlers.ts`
|
|
284
|
+
|
|
285
|
+
**A.** Extend `RegisterParams` (~line 45) with `capabilityDescriptor`
|
|
286
|
+
|
|
287
|
+
**B.** Modify `"map/agents/register"` handler (~line 139):
|
|
288
|
+
- Pass `capabilityDescriptor` to `agents.register()`
|
|
289
|
+
- Include `capabilityDescriptor` in response
|
|
290
|
+
|
|
291
|
+
**C.** Extend `ListParams` (~line 70) with `capabilityId`, `tags`, `accepts`
|
|
292
|
+
|
|
293
|
+
**D.** Modify `"map/agents/list"` handler (~line 194):
|
|
294
|
+
- Map new filter params to `AgentFilter`
|
|
295
|
+
- Include `capabilityDescriptor` in response objects
|
|
296
|
+
|
|
297
|
+
**E.** Modify `"map/agents/get"` handler (~line 212):
|
|
298
|
+
- Include `capabilityDescriptor` in response
|
|
299
|
+
|
|
300
|
+
### Step 2.7: Update Agent Registry
|
|
301
|
+
|
|
302
|
+
**File**: `ts-sdk/src/server/agents/registry.ts`
|
|
303
|
+
|
|
304
|
+
Modify `register()` (~line 215):
|
|
305
|
+
- Accept `capabilityDescriptor` in params
|
|
306
|
+
- Store it on the `RegisteredAgent` object
|
|
307
|
+
|
|
308
|
+
### Step 2.8: Update Federation Agent Decorator
|
|
309
|
+
|
|
310
|
+
**File**: `ts-sdk/src/server/federation/decorators/agents.ts`
|
|
311
|
+
|
|
312
|
+
- Update `register()` (~line 75) to accept and pass through `capabilityDescriptor`
|
|
313
|
+
- Update `broadcastAgentEvent()` (~line 228) to include `capabilityDescriptor` in broadcast payload
|
|
314
|
+
|
|
315
|
+
### Step 2.9: Update JSON Schema
|
|
316
|
+
|
|
317
|
+
**File**: `schema/schema.json`
|
|
318
|
+
|
|
319
|
+
**A.** Add to `$defs`:
|
|
320
|
+
- `MAPAgentCapabilityDescriptor` (required: `version`, `description`, `capabilities`)
|
|
321
|
+
- `MAPCapabilityDeclaration` (required: `id`, `name`, `description`)
|
|
322
|
+
- `MAPInterfaceSpec` (required: `contentType`)
|
|
323
|
+
|
|
324
|
+
**B.** Add `capabilityDescriptor` property to:
|
|
325
|
+
- `Agent` definition (~line 408)
|
|
326
|
+
- `AgentsRegisterRequest` params (~line 1252)
|
|
327
|
+
- `AgentsSpawnRequest` params (~line 1292)
|
|
328
|
+
|
|
329
|
+
**C.** Add filter properties to `AgentsListRequest` params (~line 1040):
|
|
330
|
+
- `capabilityId`: string
|
|
331
|
+
- `tags`: array of strings
|
|
332
|
+
- `accepts`: string
|
|
333
|
+
|
|
334
|
+
### Step 2.10: Tests
|
|
335
|
+
|
|
336
|
+
**New file**: `ts-sdk/src/__tests__/capability-descriptor.test.ts`
|
|
337
|
+
|
|
338
|
+
| Test Case | What It Verifies |
|
|
339
|
+
|-----------|-----------------|
|
|
340
|
+
| Register agent with capabilityDescriptor | Stored and returned correctly |
|
|
341
|
+
| Get agent includes capabilityDescriptor | Field appears in get response |
|
|
342
|
+
| List filter by capabilityId | Only matching agents returned |
|
|
343
|
+
| List filter by tag | Only matching agents returned |
|
|
344
|
+
| List filter by accepts content type | Only matching agents returned |
|
|
345
|
+
| Combined filters (capabilityId + role) | Intersection semantics |
|
|
346
|
+
| Agent without capabilityDescriptor | Still registers and lists normally |
|
|
347
|
+
| Spawn with capabilityDescriptor | Descriptor propagated to spawned agent |
|
|
348
|
+
|
|
349
|
+
**Modify**: `ts-sdk/src/__tests__/server-agents.test.ts` — add cases for new filter fields in `InMemoryAgentStore.list()`
|
|
350
|
+
|
|
351
|
+
### Step 2.11: Documentation
|
|
352
|
+
|
|
353
|
+
- `docs/00-design-specification.md`: Add capability descriptor to Agent model section, show example registration
|
|
354
|
+
- Reference the design spec (`docs/11-anp-inspired-improvements.md`) for the full rationale
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Phase 3: `did:wba` Decentralized Identity (P1) ✅
|
|
359
|
+
|
|
360
|
+
### Goal
|
|
361
|
+
|
|
362
|
+
Add `did:wba` as a federation authentication method. DID resolution via HTTPS provides domain-verified identity without pre-shared secrets.
|
|
363
|
+
|
|
364
|
+
### Step 3.1: Add `did:wba` to Auth Types
|
|
365
|
+
|
|
366
|
+
**File**: `ts-sdk/src/types/index.ts`
|
|
367
|
+
|
|
368
|
+
**A.** Extend `StandardAuthMethod` (~line 1108):
|
|
369
|
+
```typescript
|
|
370
|
+
export type StandardAuthMethod = "bearer" | "api-key" | "mtls" | "none" | "did:wba";
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**B.** Add after `FederationAuth` (~line 1196):
|
|
374
|
+
```typescript
|
|
375
|
+
export interface DIDWBACredentials {
|
|
376
|
+
method: "did:wba";
|
|
377
|
+
did: string;
|
|
378
|
+
proof: DIDWBAProof;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export interface DIDWBAProof {
|
|
382
|
+
type: string; // e.g., "JsonWebSignature2020"
|
|
383
|
+
created: string; // ISO 8601
|
|
384
|
+
challenge: string; // Server-provided nonce
|
|
385
|
+
jws: string; // Signature over (challenge + did + created)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export interface DIDDocument {
|
|
389
|
+
"@context": string[];
|
|
390
|
+
id: string;
|
|
391
|
+
verificationMethod?: DIDVerificationMethod[];
|
|
392
|
+
authentication?: string[];
|
|
393
|
+
service?: DIDService[];
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export interface DIDVerificationMethod {
|
|
397
|
+
id: string;
|
|
398
|
+
type: string;
|
|
399
|
+
controller: string;
|
|
400
|
+
publicKeyJwk?: Record<string, unknown>;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export interface DIDService {
|
|
404
|
+
id: string;
|
|
405
|
+
type: string;
|
|
406
|
+
serviceEndpoint: string;
|
|
407
|
+
mapProtocolVersion?: number;
|
|
408
|
+
mapCapabilities?: Record<string, boolean>;
|
|
409
|
+
}
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**C.** Create union type:
|
|
413
|
+
```typescript
|
|
414
|
+
export type MAPFederationAuth = FederationAuth | DIDWBACredentials;
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Update `FederationConnectRequestParams.auth` type from `FederationAuth` to `MAPFederationAuth`.
|
|
418
|
+
|
|
419
|
+
**D.** Add error codes to `FEDERATION_ERROR_CODES` (~line 3093):
|
|
420
|
+
```typescript
|
|
421
|
+
FEDERATION_DID_RESOLUTION_FAILED: 5004,
|
|
422
|
+
FEDERATION_DID_PROOF_INVALID: 5005,
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Step 3.2: Create DID Resolver Module
|
|
426
|
+
|
|
427
|
+
**New file**: `ts-sdk/src/federation/did-wba/resolver.ts`
|
|
428
|
+
|
|
429
|
+
```
|
|
430
|
+
Class: DIDWBAResolver
|
|
431
|
+
- constructor(options?: { cacheTtlMs?, timeoutMs?, fetch? })
|
|
432
|
+
- resolve(did: string): Promise<DIDDocument>
|
|
433
|
+
- extractMAPEndpoint(doc: DIDDocument): string | undefined
|
|
434
|
+
- extractVerificationKeys(doc: DIDDocument): DIDVerificationMethod[]
|
|
435
|
+
- clearCache(): void
|
|
436
|
+
|
|
437
|
+
Functions:
|
|
438
|
+
- parseDIDWBA(did) → { domain, path }
|
|
439
|
+
- didToUrl(did) → HTTPS URL for DID document
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Resolution logic:
|
|
443
|
+
```
|
|
444
|
+
did:wba:agents.example.com:worker-alpha
|
|
445
|
+
→ parse → domain: "agents.example.com", path: "worker-alpha"
|
|
446
|
+
→ construct → https://agents.example.com/worker-alpha/did.json
|
|
447
|
+
→ fetch → DIDDocument
|
|
448
|
+
→ cache with TTL
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
### Step 3.3: Create Proof Generation/Verification Module
|
|
452
|
+
|
|
453
|
+
**New file**: `ts-sdk/src/federation/did-wba/proof.ts`
|
|
454
|
+
|
|
455
|
+
```
|
|
456
|
+
Functions:
|
|
457
|
+
- generateDIDWBAProof({ did, challenge, privateKey, proofType? }) → DIDWBAProof
|
|
458
|
+
- verifyDIDWBAProof({ did, proof, publicKey, maxAgeMs? }) → Promise<boolean>
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
Proof generation:
|
|
462
|
+
1. Construct payload: `JSON.stringify({ did, challenge, created })`
|
|
463
|
+
2. Sign with private key (JWS compact serialization)
|
|
464
|
+
3. Return `{ type, created, challenge, jws }`
|
|
465
|
+
|
|
466
|
+
Proof verification:
|
|
467
|
+
1. Check proof age (`created` within `maxAgeMs`)
|
|
468
|
+
2. Reconstruct payload from proof fields
|
|
469
|
+
3. Verify JWS against public key
|
|
470
|
+
4. Return boolean
|
|
471
|
+
|
|
472
|
+
### Step 3.4: Create Module Index
|
|
473
|
+
|
|
474
|
+
**New file**: `ts-sdk/src/federation/did-wba/index.ts`
|
|
475
|
+
|
|
476
|
+
Re-exports `DIDWBAResolver`, `parseDIDWBA`, `didToUrl`, `generateDIDWBAProof`, `verifyDIDWBAProof`.
|
|
477
|
+
|
|
478
|
+
### Step 3.5: Create Server-Side Authenticator
|
|
479
|
+
|
|
480
|
+
**New file**: `ts-sdk/src/server/auth/did-wba-authenticator.ts`
|
|
481
|
+
|
|
482
|
+
```
|
|
483
|
+
Class: DIDWBAAuthenticator implements Authenticator
|
|
484
|
+
- methods: ['did:wba']
|
|
485
|
+
- constructor(options?: { resolver?, trustedDomains?, challengeTtlMs? })
|
|
486
|
+
- authenticate(credentials, context): Promise<AuthResult>
|
|
487
|
+
- generateChallenge(): string
|
|
488
|
+
- isTrustedDomain(did): boolean
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Authentication flow:
|
|
492
|
+
1. Parse DID from credentials
|
|
493
|
+
2. Validate DID matches trusted domain patterns (if configured)
|
|
494
|
+
3. Resolve DID document via `DIDWBAResolver`
|
|
495
|
+
4. Extract verification key matching `authentication` relationship
|
|
496
|
+
5. Verify proof via `verifyDIDWBAProof()`
|
|
497
|
+
6. Return `AuthResult` with principal `{ id: did, issuer: domain }`
|
|
498
|
+
|
|
499
|
+
### Step 3.6: Wire Up Exports
|
|
500
|
+
|
|
501
|
+
**File**: `ts-sdk/src/server/auth/index.ts`
|
|
502
|
+
- Export `DIDWBAAuthenticator` and `DIDWBAAuthenticatorOptions`
|
|
503
|
+
|
|
504
|
+
**File**: `ts-sdk/src/federation/index.ts`
|
|
505
|
+
- Export `DIDWBAResolver`, `parseDIDWBA`, `didToUrl`, `generateDIDWBAProof`, `verifyDIDWBAProof`
|
|
506
|
+
|
|
507
|
+
### Step 3.7: Extend Auth Context
|
|
508
|
+
|
|
509
|
+
**File**: `ts-sdk/src/server/auth/types.ts`
|
|
510
|
+
|
|
511
|
+
Add to `AuthContext` (~line 20):
|
|
512
|
+
```typescript
|
|
513
|
+
didInfo?: { did: string; domain: string };
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
### Step 3.8: Update JSON Schema
|
|
517
|
+
|
|
518
|
+
**File**: `schema/schema.json`
|
|
519
|
+
|
|
520
|
+
**A.** Add `DIDWBACredentials` definition:
|
|
521
|
+
```json
|
|
522
|
+
{
|
|
523
|
+
"type": "object",
|
|
524
|
+
"properties": {
|
|
525
|
+
"method": { "const": "did:wba" },
|
|
526
|
+
"did": { "type": "string", "pattern": "^did:wba:" },
|
|
527
|
+
"proof": {
|
|
528
|
+
"type": "object",
|
|
529
|
+
"properties": {
|
|
530
|
+
"type": { "type": "string" },
|
|
531
|
+
"created": { "type": "string", "format": "date-time" },
|
|
532
|
+
"challenge": { "type": "string" },
|
|
533
|
+
"jws": { "type": "string" }
|
|
534
|
+
},
|
|
535
|
+
"required": ["type", "created", "challenge", "jws"]
|
|
536
|
+
}
|
|
537
|
+
},
|
|
538
|
+
"required": ["method", "did", "proof"]
|
|
539
|
+
}
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
**B.** Update `FederationConnectRequest.params.auth` to `oneOf` including existing auth + `DIDWBACredentials`
|
|
543
|
+
|
|
544
|
+
**C.** Add error codes 5004, 5005 to error definitions
|
|
545
|
+
|
|
546
|
+
**File**: `schema/meta.json`
|
|
547
|
+
- Add `DID_RESOLUTION_FAILED` (5004) and `DID_PROOF_INVALID` (5005) to federation errors
|
|
548
|
+
|
|
549
|
+
### Step 3.9: Tests
|
|
550
|
+
|
|
551
|
+
**New file**: `ts-sdk/src/__tests__/did-wba-resolver.test.ts`
|
|
552
|
+
|
|
553
|
+
| Test Case | What It Verifies |
|
|
554
|
+
|-----------|-----------------|
|
|
555
|
+
| Parse valid `did:wba` string | Extracts domain + path correctly |
|
|
556
|
+
| Parse multi-segment path | `did:wba:example.com:agents:worker` works |
|
|
557
|
+
| Convert DID to URL | Correct HTTPS URL constructed |
|
|
558
|
+
| Resolve DID document (mocked fetch) | Document returned, cache populated |
|
|
559
|
+
| Cache hit on second resolve | No second fetch |
|
|
560
|
+
| Cache expiry | Fetches again after TTL |
|
|
561
|
+
| Invalid DID format → error | Throws on `did:key:...` |
|
|
562
|
+
| Network failure → error | Throws descriptive error |
|
|
563
|
+
| Extract MAP service endpoint | Finds `MAPFederationEndpoint` service |
|
|
564
|
+
| Extract verification keys | Returns keys from `authentication` relationship |
|
|
565
|
+
|
|
566
|
+
**New file**: `ts-sdk/src/__tests__/did-wba-proof.test.ts`
|
|
567
|
+
|
|
568
|
+
| Test Case | What It Verifies |
|
|
569
|
+
|-----------|-----------------|
|
|
570
|
+
| Generate proof with EC P-256 key | Valid JWS produced |
|
|
571
|
+
| Verify valid proof | Returns true |
|
|
572
|
+
| Reject expired proof (old `created`) | Returns false |
|
|
573
|
+
| Reject wrong challenge | Returns false |
|
|
574
|
+
| Reject wrong signing key | Returns false |
|
|
575
|
+
| Reject tampered payload | Returns false |
|
|
576
|
+
|
|
577
|
+
**New file**: `ts-sdk/src/__tests__/did-wba-authenticator.test.ts`
|
|
578
|
+
|
|
579
|
+
| Test Case | What It Verifies |
|
|
580
|
+
|-----------|-----------------|
|
|
581
|
+
| Full happy path (resolve → verify → principal) | End-to-end auth works |
|
|
582
|
+
| Trusted domain filtering | Rejects untrusted domains |
|
|
583
|
+
| Challenge nonce generation | Unique, time-embedded |
|
|
584
|
+
| Stale challenge rejection | Rejects old challenges |
|
|
585
|
+
| DID resolution failure → AuthResult.success=false | Graceful failure |
|
|
586
|
+
| Proof verification failure → AuthResult.success=false | Graceful failure |
|
|
587
|
+
| Integration with AuthManager | Works when registered as authenticator |
|
|
588
|
+
|
|
589
|
+
### Step 3.10: Documentation
|
|
590
|
+
|
|
591
|
+
- `docs/09-authentication.md`:
|
|
592
|
+
- Add `did:wba` row to methods table
|
|
593
|
+
- Add "DID:WBA Authentication" section with resolution flow, proof format, trust model
|
|
594
|
+
- Add DID Document example with MAP service endpoint
|
|
595
|
+
- Add "Trust Model Comparison" table (mtls vs bearer vs api-key vs did:wba)
|
|
596
|
+
|
|
597
|
+
- `docs/07-federation.md`:
|
|
598
|
+
- Add `did:wba` to `MAPFederationAuth` type
|
|
599
|
+
- Add "DID-Based Federation Identity" section
|
|
600
|
+
- Add example federation connect with `did:wba` credentials
|
|
601
|
+
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## File Change Summary
|
|
605
|
+
|
|
606
|
+
### New Files (7)
|
|
607
|
+
|
|
608
|
+
| File | Phase | Purpose |
|
|
609
|
+
|------|-------|---------|
|
|
610
|
+
| `ts-sdk/src/federation/challenge.ts` | P6 | Challenge nonce utility |
|
|
611
|
+
| `ts-sdk/src/federation/did-wba/index.ts` | P1 | Module re-exports |
|
|
612
|
+
| `ts-sdk/src/federation/did-wba/resolver.ts` | P1 | DID document resolution + cache |
|
|
613
|
+
| `ts-sdk/src/federation/did-wba/proof.ts` | P1 | Proof generation + verification |
|
|
614
|
+
| `ts-sdk/src/server/auth/did-wba-authenticator.ts` | P1 | Server-side DID authenticator |
|
|
615
|
+
| `ts-sdk/src/__tests__/federation-single-request-auth.test.ts` | P6 | Single-request auth tests |
|
|
616
|
+
| `ts-sdk/src/__tests__/capability-descriptor.test.ts` | P3 | Capability descriptor tests |
|
|
617
|
+
| `ts-sdk/src/__tests__/did-wba-resolver.test.ts` | P1 | DID resolver tests |
|
|
618
|
+
| `ts-sdk/src/__tests__/did-wba-proof.test.ts` | P1 | Proof gen/verify tests |
|
|
619
|
+
| `ts-sdk/src/__tests__/did-wba-authenticator.test.ts` | P1 | Authenticator integration tests |
|
|
620
|
+
|
|
621
|
+
### Modified Files (15)
|
|
622
|
+
|
|
623
|
+
| File | Phases | Changes |
|
|
624
|
+
|------|--------|---------|
|
|
625
|
+
| `ts-sdk/src/types/index.ts` | P6, P3, P1 | Widen `FederationAuth`, add capability descriptor types, add DID types, extend agent/filter/register interfaces |
|
|
626
|
+
| `ts-sdk/src/server/types.ts` | P6, P3 | Extend `PeerConnection`, `RegisteredAgent`, `AgentFilter`, `AgentRegistry` |
|
|
627
|
+
| `ts-sdk/src/server/federation/handlers.ts` | P6 | Add `authManager` option, single-request auth logic |
|
|
628
|
+
| `ts-sdk/src/server/federation/gateway.ts` | P6 | Store principal/session on peer connections |
|
|
629
|
+
| `ts-sdk/src/connection/gateway.ts` | P6 | Handle `authContext`, `authRequired` response |
|
|
630
|
+
| `ts-sdk/src/server/agents/registry.ts` | P3 | Accept/store `capabilityDescriptor` |
|
|
631
|
+
| `ts-sdk/src/server/agents/stores/in-memory.ts` | P3 | Filter by capabilityId, tag, accepts |
|
|
632
|
+
| `ts-sdk/src/server/agents/handlers.ts` | P3 | Pass through descriptor in register/list/get |
|
|
633
|
+
| `ts-sdk/src/server/federation/decorators/agents.ts` | P3 | Include descriptor in federation broadcasts |
|
|
634
|
+
| `ts-sdk/src/server/auth/index.ts` | P1 | Export `DIDWBAAuthenticator` |
|
|
635
|
+
| `ts-sdk/src/server/auth/types.ts` | P1 | Add `didInfo` to `AuthContext` |
|
|
636
|
+
| `ts-sdk/src/federation/index.ts` | P1 | Re-export DID utilities |
|
|
637
|
+
| `schema/schema.json` | P6, P3, P1 | Federation auth schemas, capability descriptor schemas, DID credential schema |
|
|
638
|
+
| `schema/meta.json` | P6, P1 | Update federation/connect description, add DID error codes |
|
|
639
|
+
| `docs/07-federation.md` | P6, P1 | Single-request auth section, DID federation section |
|
|
640
|
+
| `docs/09-authentication.md` | P6, P1 | Federation auth optimization, did:wba method |
|
|
641
|
+
| `docs/00-design-specification.md` | P3 | Capability descriptor in agent model |
|