@longarc/mdash 3.0.0
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 +278 -0
- package/dist/checkpoint/engine.d.ts +208 -0
- package/dist/checkpoint/engine.d.ts.map +1 -0
- package/dist/checkpoint/engine.js +369 -0
- package/dist/checkpoint/engine.js.map +1 -0
- package/dist/context/engine.d.ts +197 -0
- package/dist/context/engine.d.ts.map +1 -0
- package/dist/context/engine.js +392 -0
- package/dist/context/engine.js.map +1 -0
- package/dist/core/commitment.d.ts +154 -0
- package/dist/core/commitment.d.ts.map +1 -0
- package/dist/core/commitment.js +305 -0
- package/dist/core/commitment.js.map +1 -0
- package/dist/core/crypto.d.ts +100 -0
- package/dist/core/crypto.d.ts.map +1 -0
- package/dist/core/crypto.js +243 -0
- package/dist/core/crypto.js.map +1 -0
- package/dist/index.d.ts +121 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +234 -0
- package/dist/index.js.map +1 -0
- package/dist/mcca/engine.d.ts +260 -0
- package/dist/mcca/engine.d.ts.map +1 -0
- package/dist/mcca/engine.js +518 -0
- package/dist/mcca/engine.js.map +1 -0
- package/dist/physics/engine.d.ts +165 -0
- package/dist/physics/engine.d.ts.map +1 -0
- package/dist/physics/engine.js +371 -0
- package/dist/physics/engine.js.map +1 -0
- package/dist/tee/engine.d.ts +285 -0
- package/dist/tee/engine.d.ts.map +1 -0
- package/dist/tee/engine.js +505 -0
- package/dist/tee/engine.js.map +1 -0
- package/dist/warrant/engine.d.ts +195 -0
- package/dist/warrant/engine.d.ts.map +1 -0
- package/dist/warrant/engine.js +409 -0
- package/dist/warrant/engine.js.map +1 -0
- package/dist/zk/engine.d.ts +243 -0
- package/dist/zk/engine.d.ts.map +1 -0
- package/dist/zk/engine.js +489 -0
- package/dist/zk/engine.js.map +1 -0
- package/package.json +25 -0
- package/src/__tests__/phase1.test.ts +1120 -0
- package/src/__tests__/phase2-4.test.ts +898 -0
- package/src/checkpoint/engine.ts +532 -0
- package/src/context/engine.ts +598 -0
- package/src/core/commitment.ts +438 -0
- package/src/core/crypto.ts +304 -0
- package/src/index.ts +320 -0
- package/src/mcca/engine.ts +778 -0
- package/src/physics/engine.ts +563 -0
- package/src/tee/engine.ts +810 -0
- package/src/warrant/engine.ts +625 -0
- package/src/zk/engine.ts +730 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mdash v3.0 - Warrant System
|
|
3
|
+
*
|
|
4
|
+
* Speculative warrant issuance with cryptographic invalidation.
|
|
5
|
+
* Target: <10ms activation (cache hit)
|
|
6
|
+
*
|
|
7
|
+
* Invariants:
|
|
8
|
+
* - WARRANT-INV-001: Speculative warrants expire within 60s if not activated
|
|
9
|
+
* - WARRANT-INV-002: Revocation propagates to all caches within 100ms
|
|
10
|
+
* - WARRANT-INV-003: Activated warrants immediately sealed (L1)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
Hash,
|
|
15
|
+
Seal,
|
|
16
|
+
Timestamp,
|
|
17
|
+
WarrantId,
|
|
18
|
+
generateWarrantId,
|
|
19
|
+
generateTimestamp,
|
|
20
|
+
sha256Object,
|
|
21
|
+
hmacSeal,
|
|
22
|
+
deriveKey,
|
|
23
|
+
} from '../core/crypto.js';
|
|
24
|
+
|
|
25
|
+
import { CommitmentEngine } from '../core/commitment.js';
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// WARRANT TYPES
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
export type WarrantState =
|
|
32
|
+
| 'DRAFT'
|
|
33
|
+
| 'PENDING'
|
|
34
|
+
| 'SPECULATIVE'
|
|
35
|
+
| 'ACTIVE'
|
|
36
|
+
| 'REVOKED'
|
|
37
|
+
| 'EXPIRED'
|
|
38
|
+
| 'ARCHIVED';
|
|
39
|
+
|
|
40
|
+
export type WarrantTier = 'T1' | 'T2' | 'T3';
|
|
41
|
+
|
|
42
|
+
export interface WarrantConstraints {
|
|
43
|
+
/** Maximum number of invocations */
|
|
44
|
+
maxCalls?: number;
|
|
45
|
+
/** Maximum execution time (ms) */
|
|
46
|
+
maxTime?: number;
|
|
47
|
+
/** Maximum financial exposure */
|
|
48
|
+
maxAmount?: number;
|
|
49
|
+
/** Allowed destination accounts (for financial) */
|
|
50
|
+
allowedAccounts?: string[];
|
|
51
|
+
/** Required 2FA */
|
|
52
|
+
require2FA?: boolean;
|
|
53
|
+
/** Allowed domains */
|
|
54
|
+
allowedDomains?: string[];
|
|
55
|
+
/** Custom constraints */
|
|
56
|
+
custom?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Warrant {
|
|
60
|
+
/** Unique warrant identifier */
|
|
61
|
+
id: WarrantId;
|
|
62
|
+
/** Agent this warrant governs */
|
|
63
|
+
agent_id: string;
|
|
64
|
+
/** Policy template reference */
|
|
65
|
+
policy_id: string;
|
|
66
|
+
/** Current state */
|
|
67
|
+
state: WarrantState;
|
|
68
|
+
/** Liability tier */
|
|
69
|
+
tier: WarrantTier;
|
|
70
|
+
/** Operational constraints */
|
|
71
|
+
constraints: WarrantConstraints;
|
|
72
|
+
/** Creation timestamp */
|
|
73
|
+
created_at: Timestamp;
|
|
74
|
+
/** Activation timestamp (null if not yet activated) */
|
|
75
|
+
activated_at: Timestamp | null;
|
|
76
|
+
/** Expiration timestamp */
|
|
77
|
+
expires_at: Timestamp;
|
|
78
|
+
/** Revocation timestamp (null if not revoked) */
|
|
79
|
+
revoked_at: Timestamp | null;
|
|
80
|
+
/** Revocation reason */
|
|
81
|
+
revocation_reason?: string;
|
|
82
|
+
/** Issuer identity */
|
|
83
|
+
issued_by: string;
|
|
84
|
+
/** HMAC seal */
|
|
85
|
+
seal: Seal;
|
|
86
|
+
/** Protocol version */
|
|
87
|
+
version: 'v3.0';
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface SpeculativeWarrant extends Warrant {
|
|
91
|
+
state: 'SPECULATIVE';
|
|
92
|
+
/** Speculative expiry (60s from creation) */
|
|
93
|
+
speculative_expires_at: Timestamp;
|
|
94
|
+
/** Pre-computed activation seal */
|
|
95
|
+
activation_seal: Seal;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface WarrantEvent {
|
|
99
|
+
warrant_id: WarrantId;
|
|
100
|
+
event_type: 'created' | 'activated' | 'revoked' | 'expired' | 'archived';
|
|
101
|
+
timestamp: Timestamp;
|
|
102
|
+
actor: {
|
|
103
|
+
type: 'user' | 'system';
|
|
104
|
+
id: string;
|
|
105
|
+
email?: string;
|
|
106
|
+
};
|
|
107
|
+
reason: string | undefined;
|
|
108
|
+
previous_state: WarrantState;
|
|
109
|
+
new_state: WarrantState;
|
|
110
|
+
hash: Hash;
|
|
111
|
+
parent_hash: Hash | null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ============================================================================
|
|
115
|
+
// WARRANT CACHE - For <10ms activation
|
|
116
|
+
// ============================================================================
|
|
117
|
+
|
|
118
|
+
interface CacheEntry {
|
|
119
|
+
warrant: Warrant | SpeculativeWarrant;
|
|
120
|
+
cached_at: number;
|
|
121
|
+
ttl_ms: number;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export class WarrantCache {
|
|
125
|
+
private cache: Map<WarrantId, CacheEntry> = new Map();
|
|
126
|
+
private speculative: Map<string, WarrantId[]> = new Map(); // agent_id -> warrant_ids
|
|
127
|
+
private revocations: Set<WarrantId> = new Set();
|
|
128
|
+
|
|
129
|
+
private readonly DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
|
|
130
|
+
private readonly SPECULATIVE_TTL = 60 * 1000; // 60 seconds (WARRANT-INV-001)
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Store a warrant in cache
|
|
134
|
+
*/
|
|
135
|
+
set(warrant: Warrant | SpeculativeWarrant, ttl?: number): void {
|
|
136
|
+
const effectiveTtl = warrant.state === 'SPECULATIVE'
|
|
137
|
+
? this.SPECULATIVE_TTL
|
|
138
|
+
: (ttl || this.DEFAULT_TTL);
|
|
139
|
+
|
|
140
|
+
this.cache.set(warrant.id, {
|
|
141
|
+
warrant,
|
|
142
|
+
cached_at: Date.now(),
|
|
143
|
+
ttl_ms: effectiveTtl,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Track speculative warrants by agent
|
|
147
|
+
if (warrant.state === 'SPECULATIVE') {
|
|
148
|
+
const existing = this.speculative.get(warrant.agent_id) || [];
|
|
149
|
+
existing.push(warrant.id);
|
|
150
|
+
this.speculative.set(warrant.agent_id, existing);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Get a warrant from cache
|
|
156
|
+
* Returns null if not found, expired, or revoked
|
|
157
|
+
*/
|
|
158
|
+
get(id: WarrantId): Warrant | SpeculativeWarrant | null {
|
|
159
|
+
// Check revocation first (WARRANT-INV-002)
|
|
160
|
+
if (this.revocations.has(id)) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const entry = this.cache.get(id);
|
|
165
|
+
if (!entry) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check TTL
|
|
170
|
+
if (Date.now() - entry.cached_at > entry.ttl_ms) {
|
|
171
|
+
this.cache.delete(id);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return entry.warrant;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get speculative warrants for an agent
|
|
180
|
+
* For pre-staged activation
|
|
181
|
+
*/
|
|
182
|
+
getSpeculativeForAgent(agentId: string): SpeculativeWarrant[] {
|
|
183
|
+
const ids = this.speculative.get(agentId) || [];
|
|
184
|
+
const warrants: SpeculativeWarrant[] = [];
|
|
185
|
+
|
|
186
|
+
for (const id of ids) {
|
|
187
|
+
const warrant = this.get(id);
|
|
188
|
+
if (warrant && warrant.state === 'SPECULATIVE') {
|
|
189
|
+
warrants.push(warrant as SpeculativeWarrant);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return warrants;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Record a revocation
|
|
198
|
+
* Propagates immediately (WARRANT-INV-002)
|
|
199
|
+
*/
|
|
200
|
+
revoke(id: WarrantId): void {
|
|
201
|
+
this.revocations.add(id);
|
|
202
|
+
this.cache.delete(id);
|
|
203
|
+
|
|
204
|
+
// Remove from speculative tracking
|
|
205
|
+
for (const [agentId, ids] of this.speculative) {
|
|
206
|
+
const filtered = ids.filter(i => i !== id);
|
|
207
|
+
if (filtered.length !== ids.length) {
|
|
208
|
+
this.speculative.set(agentId, filtered);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if a warrant is revoked
|
|
215
|
+
*/
|
|
216
|
+
isRevoked(id: WarrantId): boolean {
|
|
217
|
+
return this.revocations.has(id);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clear expired entries
|
|
222
|
+
*/
|
|
223
|
+
cleanup(): number {
|
|
224
|
+
let removed = 0;
|
|
225
|
+
const now = Date.now();
|
|
226
|
+
|
|
227
|
+
for (const [id, entry] of this.cache) {
|
|
228
|
+
if (now - entry.cached_at > entry.ttl_ms) {
|
|
229
|
+
this.cache.delete(id);
|
|
230
|
+
removed++;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return removed;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Get cache statistics
|
|
239
|
+
*/
|
|
240
|
+
getStats(): {
|
|
241
|
+
size: number;
|
|
242
|
+
speculative: number;
|
|
243
|
+
revocations: number;
|
|
244
|
+
} {
|
|
245
|
+
return {
|
|
246
|
+
size: this.cache.size,
|
|
247
|
+
speculative: Array.from(this.speculative.values()).flat().length,
|
|
248
|
+
revocations: this.revocations.size,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// WARRANT ENGINE
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
export class WarrantEngine {
|
|
258
|
+
private key: CryptoKey | null = null;
|
|
259
|
+
private cache: WarrantCache;
|
|
260
|
+
private commitmentEngine: CommitmentEngine;
|
|
261
|
+
private eventLog: WarrantEvent[] = [];
|
|
262
|
+
private lastEventHash: Hash | null = null;
|
|
263
|
+
|
|
264
|
+
constructor(commitmentEngine: CommitmentEngine) {
|
|
265
|
+
this.cache = new WarrantCache();
|
|
266
|
+
this.commitmentEngine = commitmentEngine;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Initialize the engine with a seal key
|
|
271
|
+
*/
|
|
272
|
+
async initialize(sealKey: string): Promise<void> {
|
|
273
|
+
this.key = await deriveKey(sealKey);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Create a speculative warrant (pre-staged)
|
|
278
|
+
* WARRANT-INV-001: Expires in 60s if not activated
|
|
279
|
+
*/
|
|
280
|
+
async createSpeculative(params: {
|
|
281
|
+
agent_id: string;
|
|
282
|
+
policy_id: string;
|
|
283
|
+
tier: WarrantTier;
|
|
284
|
+
constraints: WarrantConstraints;
|
|
285
|
+
duration_ms: number;
|
|
286
|
+
issued_by: string;
|
|
287
|
+
}): Promise<SpeculativeWarrant> {
|
|
288
|
+
if (!this.key) {
|
|
289
|
+
throw new Error('Engine not initialized. Call initialize() first.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const startTime = performance.now();
|
|
293
|
+
|
|
294
|
+
const id = generateWarrantId();
|
|
295
|
+
const now = generateTimestamp();
|
|
296
|
+
const expiresAt = new Date(Date.now() + params.duration_ms).toISOString() as Timestamp;
|
|
297
|
+
const speculativeExpiresAt = new Date(Date.now() + 60000).toISOString() as Timestamp;
|
|
298
|
+
|
|
299
|
+
// Pre-compute activation seal
|
|
300
|
+
const activationData = {
|
|
301
|
+
_v: 1,
|
|
302
|
+
id,
|
|
303
|
+
agent_id: params.agent_id,
|
|
304
|
+
activated: true,
|
|
305
|
+
};
|
|
306
|
+
const activationSeal = await hmacSeal(activationData, this.key);
|
|
307
|
+
|
|
308
|
+
// Seal the warrant
|
|
309
|
+
const warrantData = {
|
|
310
|
+
_v: 1,
|
|
311
|
+
id,
|
|
312
|
+
agent_id: params.agent_id,
|
|
313
|
+
policy_id: params.policy_id,
|
|
314
|
+
state: 'SPECULATIVE',
|
|
315
|
+
tier: params.tier,
|
|
316
|
+
constraints: params.constraints,
|
|
317
|
+
created_at: now,
|
|
318
|
+
expires_at: expiresAt,
|
|
319
|
+
issued_by: params.issued_by,
|
|
320
|
+
};
|
|
321
|
+
const seal = await hmacSeal(warrantData, this.key);
|
|
322
|
+
|
|
323
|
+
const warrant: SpeculativeWarrant = {
|
|
324
|
+
id,
|
|
325
|
+
agent_id: params.agent_id,
|
|
326
|
+
policy_id: params.policy_id,
|
|
327
|
+
state: 'SPECULATIVE',
|
|
328
|
+
tier: params.tier,
|
|
329
|
+
constraints: params.constraints,
|
|
330
|
+
created_at: now,
|
|
331
|
+
activated_at: null,
|
|
332
|
+
expires_at: expiresAt,
|
|
333
|
+
revoked_at: null,
|
|
334
|
+
issued_by: params.issued_by,
|
|
335
|
+
seal,
|
|
336
|
+
version: 'v3.0',
|
|
337
|
+
speculative_expires_at: speculativeExpiresAt,
|
|
338
|
+
activation_seal: activationSeal,
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// Cache for fast activation
|
|
342
|
+
this.cache.set(warrant);
|
|
343
|
+
|
|
344
|
+
// Log event
|
|
345
|
+
await this.logEvent({
|
|
346
|
+
warrant_id: id,
|
|
347
|
+
event_type: 'created',
|
|
348
|
+
actor: { type: 'user', id: params.issued_by },
|
|
349
|
+
previous_state: 'DRAFT' as WarrantState,
|
|
350
|
+
new_state: 'SPECULATIVE',
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
const elapsed = performance.now() - startTime;
|
|
354
|
+
if (elapsed > 10) {
|
|
355
|
+
console.warn(`Speculative warrant creation exceeded target: ${elapsed.toFixed(2)}ms`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return warrant;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Activate a speculative warrant
|
|
363
|
+
* Target: <10ms (cache hit)
|
|
364
|
+
* WARRANT-INV-003: Immediately sealed (L1)
|
|
365
|
+
*/
|
|
366
|
+
async activate(id: WarrantId): Promise<Warrant> {
|
|
367
|
+
if (!this.key) {
|
|
368
|
+
throw new Error('Engine not initialized. Call initialize() first.');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const startTime = performance.now();
|
|
372
|
+
|
|
373
|
+
// Try cache first
|
|
374
|
+
const cached = this.cache.get(id);
|
|
375
|
+
if (!cached) {
|
|
376
|
+
throw new Error(`Warrant not found or expired: ${id}`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (cached.state !== 'SPECULATIVE') {
|
|
380
|
+
throw new Error(`Warrant not in SPECULATIVE state: ${cached.state}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const specWarrant = cached as SpeculativeWarrant;
|
|
384
|
+
|
|
385
|
+
// Check speculative expiry (WARRANT-INV-001)
|
|
386
|
+
if (new Date(specWarrant.speculative_expires_at) < new Date()) {
|
|
387
|
+
this.cache.revoke(id);
|
|
388
|
+
throw new Error('Speculative warrant expired (60s limit)');
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const now = generateTimestamp();
|
|
392
|
+
|
|
393
|
+
// Create activated warrant
|
|
394
|
+
const activatedWarrant: Warrant = {
|
|
395
|
+
id: specWarrant.id,
|
|
396
|
+
agent_id: specWarrant.agent_id,
|
|
397
|
+
policy_id: specWarrant.policy_id,
|
|
398
|
+
state: 'ACTIVE',
|
|
399
|
+
tier: specWarrant.tier,
|
|
400
|
+
constraints: specWarrant.constraints,
|
|
401
|
+
created_at: specWarrant.created_at,
|
|
402
|
+
activated_at: now,
|
|
403
|
+
expires_at: specWarrant.expires_at,
|
|
404
|
+
revoked_at: null,
|
|
405
|
+
issued_by: specWarrant.issued_by,
|
|
406
|
+
seal: specWarrant.seal, // Original seal preserved
|
|
407
|
+
version: 'v3.0',
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// WARRANT-INV-003: Immediately seal via commitment layer
|
|
411
|
+
await this.commitmentEngine.commit(activatedWarrant, `warrant:${id}`);
|
|
412
|
+
|
|
413
|
+
// Update cache
|
|
414
|
+
this.cache.set(activatedWarrant);
|
|
415
|
+
|
|
416
|
+
// Log event
|
|
417
|
+
await this.logEvent({
|
|
418
|
+
warrant_id: id,
|
|
419
|
+
event_type: 'activated',
|
|
420
|
+
actor: { type: 'system', id: 'warrant-engine' },
|
|
421
|
+
previous_state: 'SPECULATIVE',
|
|
422
|
+
new_state: 'ACTIVE',
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
const elapsed = performance.now() - startTime;
|
|
426
|
+
if (elapsed > 10) {
|
|
427
|
+
console.warn(`Warrant activation exceeded target: ${elapsed.toFixed(2)}ms`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return activatedWarrant;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Revoke a warrant
|
|
435
|
+
* WARRANT-INV-002: Propagates to all caches within 100ms
|
|
436
|
+
*/
|
|
437
|
+
async revoke(id: WarrantId, reason: string, actor: { type: 'user' | 'system'; id: string }): Promise<Warrant> {
|
|
438
|
+
if (!this.key) {
|
|
439
|
+
throw new Error('Engine not initialized. Call initialize() first.');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const startTime = performance.now();
|
|
443
|
+
|
|
444
|
+
const warrant = this.cache.get(id);
|
|
445
|
+
if (!warrant) {
|
|
446
|
+
throw new Error(`Warrant not found: ${id}`);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (warrant.state === 'REVOKED' || warrant.state === 'ARCHIVED') {
|
|
450
|
+
throw new Error(`Warrant already ${warrant.state}`);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const previousState = warrant.state;
|
|
454
|
+
const now = generateTimestamp();
|
|
455
|
+
|
|
456
|
+
// Create revoked warrant
|
|
457
|
+
const revokedWarrant: Warrant = {
|
|
458
|
+
...warrant,
|
|
459
|
+
state: 'REVOKED',
|
|
460
|
+
revoked_at: now,
|
|
461
|
+
revocation_reason: reason,
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
// Seal revocation
|
|
465
|
+
await this.commitmentEngine.commit(
|
|
466
|
+
{ warrant_id: id, revoked_at: now, reason },
|
|
467
|
+
`revocation:${id}`
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
// WARRANT-INV-002: Immediate cache revocation
|
|
471
|
+
this.cache.revoke(id);
|
|
472
|
+
|
|
473
|
+
// Log event
|
|
474
|
+
await this.logEvent({
|
|
475
|
+
warrant_id: id,
|
|
476
|
+
event_type: 'revoked',
|
|
477
|
+
actor,
|
|
478
|
+
reason,
|
|
479
|
+
previous_state: previousState,
|
|
480
|
+
new_state: 'REVOKED',
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
const elapsed = performance.now() - startTime;
|
|
484
|
+
if (elapsed > 100) {
|
|
485
|
+
console.warn(`Warrant revocation exceeded propagation target: ${elapsed.toFixed(2)}ms`);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return revokedWarrant;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Check if an action is authorized by warrant
|
|
493
|
+
* Returns matching warrant or null
|
|
494
|
+
*/
|
|
495
|
+
async checkAuthorization(params: {
|
|
496
|
+
agent_id: string;
|
|
497
|
+
action: string;
|
|
498
|
+
amount?: number;
|
|
499
|
+
domain?: string;
|
|
500
|
+
}): Promise<Warrant | null> {
|
|
501
|
+
const startTime = performance.now();
|
|
502
|
+
|
|
503
|
+
// Get speculative warrants for agent
|
|
504
|
+
const specWarrants = this.cache.getSpeculativeForAgent(params.agent_id);
|
|
505
|
+
|
|
506
|
+
for (const warrant of specWarrants) {
|
|
507
|
+
// Check constraints
|
|
508
|
+
if (this.meetsConstraints(warrant.constraints, params)) {
|
|
509
|
+
// Activate on first matching warrant
|
|
510
|
+
const activated = await this.activate(warrant.id);
|
|
511
|
+
|
|
512
|
+
const elapsed = performance.now() - startTime;
|
|
513
|
+
if (elapsed > 10) {
|
|
514
|
+
console.warn(`Authorization check exceeded target: ${elapsed.toFixed(2)}ms`);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return activated;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Check if action meets warrant constraints
|
|
526
|
+
*/
|
|
527
|
+
private meetsConstraints(
|
|
528
|
+
constraints: WarrantConstraints,
|
|
529
|
+
action: { amount?: number; domain?: string }
|
|
530
|
+
): boolean {
|
|
531
|
+
// Check amount limit
|
|
532
|
+
if (constraints.maxAmount !== undefined && action.amount !== undefined) {
|
|
533
|
+
if (action.amount > constraints.maxAmount) {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// Check domain allowlist
|
|
539
|
+
if (constraints.allowedDomains && action.domain) {
|
|
540
|
+
if (!constraints.allowedDomains.includes(action.domain)) {
|
|
541
|
+
return false;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return true;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Log a warrant event with hash chain
|
|
550
|
+
*/
|
|
551
|
+
private async logEvent(params: {
|
|
552
|
+
warrant_id: WarrantId;
|
|
553
|
+
event_type: WarrantEvent['event_type'];
|
|
554
|
+
actor: WarrantEvent['actor'];
|
|
555
|
+
reason?: string;
|
|
556
|
+
previous_state: WarrantState;
|
|
557
|
+
new_state: WarrantState;
|
|
558
|
+
}): Promise<void> {
|
|
559
|
+
const timestamp = generateTimestamp();
|
|
560
|
+
|
|
561
|
+
const event: WarrantEvent = {
|
|
562
|
+
warrant_id: params.warrant_id,
|
|
563
|
+
event_type: params.event_type,
|
|
564
|
+
timestamp,
|
|
565
|
+
actor: params.actor,
|
|
566
|
+
reason: params.reason,
|
|
567
|
+
previous_state: params.previous_state,
|
|
568
|
+
new_state: params.new_state,
|
|
569
|
+
hash: '' as Hash, // Will be computed
|
|
570
|
+
parent_hash: this.lastEventHash,
|
|
571
|
+
};
|
|
572
|
+
|
|
573
|
+
// Compute event hash
|
|
574
|
+
event.hash = await sha256Object({
|
|
575
|
+
...event,
|
|
576
|
+
hash: undefined, // Exclude from hash computation
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
this.eventLog.push(event);
|
|
580
|
+
this.lastEventHash = event.hash;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Get event log for a warrant
|
|
585
|
+
*/
|
|
586
|
+
getEventLog(warrantId?: WarrantId): WarrantEvent[] {
|
|
587
|
+
if (warrantId) {
|
|
588
|
+
return this.eventLog.filter(e => e.warrant_id === warrantId);
|
|
589
|
+
}
|
|
590
|
+
return [...this.eventLog];
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* Get cache statistics
|
|
595
|
+
*/
|
|
596
|
+
getCacheStats(): ReturnType<WarrantCache['getStats']> {
|
|
597
|
+
return this.cache.getStats();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// ============================================================================
|
|
602
|
+
// TIER DEFINITIONS
|
|
603
|
+
// ============================================================================
|
|
604
|
+
|
|
605
|
+
export const TIER_LIMITS: Record<WarrantTier, {
|
|
606
|
+
maxExposure: number;
|
|
607
|
+
maxCalls: number;
|
|
608
|
+
requiresApproval: boolean;
|
|
609
|
+
}> = {
|
|
610
|
+
T1: {
|
|
611
|
+
maxExposure: 1000,
|
|
612
|
+
maxCalls: 100,
|
|
613
|
+
requiresApproval: false,
|
|
614
|
+
},
|
|
615
|
+
T2: {
|
|
616
|
+
maxExposure: 100000,
|
|
617
|
+
maxCalls: 1000,
|
|
618
|
+
requiresApproval: false,
|
|
619
|
+
},
|
|
620
|
+
T3: {
|
|
621
|
+
maxExposure: Infinity,
|
|
622
|
+
maxCalls: Infinity,
|
|
623
|
+
requiresApproval: true,
|
|
624
|
+
},
|
|
625
|
+
};
|