@kya-os/mcp-i 1.5.3-canary.0 → 1.5.4
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/dist/auth/jwt.d.ts +1 -1
- package/dist/auth/oauth/router.js +3 -8
- package/dist/cli-adapter/index.js +1 -1
- package/dist/cli-adapter/kta-registration.d.ts +1 -1
- package/dist/cli-adapter/kta-registration.js +2 -2
- package/dist/compiler/config/injection.js +2 -2
- package/dist/compiler/get-webpack-config/get-entries.js +12 -8
- package/dist/providers/node-providers.d.ts +1 -1
- package/dist/providers/node-providers.js +4 -4
- package/dist/runtime/adapter-express.js +1 -1
- package/dist/runtime/adapter-nextjs.js +1 -1
- package/dist/runtime/audit.d.ts +287 -3
- package/dist/runtime/audit.js +169 -4
- package/dist/runtime/auth-handshake.d.ts +1 -1
- package/dist/runtime/auth-handshake.js +1 -1
- package/dist/runtime/debug.d.ts +2 -2
- package/dist/runtime/debug.js +3 -3
- package/dist/runtime/delegation/index.d.ts +7 -0
- package/dist/runtime/delegation/index.js +23 -0
- package/dist/runtime/delegation/vc-issuer.d.ts +119 -0
- package/dist/runtime/delegation/vc-issuer.js +220 -0
- package/dist/runtime/delegation/vc-verifier.d.ts +193 -0
- package/dist/runtime/delegation/vc-verifier.js +387 -0
- package/dist/runtime/http.js +1 -1
- package/dist/runtime/identity.d.ts +10 -2
- package/dist/runtime/identity.js +68 -11
- package/dist/runtime/mcpi-runtime.d.ts +28 -1
- package/dist/runtime/mcpi-runtime.js +2 -2
- package/dist/runtime/migrate-identity.d.ts +16 -0
- package/dist/runtime/migrate-identity.js +118 -0
- package/dist/runtime/proof.js +2 -2
- package/dist/runtime/stdio.js +1 -1
- package/dist/runtime/transports/http/index.js +3 -1
- package/dist/runtime/utils/time.d.ts +80 -0
- package/dist/runtime/utils/time.js +117 -0
- package/dist/runtime/utils/tools.js +22 -3
- package/dist/runtime/verifier-middleware.js +1 -1
- package/dist/runtime/well-known.d.ts +0 -4
- package/dist/runtime/well-known.js +12 -26
- package/dist/storage/delegation.js +2 -2
- package/dist/test/deterministic-keys.d.ts +1 -1
- package/dist/test/deterministic-keys.js +6 -6
- package/dist/test/examples/test-usage-example.d.ts +6 -6
- package/dist/test/examples/test-usage-example.js +5 -5
- package/dist/test/local-verification.d.ts +1 -1
- package/dist/test/local-verification.js +10 -10
- package/dist/test/mock-identity-provider.d.ts +4 -4
- package/dist/test/mock-identity-provider.js +7 -7
- package/dist/test/runtime-integration.d.ts +2 -2
- package/package.json +4 -3
package/dist/runtime/audit.d.ts
CHANGED
|
@@ -1,41 +1,294 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Audit Logging System for
|
|
2
|
+
* Audit Logging System for MCP-I Runtime
|
|
3
3
|
*
|
|
4
4
|
* Handles audit record generation and logging with frozen format
|
|
5
|
-
* according to requirements 5.4, 5.5.
|
|
5
|
+
* according to requirements 5.4, 5.5, and 6.7 (configurable retention).
|
|
6
|
+
*
|
|
7
|
+
* ## Privacy Guarantees
|
|
8
|
+
*
|
|
9
|
+
* This system is designed with privacy-first principles:
|
|
10
|
+
*
|
|
11
|
+
* **NEVER Logged:**
|
|
12
|
+
* - Request/response bodies (only SHA-256 hashes)
|
|
13
|
+
* - Secrets (private keys, API keys, tokens, nonces)
|
|
14
|
+
* - PII (names, emails, addresses, phone numbers)
|
|
15
|
+
* - Key material (only key IDs are logged)
|
|
16
|
+
*
|
|
17
|
+
* **ALWAYS Logged:**
|
|
18
|
+
* - Metadata only (DIDs, key IDs, timestamps)
|
|
19
|
+
* - SHA-256 hashes (not plaintext data)
|
|
20
|
+
* - Session IDs (for correlation, not sensitive)
|
|
21
|
+
* - Verification results (yes/no only)
|
|
22
|
+
* - Scope identifiers (application-level)
|
|
23
|
+
*
|
|
24
|
+
* ## Frozen Format
|
|
25
|
+
*
|
|
26
|
+
* The audit.v1 format is **frozen** and will not change:
|
|
27
|
+
*
|
|
28
|
+
* ```
|
|
29
|
+
* audit.v1 ts=<unix> session=<id> audience=<host> did=<did> kid=<kid> reqHash=<sha256:..> resHash=<sha256:..> verified=yes|no scope=<scopeId|->
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* ## Event-Driven Rotation
|
|
33
|
+
*
|
|
34
|
+
* This implementation uses **event-driven rotation** (not timers):
|
|
35
|
+
* - Rotation checks happen on each `logAuditRecord()` call
|
|
36
|
+
* - No setInterval/setTimeout (works in Cloudflare Workers)
|
|
37
|
+
* - No cleanup needed (no timers to clear)
|
|
38
|
+
* - Predictable behavior (rotation on activity)
|
|
39
|
+
*
|
|
40
|
+
* @module audit
|
|
41
|
+
* @see {@link https://github.com/kya-os/xmcp-i/docs/concepts/audit-logging.md | Audit Logging Documentation}
|
|
6
42
|
*/
|
|
7
43
|
import { AuditRecord } from "@kya-os/contracts/proof";
|
|
8
44
|
import { SessionContext } from "@kya-os/contracts/handshake";
|
|
9
45
|
import { AgentIdentity } from "./identity";
|
|
46
|
+
/**
|
|
47
|
+
* Audit log rotation strategy
|
|
48
|
+
*/
|
|
49
|
+
export type AuditRotationStrategy = "size" | "time" | "count" | "custom";
|
|
50
|
+
/**
|
|
51
|
+
* Audit rotation context passed to hooks
|
|
52
|
+
*/
|
|
53
|
+
export interface AuditRotationContext {
|
|
54
|
+
strategy: AuditRotationStrategy;
|
|
55
|
+
trigger: string;
|
|
56
|
+
recordsLogged: number;
|
|
57
|
+
timestamp: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Audit log rotation hooks
|
|
61
|
+
*/
|
|
62
|
+
export interface AuditRotationHooks {
|
|
63
|
+
/**
|
|
64
|
+
* Called when audit log should be rotated
|
|
65
|
+
* @param context - Rotation context with metadata
|
|
66
|
+
* @returns Promise that resolves when rotation is complete
|
|
67
|
+
*/
|
|
68
|
+
onRotation?: (context: AuditRotationContext) => Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Called when audit log reaches size limit
|
|
71
|
+
* @param sizeBytes - Current size in bytes
|
|
72
|
+
* @param limit - Size limit that was exceeded
|
|
73
|
+
*/
|
|
74
|
+
onSizeLimit?: (sizeBytes: number, limit: number) => Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* Called on time-based rotation (e.g., daily, hourly)
|
|
77
|
+
* @param interval - Rotation interval that triggered (e.g., "daily", "hourly")
|
|
78
|
+
*/
|
|
79
|
+
onTimeBased?: (interval: string) => Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Called when record count reaches threshold
|
|
82
|
+
* @param count - Number of records logged
|
|
83
|
+
* @param threshold - Count threshold that was exceeded
|
|
84
|
+
*/
|
|
85
|
+
onCountThreshold?: (count: number, threshold: number) => Promise<void>;
|
|
86
|
+
}
|
|
10
87
|
/**
|
|
11
88
|
* Audit logging configuration
|
|
89
|
+
*
|
|
90
|
+
* @example Basic configuration
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const config: AuditConfig = {
|
|
93
|
+
* enabled: true,
|
|
94
|
+
* logFunction: console.log,
|
|
95
|
+
* includePayloads: false, // ALWAYS false for privacy
|
|
96
|
+
* };
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example With rotation
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const config: AuditConfig = {
|
|
102
|
+
* enabled: true,
|
|
103
|
+
* rotation: {
|
|
104
|
+
* strategy: 'size',
|
|
105
|
+
* sizeLimit: 10 * 1024 * 1024, // 10 MB
|
|
106
|
+
* hooks: {
|
|
107
|
+
* onRotation: async (context) => {
|
|
108
|
+
* // Archive old log file
|
|
109
|
+
* },
|
|
110
|
+
* },
|
|
111
|
+
* },
|
|
112
|
+
* };
|
|
113
|
+
* ```
|
|
12
114
|
*/
|
|
13
115
|
export interface AuditConfig {
|
|
116
|
+
/**
|
|
117
|
+
* Enable audit logging (default: true)
|
|
118
|
+
*/
|
|
14
119
|
enabled?: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Custom log function (default: console.log)
|
|
122
|
+
*
|
|
123
|
+
* @param record - The formatted audit line (frozen format)
|
|
124
|
+
*
|
|
125
|
+
* @example File-based logging
|
|
126
|
+
* ```typescript
|
|
127
|
+
* logFunction: (record) => {
|
|
128
|
+
* fs.appendFileSync('/var/log/audit.log', record + '\n');
|
|
129
|
+
* }
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
15
132
|
logFunction?: (record: string) => void;
|
|
133
|
+
/**
|
|
134
|
+
* Include payloads in logs (default: false)
|
|
135
|
+
*
|
|
136
|
+
* **WARNING:** This should ALWAYS be false. The frozen format ensures
|
|
137
|
+
* that even if set to true, no request/response bodies will be logged.
|
|
138
|
+
* This flag exists for compatibility but has no effect on the frozen format.
|
|
139
|
+
*
|
|
140
|
+
* @deprecated This flag has no effect due to frozen format privacy guarantees
|
|
141
|
+
*/
|
|
16
142
|
includePayloads?: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* Log rotation configuration (event-driven, no timers)
|
|
145
|
+
*/
|
|
146
|
+
rotation?: {
|
|
147
|
+
/**
|
|
148
|
+
* Rotation strategy
|
|
149
|
+
* - 'size': Rotate when log reaches size limit (checked on each call)
|
|
150
|
+
* - 'time': Rotate based on elapsed time (checked on each call)
|
|
151
|
+
* - 'count': Rotate after N records
|
|
152
|
+
* - 'custom': Custom rotation logic via hooks only
|
|
153
|
+
*/
|
|
154
|
+
strategy?: AuditRotationStrategy;
|
|
155
|
+
/**
|
|
156
|
+
* Size limit in bytes (for size-based rotation)
|
|
157
|
+
*
|
|
158
|
+
* @example 10 MB limit
|
|
159
|
+
* ```typescript
|
|
160
|
+
* sizeLimit: 10 * 1024 * 1024
|
|
161
|
+
* ```
|
|
162
|
+
*/
|
|
163
|
+
sizeLimit?: number;
|
|
164
|
+
/**
|
|
165
|
+
* Time interval in milliseconds (for time-based rotation)
|
|
166
|
+
* Rotation is checked on each logAuditRecord() call, not via timer.
|
|
167
|
+
*
|
|
168
|
+
* @example Daily rotation
|
|
169
|
+
* ```typescript
|
|
170
|
+
* timeInterval: 24 * 60 * 60 * 1000
|
|
171
|
+
* ```
|
|
172
|
+
*/
|
|
173
|
+
timeInterval?: number;
|
|
174
|
+
/**
|
|
175
|
+
* Record count threshold (for count-based rotation)
|
|
176
|
+
*
|
|
177
|
+
* @example Rotate after 10k records
|
|
178
|
+
* ```typescript
|
|
179
|
+
* countThreshold: 10000
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
countThreshold?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Custom rotation hooks
|
|
185
|
+
*/
|
|
186
|
+
hooks?: AuditRotationHooks;
|
|
187
|
+
};
|
|
17
188
|
}
|
|
18
189
|
/**
|
|
19
190
|
* Audit context for logging
|
|
191
|
+
*
|
|
192
|
+
* Contains all metadata needed to generate an audit record.
|
|
193
|
+
*
|
|
194
|
+
* **Privacy Note:** Only metadata is extracted from these objects.
|
|
195
|
+
* The identity's private key, session's nonce, and other sensitive
|
|
196
|
+
* fields are NEVER included in the audit log.
|
|
197
|
+
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```typescript
|
|
200
|
+
* const context: AuditContext = {
|
|
201
|
+
* identity: {
|
|
202
|
+
* did: 'did:web:example.com:agents:agent-1',
|
|
203
|
+
* keyId: 'key-20241014-abc123',
|
|
204
|
+
* // ... private key NOT logged
|
|
205
|
+
* },
|
|
206
|
+
* session: {
|
|
207
|
+
* sessionId: 'sess_abc123',
|
|
208
|
+
* audience: 'example.com',
|
|
209
|
+
* // ... nonce NOT logged
|
|
210
|
+
* },
|
|
211
|
+
* requestHash: 'sha256:1234567890abcdef...',
|
|
212
|
+
* responseHash: 'sha256:fedcba0987654321...',
|
|
213
|
+
* verified: 'yes',
|
|
214
|
+
* scopeId: 'orders.create',
|
|
215
|
+
* };
|
|
216
|
+
* ```
|
|
20
217
|
*/
|
|
21
218
|
export interface AuditContext {
|
|
219
|
+
/**
|
|
220
|
+
* Agent identity
|
|
221
|
+
*
|
|
222
|
+
* Only `did` and `keyId` are logged. Private key is NEVER logged.
|
|
223
|
+
*/
|
|
22
224
|
identity: AgentIdentity;
|
|
225
|
+
/**
|
|
226
|
+
* Session context
|
|
227
|
+
*
|
|
228
|
+
* Only `sessionId` and `audience` are logged. Nonce is NEVER logged.
|
|
229
|
+
*/
|
|
23
230
|
session: SessionContext;
|
|
231
|
+
/**
|
|
232
|
+
* Request hash (SHA-256 with `sha256:` prefix)
|
|
233
|
+
*
|
|
234
|
+
* @example 'sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
|
|
235
|
+
*/
|
|
24
236
|
requestHash: string;
|
|
237
|
+
/**
|
|
238
|
+
* Response hash (SHA-256 with `sha256:` prefix)
|
|
239
|
+
*
|
|
240
|
+
* @example 'sha256:fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321'
|
|
241
|
+
*/
|
|
25
242
|
responseHash: string;
|
|
243
|
+
/**
|
|
244
|
+
* Verification result
|
|
245
|
+
*
|
|
246
|
+
* - 'yes': Proof was verified successfully
|
|
247
|
+
* - 'no': Proof verification failed
|
|
248
|
+
*/
|
|
26
249
|
verified: "yes" | "no";
|
|
250
|
+
/**
|
|
251
|
+
* Optional scope identifier
|
|
252
|
+
*
|
|
253
|
+
* Application-level scope (e.g., 'orders.create', 'users.read').
|
|
254
|
+
* If not provided, '-' is used in the audit log.
|
|
255
|
+
*
|
|
256
|
+
* @example 'orders.create'
|
|
257
|
+
*/
|
|
27
258
|
scopeId?: string;
|
|
28
259
|
}
|
|
29
260
|
/**
|
|
30
|
-
* Audit logger class
|
|
261
|
+
* Audit logger class with event-driven rotation support
|
|
262
|
+
*
|
|
263
|
+
* Privacy Guarantees:
|
|
264
|
+
* - NEVER logs request/response bodies (only SHA-256 hashes)
|
|
265
|
+
* - NEVER logs secrets (private keys, API keys, tokens, nonces)
|
|
266
|
+
* - NEVER logs PII (personal information)
|
|
267
|
+
* - Only logs metadata: DIDs, key IDs, timestamps, hashes, session IDs
|
|
268
|
+
* - Frozen format: audit.v1 with fixed field order
|
|
269
|
+
*
|
|
270
|
+
* Rotation Strategy:
|
|
271
|
+
* - Event-driven (no timers) - rotation checks on each logAuditRecord() call
|
|
272
|
+
* - Works in all environments (Node.js, Cloudflare Workers, Vercel Edge)
|
|
273
|
+
* - No cleanup needed (no timers to clear)
|
|
31
274
|
*/
|
|
32
275
|
export declare class AuditLogger {
|
|
33
276
|
private config;
|
|
34
277
|
private sessionAuditLog;
|
|
278
|
+
private totalRecordsLogged;
|
|
279
|
+
private currentLogSize;
|
|
280
|
+
private lastRotationTime;
|
|
281
|
+
private destroyed;
|
|
35
282
|
constructor(config?: AuditConfig);
|
|
36
283
|
/**
|
|
37
284
|
* Emit audit record on first call per session
|
|
38
285
|
* Requirements: 5.4, 5.5
|
|
286
|
+
*
|
|
287
|
+
* This method:
|
|
288
|
+
* 1. Checks if logger is destroyed
|
|
289
|
+
* 2. Logs audit record (first call per session)
|
|
290
|
+
* 3. Checks if rotation is needed (event-driven)
|
|
291
|
+
* 4. Triggers rotation hooks if threshold met
|
|
39
292
|
*/
|
|
40
293
|
logAuditRecord(context: AuditContext): Promise<void>;
|
|
41
294
|
/**
|
|
@@ -43,6 +296,34 @@ export declare class AuditLogger {
|
|
|
43
296
|
* Format: audit.v1 ts=<unix> session=<id> audience=<host> did=<did> kid=<kid> reqHash=<sha256:..> resHash=<sha256:..> verified=yes|no scope=<scopeId|->
|
|
44
297
|
*/
|
|
45
298
|
private formatAuditLine;
|
|
299
|
+
/**
|
|
300
|
+
* Check if rotation is needed and trigger if necessary (event-driven)
|
|
301
|
+
*
|
|
302
|
+
* This method is called on each logAuditRecord() call.
|
|
303
|
+
* No timers are used - rotation is checked based on current state.
|
|
304
|
+
*/
|
|
305
|
+
private checkRotation;
|
|
306
|
+
/**
|
|
307
|
+
* Rotate audit log now (manually triggered)
|
|
308
|
+
*
|
|
309
|
+
* This method:
|
|
310
|
+
* 1. Calls onRotation hook (for user to archive/rotate log file)
|
|
311
|
+
* 2. Resets rotation counters (size, time, count)
|
|
312
|
+
*
|
|
313
|
+
* @param trigger - Reason for rotation (e.g., "manual", "size-limit")
|
|
314
|
+
*/
|
|
315
|
+
rotateNow(trigger?: string): Promise<void>;
|
|
316
|
+
/**
|
|
317
|
+
* Destroy audit logger and cleanup resources
|
|
318
|
+
*
|
|
319
|
+
* This method:
|
|
320
|
+
* 1. Clears session tracking (memory)
|
|
321
|
+
* 2. Resets statistics
|
|
322
|
+
* 3. Marks logger as destroyed
|
|
323
|
+
*
|
|
324
|
+
* Note: No timers to clear (event-driven rotation)
|
|
325
|
+
*/
|
|
326
|
+
destroy(): void;
|
|
46
327
|
/**
|
|
47
328
|
* Clear session audit log (useful for testing)
|
|
48
329
|
*/
|
|
@@ -54,6 +335,9 @@ export declare class AuditLogger {
|
|
|
54
335
|
enabled: boolean;
|
|
55
336
|
sessionsLogged: number;
|
|
56
337
|
includePayloads: boolean;
|
|
338
|
+
totalRecordsLogged: number;
|
|
339
|
+
currentLogSize: number;
|
|
340
|
+
lastRotationTime: number;
|
|
57
341
|
};
|
|
58
342
|
/**
|
|
59
343
|
* Update configuration
|
package/dist/runtime/audit.js
CHANGED
|
@@ -1,34 +1,104 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Audit Logging System for
|
|
3
|
+
* Audit Logging System for MCP-I Runtime
|
|
4
4
|
*
|
|
5
5
|
* Handles audit record generation and logging with frozen format
|
|
6
|
-
* according to requirements 5.4, 5.5.
|
|
6
|
+
* according to requirements 5.4, 5.5, and 6.7 (configurable retention).
|
|
7
|
+
*
|
|
8
|
+
* ## Privacy Guarantees
|
|
9
|
+
*
|
|
10
|
+
* This system is designed with privacy-first principles:
|
|
11
|
+
*
|
|
12
|
+
* **NEVER Logged:**
|
|
13
|
+
* - Request/response bodies (only SHA-256 hashes)
|
|
14
|
+
* - Secrets (private keys, API keys, tokens, nonces)
|
|
15
|
+
* - PII (names, emails, addresses, phone numbers)
|
|
16
|
+
* - Key material (only key IDs are logged)
|
|
17
|
+
*
|
|
18
|
+
* **ALWAYS Logged:**
|
|
19
|
+
* - Metadata only (DIDs, key IDs, timestamps)
|
|
20
|
+
* - SHA-256 hashes (not plaintext data)
|
|
21
|
+
* - Session IDs (for correlation, not sensitive)
|
|
22
|
+
* - Verification results (yes/no only)
|
|
23
|
+
* - Scope identifiers (application-level)
|
|
24
|
+
*
|
|
25
|
+
* ## Frozen Format
|
|
26
|
+
*
|
|
27
|
+
* The audit.v1 format is **frozen** and will not change:
|
|
28
|
+
*
|
|
29
|
+
* ```
|
|
30
|
+
* audit.v1 ts=<unix> session=<id> audience=<host> did=<did> kid=<kid> reqHash=<sha256:..> resHash=<sha256:..> verified=yes|no scope=<scopeId|->
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* ## Event-Driven Rotation
|
|
34
|
+
*
|
|
35
|
+
* This implementation uses **event-driven rotation** (not timers):
|
|
36
|
+
* - Rotation checks happen on each `logAuditRecord()` call
|
|
37
|
+
* - No setInterval/setTimeout (works in Cloudflare Workers)
|
|
38
|
+
* - No cleanup needed (no timers to clear)
|
|
39
|
+
* - Predictable behavior (rotation on activity)
|
|
40
|
+
*
|
|
41
|
+
* @module audit
|
|
42
|
+
* @see {@link https://github.com/kya-os/xmcp-i/docs/concepts/audit-logging.md | Audit Logging Documentation}
|
|
7
43
|
*/
|
|
8
44
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
45
|
exports.defaultAuditLogger = exports.AuditLogger = void 0;
|
|
10
46
|
exports.logKeyRotationAudit = logKeyRotationAudit;
|
|
11
47
|
exports.parseAuditLine = parseAuditLine;
|
|
12
48
|
exports.validateAuditRecord = validateAuditRecord;
|
|
49
|
+
const time_1 = require("./utils/time");
|
|
13
50
|
/**
|
|
14
|
-
* Audit logger class
|
|
51
|
+
* Audit logger class with event-driven rotation support
|
|
52
|
+
*
|
|
53
|
+
* Privacy Guarantees:
|
|
54
|
+
* - NEVER logs request/response bodies (only SHA-256 hashes)
|
|
55
|
+
* - NEVER logs secrets (private keys, API keys, tokens, nonces)
|
|
56
|
+
* - NEVER logs PII (personal information)
|
|
57
|
+
* - Only logs metadata: DIDs, key IDs, timestamps, hashes, session IDs
|
|
58
|
+
* - Frozen format: audit.v1 with fixed field order
|
|
59
|
+
*
|
|
60
|
+
* Rotation Strategy:
|
|
61
|
+
* - Event-driven (no timers) - rotation checks on each logAuditRecord() call
|
|
62
|
+
* - Works in all environments (Node.js, Cloudflare Workers, Vercel Edge)
|
|
63
|
+
* - No cleanup needed (no timers to clear)
|
|
15
64
|
*/
|
|
16
65
|
class AuditLogger {
|
|
17
66
|
config;
|
|
18
67
|
sessionAuditLog = new Set(); // Track first call per session
|
|
68
|
+
totalRecordsLogged = 0; // Total records logged (for count rotation)
|
|
69
|
+
currentLogSize = 0; // Current log size in bytes (for size rotation)
|
|
70
|
+
lastRotationTime = Date.now(); // Last rotation timestamp (for time rotation)
|
|
71
|
+
destroyed = false; // Track if logger has been destroyed
|
|
19
72
|
constructor(config = {}) {
|
|
73
|
+
// Merge rotation config carefully to preserve defaults
|
|
74
|
+
const rotationConfig = config.rotation
|
|
75
|
+
? {
|
|
76
|
+
strategy: "custom",
|
|
77
|
+
...config.rotation,
|
|
78
|
+
}
|
|
79
|
+
: undefined;
|
|
20
80
|
this.config = {
|
|
21
81
|
enabled: true,
|
|
22
82
|
logFunction: console.log,
|
|
23
83
|
includePayloads: false, // Keep identity/proof data out by default
|
|
24
84
|
...config,
|
|
85
|
+
rotation: rotationConfig,
|
|
25
86
|
};
|
|
26
87
|
}
|
|
27
88
|
/**
|
|
28
89
|
* Emit audit record on first call per session
|
|
29
90
|
* Requirements: 5.4, 5.5
|
|
91
|
+
*
|
|
92
|
+
* This method:
|
|
93
|
+
* 1. Checks if logger is destroyed
|
|
94
|
+
* 2. Logs audit record (first call per session)
|
|
95
|
+
* 3. Checks if rotation is needed (event-driven)
|
|
96
|
+
* 4. Triggers rotation hooks if threshold met
|
|
30
97
|
*/
|
|
31
98
|
async logAuditRecord(context) {
|
|
99
|
+
if (this.destroyed) {
|
|
100
|
+
throw new Error("AuditLogger has been destroyed");
|
|
101
|
+
}
|
|
32
102
|
if (!this.config.enabled) {
|
|
33
103
|
return;
|
|
34
104
|
}
|
|
@@ -46,7 +116,7 @@ class AuditLogger {
|
|
|
46
116
|
session: context.session.sessionId,
|
|
47
117
|
audience: context.session.audience,
|
|
48
118
|
did: context.identity.did,
|
|
49
|
-
kid: context.identity.
|
|
119
|
+
kid: context.identity.kid,
|
|
50
120
|
reqHash: context.requestHash,
|
|
51
121
|
resHash: context.responseHash,
|
|
52
122
|
verified: context.verified,
|
|
@@ -54,8 +124,14 @@ class AuditLogger {
|
|
|
54
124
|
};
|
|
55
125
|
// Format as frozen audit line
|
|
56
126
|
const auditLine = this.formatAuditLine(auditRecord);
|
|
127
|
+
// Track size in bytes (UTF-8)
|
|
128
|
+
const sizeBytes = Buffer.byteLength(auditLine, "utf8");
|
|
129
|
+
this.currentLogSize += sizeBytes;
|
|
130
|
+
this.totalRecordsLogged++;
|
|
57
131
|
// Emit audit record
|
|
58
132
|
this.config.logFunction(auditLine);
|
|
133
|
+
// Check if rotation is needed (event-driven)
|
|
134
|
+
await this.checkRotation();
|
|
59
135
|
}
|
|
60
136
|
/**
|
|
61
137
|
* Format audit record as frozen audit line
|
|
@@ -76,6 +152,92 @@ class AuditLogger {
|
|
|
76
152
|
];
|
|
77
153
|
return fields.join(" ");
|
|
78
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Check if rotation is needed and trigger if necessary (event-driven)
|
|
157
|
+
*
|
|
158
|
+
* This method is called on each logAuditRecord() call.
|
|
159
|
+
* No timers are used - rotation is checked based on current state.
|
|
160
|
+
*/
|
|
161
|
+
async checkRotation() {
|
|
162
|
+
if (!this.config.rotation) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const { strategy, sizeLimit, timeInterval, countThreshold, hooks } = this.config.rotation;
|
|
166
|
+
let shouldRotate = false;
|
|
167
|
+
let trigger = "";
|
|
168
|
+
// Size-based rotation
|
|
169
|
+
if (strategy === "size" && sizeLimit && this.currentLogSize >= sizeLimit) {
|
|
170
|
+
shouldRotate = true;
|
|
171
|
+
trigger = "size-limit";
|
|
172
|
+
await hooks?.onSizeLimit?.(this.currentLogSize, sizeLimit);
|
|
173
|
+
}
|
|
174
|
+
// Time-based rotation (event-driven, not timer-based)
|
|
175
|
+
if (strategy === "time" &&
|
|
176
|
+
timeInterval &&
|
|
177
|
+
Date.now() - this.lastRotationTime >= timeInterval) {
|
|
178
|
+
shouldRotate = true;
|
|
179
|
+
trigger = "time-interval";
|
|
180
|
+
const interval = (0, time_1.formatTimeInterval)(timeInterval);
|
|
181
|
+
await hooks?.onTimeBased?.(interval);
|
|
182
|
+
}
|
|
183
|
+
// Count-based rotation
|
|
184
|
+
if (strategy === "count" &&
|
|
185
|
+
countThreshold &&
|
|
186
|
+
this.totalRecordsLogged >= countThreshold) {
|
|
187
|
+
shouldRotate = true;
|
|
188
|
+
trigger = "count-threshold";
|
|
189
|
+
await hooks?.onCountThreshold?.(this.totalRecordsLogged, countThreshold);
|
|
190
|
+
}
|
|
191
|
+
// Trigger rotation if needed
|
|
192
|
+
if (shouldRotate) {
|
|
193
|
+
await this.rotateNow(trigger);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Rotate audit log now (manually triggered)
|
|
198
|
+
*
|
|
199
|
+
* This method:
|
|
200
|
+
* 1. Calls onRotation hook (for user to archive/rotate log file)
|
|
201
|
+
* 2. Resets rotation counters (size, time, count)
|
|
202
|
+
*
|
|
203
|
+
* @param trigger - Reason for rotation (e.g., "manual", "size-limit")
|
|
204
|
+
*/
|
|
205
|
+
async rotateNow(trigger = "manual") {
|
|
206
|
+
if (!this.config.rotation?.hooks?.onRotation) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const context = {
|
|
210
|
+
strategy: this.config.rotation.strategy || "custom",
|
|
211
|
+
trigger,
|
|
212
|
+
recordsLogged: this.totalRecordsLogged,
|
|
213
|
+
timestamp: Date.now(),
|
|
214
|
+
};
|
|
215
|
+
// Call rotation hook (user implements log file rotation)
|
|
216
|
+
await this.config.rotation.hooks.onRotation(context);
|
|
217
|
+
// Reset rotation counters
|
|
218
|
+
this.currentLogSize = 0;
|
|
219
|
+
this.lastRotationTime = Date.now();
|
|
220
|
+
this.totalRecordsLogged = 0;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Destroy audit logger and cleanup resources
|
|
224
|
+
*
|
|
225
|
+
* This method:
|
|
226
|
+
* 1. Clears session tracking (memory)
|
|
227
|
+
* 2. Resets statistics
|
|
228
|
+
* 3. Marks logger as destroyed
|
|
229
|
+
*
|
|
230
|
+
* Note: No timers to clear (event-driven rotation)
|
|
231
|
+
*/
|
|
232
|
+
destroy() {
|
|
233
|
+
if (this.destroyed) {
|
|
234
|
+
return; // Idempotent
|
|
235
|
+
}
|
|
236
|
+
this.sessionAuditLog.clear();
|
|
237
|
+
this.totalRecordsLogged = 0;
|
|
238
|
+
this.currentLogSize = 0;
|
|
239
|
+
this.destroyed = true;
|
|
240
|
+
}
|
|
79
241
|
/**
|
|
80
242
|
* Clear session audit log (useful for testing)
|
|
81
243
|
*/
|
|
@@ -90,6 +252,9 @@ class AuditLogger {
|
|
|
90
252
|
enabled: this.config.enabled,
|
|
91
253
|
sessionsLogged: this.sessionAuditLog.size,
|
|
92
254
|
includePayloads: this.config.includePayloads,
|
|
255
|
+
totalRecordsLogged: this.totalRecordsLogged,
|
|
256
|
+
currentLogSize: this.currentLogSize,
|
|
257
|
+
lastRotationTime: this.lastRotationTime,
|
|
93
258
|
};
|
|
94
259
|
}
|
|
95
260
|
/**
|
|
@@ -136,7 +136,7 @@ export declare class MemoryResumeTokenStore implements ResumeTokenStore {
|
|
|
136
136
|
* @param resumeToken - Optional resume token from previous authorization
|
|
137
137
|
* @returns Verification result with delegation or auth error
|
|
138
138
|
*/
|
|
139
|
-
export declare function verifyOrHints(agentDid: string, scopes: string[], config: AuthHandshakeConfig,
|
|
139
|
+
export declare function verifyOrHints(agentDid: string, scopes: string[], config: AuthHandshakeConfig, _resumeToken?: string): Promise<VerifyOrHintsResult>;
|
|
140
140
|
/**
|
|
141
141
|
* Helper: Check if scopes are sensitive and require authorization
|
|
142
142
|
*
|
|
@@ -81,7 +81,7 @@ exports.MemoryResumeTokenStore = MemoryResumeTokenStore;
|
|
|
81
81
|
* @param resumeToken - Optional resume token from previous authorization
|
|
82
82
|
* @returns Verification result with delegation or auth error
|
|
83
83
|
*/
|
|
84
|
-
async function verifyOrHints(agentDid, scopes, config,
|
|
84
|
+
async function verifyOrHints(agentDid, scopes, config, _resumeToken) {
|
|
85
85
|
const startTime = Date.now();
|
|
86
86
|
if (config.debug) {
|
|
87
87
|
console.log(`[AuthHandshake] Verifying ${agentDid} for scopes: ${scopes.join(', ')}`);
|
package/dist/runtime/debug.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface DebugVerificationResult {
|
|
|
16
16
|
signature: {
|
|
17
17
|
valid: boolean;
|
|
18
18
|
algorithm: string;
|
|
19
|
-
|
|
19
|
+
kid: string;
|
|
20
20
|
};
|
|
21
21
|
proof: {
|
|
22
22
|
valid: boolean;
|
|
@@ -43,7 +43,7 @@ export interface DebugVerificationResult {
|
|
|
43
43
|
export interface DebugPageData {
|
|
44
44
|
identity: {
|
|
45
45
|
did: string;
|
|
46
|
-
|
|
46
|
+
kid: string;
|
|
47
47
|
didDocumentUrl: string;
|
|
48
48
|
};
|
|
49
49
|
registry: {
|
package/dist/runtime/debug.js
CHANGED
|
@@ -46,7 +46,7 @@ class DebugManager {
|
|
|
46
46
|
const data = {
|
|
47
47
|
identity: {
|
|
48
48
|
did: this.identity.did,
|
|
49
|
-
|
|
49
|
+
kid: this.identity.kid,
|
|
50
50
|
didDocumentUrl: this.generateDIDDocumentUrl(this.identity.did),
|
|
51
51
|
},
|
|
52
52
|
registry: {
|
|
@@ -115,7 +115,7 @@ class DebugManager {
|
|
|
115
115
|
signature: {
|
|
116
116
|
valid: false,
|
|
117
117
|
algorithm: "EdDSA",
|
|
118
|
-
|
|
118
|
+
kid: proof.meta.kid,
|
|
119
119
|
},
|
|
120
120
|
proof: {
|
|
121
121
|
valid: false,
|
|
@@ -366,7 +366,7 @@ class DebugManager {
|
|
|
366
366
|
</div>
|
|
367
367
|
<div class="field">
|
|
368
368
|
<label>Key ID</label>
|
|
369
|
-
<div class="value">${data.identity.
|
|
369
|
+
<div class="value">${data.identity.kid}</div>
|
|
370
370
|
</div>
|
|
371
371
|
<div class="field">
|
|
372
372
|
<label>DID Document</label>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Delegation Module Exports
|
|
4
|
+
*
|
|
5
|
+
* W3C VC-based delegation issuance and verification
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
__exportStar(require("./vc-issuer"), exports);
|
|
23
|
+
__exportStar(require("./vc-verifier"), exports);
|