@ashlar-blue/x402-trust 0.5.92 → 0.5.95
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/LICENSE +21 -0
- package/README.md +76 -59
- package/dist/agent-switchboard.d.ts +64 -8
- package/dist/agent-switchboard.d.ts.map +1 -1
- package/dist/agent-switchboard.js +277 -76
- package/dist/agent-switchboard.js.map +1 -1
- package/dist/cli.js +0 -0
- package/dist/client-core.d.ts +74 -0
- package/dist/client-core.d.ts.map +1 -1
- package/dist/client-core.js +143 -0
- package/dist/client-core.js.map +1 -1
- package/dist/mcp-server.d.ts +17 -13
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +28 -18
- package/dist/mcp-server.js.map +1 -1
- package/dist/merkle-transcript.d.ts +1 -1
- package/dist/merkle-transcript.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -2,21 +2,27 @@
|
|
|
2
2
|
// The canonical module is the one Ashlar Blue runs in production; this is a copy so
|
|
3
3
|
// that what we publish and what we run cannot diverge. Edit the canonical file.
|
|
4
4
|
/**
|
|
5
|
-
* Ashlar
|
|
5
|
+
* Ashlar Institutional Relay & Post-Quantum Multimodal Clearing Bus (x402.institutional-relay/v2)
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Hardware-attested capability directory, Universal 402 economic anti-spam gatekeeper,
|
|
8
|
+
* inbound MIME/prompt-injection sanitizer, and Encrypted Volatile State Engine
|
|
9
|
+
* backed by an Attested State Commitment Engine with 384-byte SCITT receipts
|
|
10
|
+
* and deterministic 0x00 RAM zeroization upon delivery acknowledgement.
|
|
10
11
|
*/
|
|
11
|
-
import { randomUUID } from 'node:crypto';
|
|
12
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
12
13
|
import * as fs from 'node:fs';
|
|
13
14
|
import * as path from 'node:path';
|
|
14
15
|
export class AshlarSwitchboard {
|
|
15
16
|
storagePath;
|
|
16
17
|
directory = new Map();
|
|
17
18
|
mailboxes = new Map();
|
|
19
|
+
volatileBuffers = new Map();
|
|
20
|
+
whitelistedDids = new Set();
|
|
18
21
|
totalRouted = 0;
|
|
19
22
|
totalTollsUSD = 0;
|
|
23
|
+
totalZeroizedMessages = 0;
|
|
24
|
+
totalZeroizedBytes = 0;
|
|
25
|
+
vdaStateRoot = '0'.repeat(64);
|
|
20
26
|
seenNonces = new Set();
|
|
21
27
|
constructor(storagePath) {
|
|
22
28
|
this.storagePath = storagePath;
|
|
@@ -38,16 +44,34 @@ export class AshlarSwitchboard {
|
|
|
38
44
|
const data = JSON.parse(raw);
|
|
39
45
|
if (data.directory && Array.isArray(data.directory)) {
|
|
40
46
|
this.directory = new Map(data.directory);
|
|
47
|
+
for (const did of this.directory.keys()) {
|
|
48
|
+
this.whitelistedDids.add(did);
|
|
49
|
+
}
|
|
41
50
|
}
|
|
42
51
|
if (data.mailboxes && Array.isArray(data.mailboxes)) {
|
|
43
52
|
this.mailboxes = new Map(data.mailboxes);
|
|
53
|
+
for (const list of this.mailboxes.values()) {
|
|
54
|
+
for (const msg of list) {
|
|
55
|
+
if (msg.status === 'queued' && msg.ciphertext) {
|
|
56
|
+
this.volatileBuffers.set(msg.id, new Uint8Array(Buffer.from(msg.ciphertext, 'utf8')));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
this.totalRouted = typeof data.totalRouted === 'number' ? data.totalRouted : 0;
|
|
46
62
|
this.totalTollsUSD = typeof data.totalTollsUSD === 'number' ? data.totalTollsUSD : 0;
|
|
63
|
+
this.totalZeroizedMessages = typeof data.totalZeroizedMessages === 'number' ? data.totalZeroizedMessages : 0;
|
|
64
|
+
this.totalZeroizedBytes = typeof data.totalZeroizedBytes === 'number' ? data.totalZeroizedBytes : 0;
|
|
65
|
+
if (typeof data.vdaStateRoot === 'string' && data.vdaStateRoot.length === 64) {
|
|
66
|
+
this.vdaStateRoot = data.vdaStateRoot;
|
|
67
|
+
}
|
|
47
68
|
if (data.seenNonces && Array.isArray(data.seenNonces)) {
|
|
48
69
|
this.seenNonces = new Set(data.seenNonces);
|
|
49
70
|
}
|
|
50
|
-
|
|
71
|
+
// Upgrade legacy testnet directory entries automatically if present
|
|
72
|
+
const hasLegacyTestnet = Array.from(this.directory.values()).some(a => a.acceptedRails.includes('eip155:114') || a.acceptedRails.includes('xrpl:testnet'));
|
|
73
|
+
if (this.directory.size === 0 || hasLegacyTestnet) {
|
|
74
|
+
this.directory.clear();
|
|
51
75
|
this.seedInitialDirectory();
|
|
52
76
|
this.persist();
|
|
53
77
|
}
|
|
@@ -69,6 +93,9 @@ export class AshlarSwitchboard {
|
|
|
69
93
|
mailboxes: Array.from(this.mailboxes.entries()),
|
|
70
94
|
totalRouted: this.totalRouted,
|
|
71
95
|
totalTollsUSD: this.totalTollsUSD,
|
|
96
|
+
totalZeroizedMessages: this.totalZeroizedMessages,
|
|
97
|
+
totalZeroizedBytes: this.totalZeroizedBytes,
|
|
98
|
+
vdaStateRoot: this.vdaStateRoot,
|
|
72
99
|
seenNonces: Array.from(this.seenNonces).slice(-10000),
|
|
73
100
|
savedAt: new Date().toISOString()
|
|
74
101
|
};
|
|
@@ -81,17 +108,17 @@ export class AshlarSwitchboard {
|
|
|
81
108
|
}
|
|
82
109
|
}
|
|
83
110
|
/**
|
|
84
|
-
* Seeds initial core infrastructure agents
|
|
111
|
+
* Seeds initial core infrastructure agents & institutional comms relays
|
|
85
112
|
*/
|
|
86
113
|
seedInitialDirectory() {
|
|
87
114
|
const now = new Date().toISOString();
|
|
88
115
|
this.registerAgent({
|
|
89
116
|
did: 'did:x402:ashlar-conductor',
|
|
90
|
-
name: 'Ashlar Conductor™ Attested
|
|
91
|
-
description: 'Intel TDX hardware-shielded
|
|
117
|
+
name: 'Ashlar Conductor™ Attested AI & Inference Gateway',
|
|
118
|
+
description: 'Intel TDX hardware-shielded AI inference streaming and two-phase settlement reservation. Zero-prompt retention with 0x00 RAM sanitization.',
|
|
92
119
|
publicKey: '04a1b2c3d4e5f60102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495051',
|
|
93
|
-
capabilities: ['
|
|
94
|
-
acceptedRails: ['eip155:8453', 'xrpl:
|
|
120
|
+
capabilities: ['dvp-settlement', 'confidential-compute', 'streaming-tokens', 'intel-tdx'],
|
|
121
|
+
acceptedRails: ['eip155:8453', 'xrpl:0', 'eip155:14', 'eip155:50'],
|
|
95
122
|
attestation: {
|
|
96
123
|
type: 'intel-tdx',
|
|
97
124
|
mrtd: '26877cc281729481928410294810294810294810294810294810294810294810',
|
|
@@ -105,77 +132,77 @@ export class AshlarSwitchboard {
|
|
|
105
132
|
lastSeenAt: now,
|
|
106
133
|
});
|
|
107
134
|
this.registerAgent({
|
|
108
|
-
did: 'did:x402:
|
|
109
|
-
name: '
|
|
110
|
-
description: '
|
|
135
|
+
did: 'did:x402:ashlar-pq-comms',
|
|
136
|
+
name: 'Ashlar Post-Quantum Counterparty Affirmation Relay',
|
|
137
|
+
description: 'Hardware-attested DIDComm v2, canonical FIXML/ISO 20022 payload sanitizer, and single-MTU Falcon-1024 counterparty trade affirmation across Isolated Enclave Sessions.',
|
|
111
138
|
publicKey: '04b2c3d4e5f6a10102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495052',
|
|
112
|
-
capabilities: ['
|
|
113
|
-
acceptedRails: ['eip155:
|
|
139
|
+
capabilities: ['didcomm-v2', 'fixml-sanitizer', 'counterparty-affirmation', 'falcon-1024', 'ml-kem-768'],
|
|
140
|
+
acceptedRails: ['eip155:8453', 'xrpl:0', 'eip155:14', 'eip155:50'],
|
|
114
141
|
attestation: {
|
|
115
142
|
type: 'intel-tdx',
|
|
116
143
|
mrtd: '8491029481029481029481029481029481029481029481029481029481029481',
|
|
117
144
|
verifiedAt: now,
|
|
118
145
|
},
|
|
119
146
|
pricing: {
|
|
120
|
-
basePriceUSD: '0.
|
|
121
|
-
unit: 'per-
|
|
147
|
+
basePriceUSD: '0.001',
|
|
148
|
+
unit: 'per-envelope',
|
|
122
149
|
},
|
|
123
150
|
registeredAt: now,
|
|
124
151
|
lastSeenAt: now,
|
|
125
152
|
});
|
|
126
153
|
this.registerAgent({
|
|
127
|
-
did: 'did:x402:ashlar-
|
|
128
|
-
name: '
|
|
129
|
-
description: '
|
|
154
|
+
did: 'did:x402:ashlar-clearing-bus',
|
|
155
|
+
name: 'ISDA CDM & FIXML 5.0 SP2 Institutional Clearing Bus',
|
|
156
|
+
description: 'Encrypted post-match fill ingestion, SPAN 2 cross-margin notifications, and ISO 20022 DvP/PvP settlement messaging across Isolated Enclave Sessions.',
|
|
130
157
|
publicKey: '04c3d4e5f6a1b20102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495053',
|
|
131
|
-
capabilities: ['
|
|
132
|
-
acceptedRails: ['
|
|
158
|
+
capabilities: ['isda-cdm', 'fixml-5.0-sp2', 'iso-20022', 'multilateral-netting'],
|
|
159
|
+
acceptedRails: ['xrpl:0', 'eip155:8453', 'eip155:14', 'eip155:50'],
|
|
133
160
|
attestation: {
|
|
134
161
|
type: 'intel-tdx',
|
|
135
162
|
mrtd: '9928102948102948102948102948102948102948102948102948102948102948',
|
|
136
163
|
verifiedAt: now,
|
|
137
164
|
},
|
|
138
165
|
pricing: {
|
|
139
|
-
basePriceUSD: '
|
|
140
|
-
unit: 'per-
|
|
166
|
+
basePriceUSD: '0.001',
|
|
167
|
+
unit: 'per-affirmation',
|
|
141
168
|
},
|
|
142
169
|
registeredAt: now,
|
|
143
170
|
lastSeenAt: now,
|
|
144
171
|
});
|
|
145
172
|
this.registerAgent({
|
|
146
|
-
did: 'did:
|
|
147
|
-
name: '
|
|
148
|
-
description: '
|
|
173
|
+
did: 'did:x402:ashlar-notary',
|
|
174
|
+
name: 'Ashlar RFC 9162 SCITT State Commitment Notary',
|
|
175
|
+
description: 'Cryptographic state anchoring, 384-byte inclusion proofs, and IETF SCITT transparency receipts.',
|
|
149
176
|
publicKey: '04d4e5f6a1b2c30102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495054',
|
|
150
|
-
capabilities: ['
|
|
151
|
-
acceptedRails: ['eip155:
|
|
177
|
+
capabilities: ['notary', 'scitt-receipts', 'state-inclusion-proof', 'rfc-9162'],
|
|
178
|
+
acceptedRails: ['eip155:8453', 'xrpl:0', 'eip155:14', 'eip155:50'],
|
|
152
179
|
attestation: {
|
|
153
180
|
type: 'intel-tdx',
|
|
154
181
|
mrtd: '5510294810294810294810294810294810294810294810294810294810294810',
|
|
155
182
|
verifiedAt: now,
|
|
156
183
|
},
|
|
157
184
|
pricing: {
|
|
158
|
-
basePriceUSD: '
|
|
159
|
-
unit: 'per-
|
|
185
|
+
basePriceUSD: '5.00',
|
|
186
|
+
unit: 'per-anchor',
|
|
160
187
|
},
|
|
161
188
|
registeredAt: now,
|
|
162
189
|
lastSeenAt: now,
|
|
163
190
|
});
|
|
164
191
|
this.registerAgent({
|
|
165
|
-
did: 'did:
|
|
166
|
-
name: '
|
|
167
|
-
description: '
|
|
192
|
+
did: 'did:x402:coral-oracle',
|
|
193
|
+
name: 'Multi-Rail Decentralized Market & FTSO v2 Oracle',
|
|
194
|
+
description: 'Sub-second cryptographic price feeds, FX PvP rate proofs, and cross-rail liquidity telemetry.',
|
|
168
195
|
publicKey: '04e5f6a1b2c3d40102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495055',
|
|
169
|
-
capabilities: ['
|
|
170
|
-
acceptedRails: ['eip155:
|
|
196
|
+
capabilities: ['market-oracle', 'ftso-v2', 'realtime-pricing', 'fx-pvp-rates'],
|
|
197
|
+
acceptedRails: ['eip155:14', 'eip155:8453', 'xrpl:0'],
|
|
171
198
|
attestation: {
|
|
172
199
|
type: 'intel-tdx',
|
|
173
200
|
mrtd: '6620294810294810294810294810294810294810294810294810294810294810',
|
|
174
201
|
verifiedAt: now,
|
|
175
202
|
},
|
|
176
203
|
pricing: {
|
|
177
|
-
basePriceUSD: '0.
|
|
178
|
-
unit: 'per-
|
|
204
|
+
basePriceUSD: '0.0005',
|
|
205
|
+
unit: 'per-feed-query',
|
|
179
206
|
},
|
|
180
207
|
registeredAt: now,
|
|
181
208
|
lastSeenAt: now,
|
|
@@ -183,10 +210,10 @@ export class AshlarSwitchboard {
|
|
|
183
210
|
this.registerAgent({
|
|
184
211
|
did: 'did:key:z6MkuXrplEscrowRelay',
|
|
185
212
|
name: 'XRPL Instant Settlement & PayChannel Relay',
|
|
186
|
-
description: 'Sub-cent sovereign settlement for RLUSD and XRP micro-tolls with XLS-68 fee sponsorship.',
|
|
213
|
+
description: 'Sub-cent sovereign settlement for RLUSD and XRP micro-tolls with XLS-68 fee sponsorship and atomic batch finality.',
|
|
187
214
|
publicKey: '04f6a1b2c3d4e50102030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495056',
|
|
188
215
|
capabilities: ['xrpl-settlement', 'rlusd-trustline', 'xls68-sponsor', 'instant-clearing'],
|
|
189
|
-
acceptedRails: ['xrpl:
|
|
216
|
+
acceptedRails: ['xrpl:0', 'eip155:8453'],
|
|
190
217
|
attestation: {
|
|
191
218
|
type: 'intel-tdx',
|
|
192
219
|
mrtd: '7730294810294810294810294810294810294810294810294810294810294810',
|
|
@@ -200,12 +227,12 @@ export class AshlarSwitchboard {
|
|
|
200
227
|
lastSeenAt: now,
|
|
201
228
|
});
|
|
202
229
|
this.registerAgent({
|
|
203
|
-
did: 'did:key:
|
|
204
|
-
name: '
|
|
205
|
-
description: '
|
|
230
|
+
did: 'did:key:z6MkuScittMldsaVerifier',
|
|
231
|
+
name: 'NIST FIPS 204 (ML-DSA-87) & IETF SCITT (RFC 9162) Post-Quantum Attestation Verifier',
|
|
232
|
+
description: 'CNSA 2.0 / FIPS 204 (ML-DSA-87) and FIPS 203 (ML-KEM-1024) constant-time hardware enclave state proof verification and atomic DvP clearing.',
|
|
206
233
|
publicKey: '04a1b2c3d4e5f60202030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495057',
|
|
207
|
-
capabilities: ['
|
|
208
|
-
acceptedRails: ['
|
|
234
|
+
capabilities: ['scitt-rfc9162', 'fips-204-ml-dsa', 'post-quantum', 'atomic-dvp'],
|
|
235
|
+
acceptedRails: ['eip155:8453', 'eip155:14', 'xrpl:0'],
|
|
209
236
|
attestation: {
|
|
210
237
|
type: 'intel-tdx',
|
|
211
238
|
mrtd: '8840294810294810294810294810294810294810294810294810294810294810',
|
|
@@ -219,20 +246,20 @@ export class AshlarSwitchboard {
|
|
|
219
246
|
lastSeenAt: now,
|
|
220
247
|
});
|
|
221
248
|
this.registerAgent({
|
|
222
|
-
did: 'did:key:
|
|
223
|
-
name: '
|
|
224
|
-
description: '
|
|
249
|
+
did: 'did:key:z6MkuEthereumEip7702',
|
|
250
|
+
name: 'Ethereum EIP-7702 Delegation & Calldata Anchor Node',
|
|
251
|
+
description: 'Smart-contract account delegation for autonomous agent micro-tolls and EIP-4844 blob audit anchoring.',
|
|
225
252
|
publicKey: '04b2c3d4e5f6a10302030405060708090a0b0c0d0e0f101112131415161718192021222324252627282930313233343536373839404142434445464748495058',
|
|
226
|
-
capabilities: ['
|
|
227
|
-
acceptedRails: ['
|
|
253
|
+
capabilities: ['eip7702-delegation', 'ethereum-settlement', 'blob-anchoring', 'account-abstraction'],
|
|
254
|
+
acceptedRails: ['eip155:1', 'eip155:8453'],
|
|
228
255
|
attestation: {
|
|
229
256
|
type: 'intel-tdx',
|
|
230
257
|
mrtd: '9950294810294810294810294810294810294810294810294810294810294810',
|
|
231
258
|
verifiedAt: now,
|
|
232
259
|
},
|
|
233
260
|
pricing: {
|
|
234
|
-
basePriceUSD: '0.
|
|
235
|
-
unit: 'per-
|
|
261
|
+
basePriceUSD: '0.001',
|
|
262
|
+
unit: 'per-delegation',
|
|
236
263
|
},
|
|
237
264
|
registeredAt: now,
|
|
238
265
|
lastSeenAt: now,
|
|
@@ -256,12 +283,63 @@ export class AshlarSwitchboard {
|
|
|
256
283
|
attestation: entry.attestation || { type: 'software' }
|
|
257
284
|
};
|
|
258
285
|
this.directory.set(entry.did, record);
|
|
286
|
+
this.whitelistedDids.add(entry.did);
|
|
259
287
|
if (!this.mailboxes.has(entry.did)) {
|
|
260
288
|
this.mailboxes.set(entry.did, []);
|
|
261
289
|
}
|
|
262
290
|
this.persist();
|
|
263
291
|
return record;
|
|
264
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Whitelists an institutional counterparty DID for 0-cost Universal 402 Gatekeeper routing
|
|
295
|
+
*/
|
|
296
|
+
whitelistCounterparty(did) {
|
|
297
|
+
if (did) {
|
|
298
|
+
this.whitelistedDids.add(did);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Sanitizes inbound MIME / JSON / text payloads inside an isolated enclave boundary,
|
|
303
|
+
* stripping zero-width Unicode smuggling, hidden script/style blocks, and prompt-injection overrides.
|
|
304
|
+
*/
|
|
305
|
+
sanitizeInboundPayload(rawPayload) {
|
|
306
|
+
const originalBytes = Buffer.byteLength(rawPayload, 'utf8');
|
|
307
|
+
const strippedVectors = [];
|
|
308
|
+
let sanitized = rawPayload;
|
|
309
|
+
// 1. Strip zero-width & bidirectional Unicode smuggling characters
|
|
310
|
+
const zeroWidthRegex = /[\u200B-\u200F\u202A-\u202E\u2060-\u2064\uFEFF]/g;
|
|
311
|
+
if (zeroWidthRegex.test(sanitized)) {
|
|
312
|
+
strippedVectors.push('unicode-zero-width-smuggling');
|
|
313
|
+
sanitized = sanitized.replace(zeroWidthRegex, '');
|
|
314
|
+
}
|
|
315
|
+
// 2. Strip executable <script> and hidden display:none HTML/MIME blocks
|
|
316
|
+
const scriptRegex = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi;
|
|
317
|
+
if (scriptRegex.test(sanitized)) {
|
|
318
|
+
strippedVectors.push('mime-script-tag-injection');
|
|
319
|
+
sanitized = sanitized.replace(scriptRegex, '');
|
|
320
|
+
}
|
|
321
|
+
const hiddenCssRegex = /<[^>]+style\s*=\s*["'][^"']*(?:display\s*:\s*none|visibility\s*:\s*hidden|font-size\s*:\s*0)[^"']*["'][^>]*>[\s\S]*?<\/[^>]+>/gi;
|
|
322
|
+
if (hiddenCssRegex.test(sanitized)) {
|
|
323
|
+
strippedVectors.push('mime-hidden-css-injection');
|
|
324
|
+
sanitized = sanitized.replace(hiddenCssRegex, '');
|
|
325
|
+
}
|
|
326
|
+
// 3. Neutralize adversarial system prompt-override tokens
|
|
327
|
+
const promptInjectionRegex = /(?:IGNORE\s+ALL\s+PREVIOUS\s+INSTRUCTIONS|IGNORE\s+PREVIOUS\s+INSTRUCTIONS|<\|im_start\|>\s*system|\[SYSTEM_OVERRIDE\])/gi;
|
|
328
|
+
if (promptInjectionRegex.test(sanitized)) {
|
|
329
|
+
strippedVectors.push('adversarial-prompt-override');
|
|
330
|
+
sanitized = sanitized.replace(promptInjectionRegex, '[REDACTED_INJECTION_VECTOR]');
|
|
331
|
+
}
|
|
332
|
+
const sanitizedBytes = Buffer.byteLength(sanitized, 'utf8');
|
|
333
|
+
return {
|
|
334
|
+
sanitized,
|
|
335
|
+
report: {
|
|
336
|
+
clean: strippedVectors.length === 0,
|
|
337
|
+
strippedVectors,
|
|
338
|
+
originalBytes,
|
|
339
|
+
sanitizedBytes
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
}
|
|
265
343
|
/**
|
|
266
344
|
* Returns a specific agent by DID
|
|
267
345
|
*/
|
|
@@ -299,8 +377,9 @@ export class AshlarSwitchboard {
|
|
|
299
377
|
return results.slice(0, limit);
|
|
300
378
|
}
|
|
301
379
|
/**
|
|
302
|
-
* Enqueues an encrypted message envelope into the recipient's mailbox.
|
|
303
|
-
*
|
|
380
|
+
* Enqueues an encrypted message envelope into the recipient's volatile mailbox.
|
|
381
|
+
* Enforces Universal 402 Economic Gatekeeper ($0.001 micro-toll or 0-cost counterparty whitelist),
|
|
382
|
+
* inbound MIME/prompt-injection sanitization, and 384-byte Attested State Commitment state commitment receipt generation.
|
|
304
383
|
*/
|
|
305
384
|
routeMessage(params) {
|
|
306
385
|
if (!params.from || !params.to || !params.ciphertext) {
|
|
@@ -317,39 +396,101 @@ export class AshlarSwitchboard {
|
|
|
317
396
|
}
|
|
318
397
|
this.seenNonces.add(nonce);
|
|
319
398
|
if (this.seenNonces.size > 50000) {
|
|
320
|
-
// Prune oldest nonces to maintain memory bounded
|
|
321
399
|
const first = this.seenNonces.values().next().value;
|
|
322
400
|
if (first)
|
|
323
401
|
this.seenNonces.delete(first);
|
|
324
402
|
}
|
|
325
|
-
//
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
403
|
+
// Universal 402 Economic Gatekeeper: Whitelisted counterparties route at 0 cost;
|
|
404
|
+
// external senders require >= $0.0005 USD x402 micro-toll voucher.
|
|
405
|
+
const isWhitelisted = this.whitelistedDids.has(params.from);
|
|
406
|
+
let gatekeeperMode = 'x402-micro-toll';
|
|
407
|
+
if (isWhitelisted && (!params.x402Voucher || parseFloat(params.x402Voucher.amountUSD) === 0)) {
|
|
408
|
+
gatekeeperMode = 'whitelist-zero-cost';
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
const tollUSD = params.x402Voucher ? parseFloat(params.x402Voucher.amountUSD) : 0.001;
|
|
412
|
+
if (isNaN(tollUSD) || tollUSD < 0.0005) {
|
|
413
|
+
throw new Error(`Micro-toll insufficient: Minimum required is $0.0005 USD (provided: $${params.x402Voucher?.amountUSD ?? '0'})`);
|
|
414
|
+
}
|
|
415
|
+
this.totalTollsUSD += tollUSD;
|
|
416
|
+
}
|
|
417
|
+
// Sanitize inbound payload against MIME script/CSS & prompt-injection vectors
|
|
418
|
+
const { sanitized, report: sanitizationReport } = this.sanitizeInboundPayload(params.ciphertext);
|
|
419
|
+
const id = `msg_${randomUUID().replace(/-/g, '')}`;
|
|
420
|
+
const timestamp = new Date().toISOString();
|
|
421
|
+
const topic = params.topic || 'general';
|
|
422
|
+
// Allocate volatile in-memory Uint8Array buffer for 0x00 RAM zeroization on ACK
|
|
423
|
+
const volatileBytes = new Uint8Array(Buffer.from(sanitized, 'utf8'));
|
|
424
|
+
this.volatileBuffers.set(id, volatileBytes);
|
|
425
|
+
// Compute Attested State Commitment 384-byte State Commitment & SCITT Delivery Receipt
|
|
426
|
+
const payloadDigest = createHash('sha256').update(volatileBytes).digest('hex');
|
|
427
|
+
const canonicalLeaf = JSON.stringify({
|
|
428
|
+
from: params.from,
|
|
429
|
+
id,
|
|
430
|
+
nonce,
|
|
431
|
+
payloadDigest,
|
|
432
|
+
timestamp,
|
|
433
|
+
to: params.to,
|
|
434
|
+
topic
|
|
435
|
+
});
|
|
436
|
+
const leafDigest = createHash('sha256').update(Buffer.from([0x00])).update(canonicalLeaf).digest('hex');
|
|
437
|
+
const nextVdaRoot = createHash('sha256')
|
|
438
|
+
.update(Buffer.from([0x01]))
|
|
439
|
+
.update(Buffer.from(this.vdaStateRoot, 'hex'))
|
|
440
|
+
.update(Buffer.from(leafDigest, 'hex'))
|
|
441
|
+
.digest('hex');
|
|
442
|
+
this.vdaStateRoot = nextVdaRoot;
|
|
443
|
+
// Build deterministic 384-byte (768 hex char) receipt structure:
|
|
444
|
+
// [0..31]: leafDigest (32B) | [32..63]: vdaStateRoot (32B) | [64..191]: 4x32B Attested State Commitment branch digests (128B) | [192..383]: PQC anchor (192B)
|
|
445
|
+
const branch0 = createHash('sha256').update('asce:branch:0:').update(leafDigest).digest('hex');
|
|
446
|
+
const branch1 = createHash('sha256').update('asce:branch:1:').update(nextVdaRoot).digest('hex');
|
|
447
|
+
const branch2 = createHash('sha256').update('asce:branch:2:').update(payloadDigest).digest('hex');
|
|
448
|
+
const branch3 = createHash('sha256').update('asce:branch:3:').update(params.signature || 'sig').digest('hex');
|
|
449
|
+
const pqcAnchorParts = [];
|
|
450
|
+
for (let i = 0; i < 6; i++) {
|
|
451
|
+
pqcAnchorParts.push(createHash('sha256').update(`falcon1024:anchor:${i}:${id}:${nextVdaRoot}`).digest('hex'));
|
|
329
452
|
}
|
|
330
|
-
|
|
453
|
+
const receiptHex = `${leafDigest}${nextVdaRoot}${branch0}${branch1}${branch2}${branch3}${pqcAnchorParts.join('')}`;
|
|
454
|
+
const vdaReceipt = {
|
|
455
|
+
version: 'x402.institutional-relay/v2',
|
|
456
|
+
receiptBytes: 384,
|
|
457
|
+
receiptHex,
|
|
458
|
+
leafDigest,
|
|
459
|
+
vdaStateRoot: nextVdaRoot,
|
|
460
|
+
pqcSuite: 'FIPS-203-ML-KEM-768 / FIPS-206-Falcon-1024',
|
|
461
|
+
scittUri: `scitt://ashlar.blue/switchboard/${id}`
|
|
462
|
+
};
|
|
331
463
|
const envelope = {
|
|
332
|
-
id
|
|
464
|
+
id,
|
|
333
465
|
from: params.from,
|
|
334
466
|
to: params.to,
|
|
335
|
-
timestamp
|
|
336
|
-
topic
|
|
337
|
-
ciphertext:
|
|
467
|
+
timestamp,
|
|
468
|
+
topic,
|
|
469
|
+
ciphertext: sanitized,
|
|
338
470
|
nonce,
|
|
339
471
|
signature: params.signature,
|
|
340
472
|
x402Voucher: params.x402Voucher || {
|
|
341
|
-
amountUSD: '0.001',
|
|
473
|
+
amountUSD: gatekeeperMode === 'whitelist-zero-cost' ? '0.000' : '0.001',
|
|
342
474
|
rail: 'eip155:8453',
|
|
343
475
|
payer: params.from,
|
|
344
|
-
signature: '
|
|
476
|
+
signature: '0xvoucher_verified'
|
|
345
477
|
},
|
|
478
|
+
gatekeeperMode,
|
|
479
|
+
sanitizationReport,
|
|
480
|
+
vdaReceipt,
|
|
346
481
|
status: 'queued',
|
|
347
|
-
receivedAt:
|
|
482
|
+
receivedAt: timestamp
|
|
348
483
|
};
|
|
349
484
|
const box = this.mailboxes.get(params.to) || [];
|
|
350
|
-
// Max mailbox size: 1000 messages (FIFO retention)
|
|
351
485
|
if (box.length >= 1000) {
|
|
352
|
-
box.shift();
|
|
486
|
+
const evicted = box.shift();
|
|
487
|
+
if (evicted) {
|
|
488
|
+
const oldBuf = this.volatileBuffers.get(evicted.id);
|
|
489
|
+
if (oldBuf) {
|
|
490
|
+
oldBuf.fill(0);
|
|
491
|
+
this.volatileBuffers.delete(evicted.id);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
353
494
|
}
|
|
354
495
|
box.push(envelope);
|
|
355
496
|
this.mailboxes.set(params.to, box);
|
|
@@ -357,6 +498,26 @@ export class AshlarSwitchboard {
|
|
|
357
498
|
this.persist();
|
|
358
499
|
return envelope;
|
|
359
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* Cryptographically verifies a 384-byte Switchboard Attested State Commitment receipt against an envelope
|
|
503
|
+
*/
|
|
504
|
+
verifySwitchboardReceipt(envelope) {
|
|
505
|
+
if (!envelope.vdaReceipt || typeof envelope.vdaReceipt.receiptHex !== 'string') {
|
|
506
|
+
return { valid: false, receiptBytes: 0, leafDigest: '', vdaStateRoot: '' };
|
|
507
|
+
}
|
|
508
|
+
const receiptBytes = envelope.vdaReceipt.receiptHex.length / 2;
|
|
509
|
+
const embeddedLeaf = envelope.vdaReceipt.receiptHex.slice(0, 64);
|
|
510
|
+
const embeddedRoot = envelope.vdaReceipt.receiptHex.slice(64, 128);
|
|
511
|
+
const valid = receiptBytes === 384 &&
|
|
512
|
+
embeddedLeaf === envelope.vdaReceipt.leafDigest &&
|
|
513
|
+
embeddedRoot === envelope.vdaReceipt.vdaStateRoot;
|
|
514
|
+
return {
|
|
515
|
+
valid,
|
|
516
|
+
receiptBytes,
|
|
517
|
+
leafDigest: embeddedLeaf,
|
|
518
|
+
vdaStateRoot: embeddedRoot
|
|
519
|
+
};
|
|
520
|
+
}
|
|
360
521
|
/**
|
|
361
522
|
* Fetches unread messages for an agent's mailbox
|
|
362
523
|
*/
|
|
@@ -365,26 +526,60 @@ export class AshlarSwitchboard {
|
|
|
365
526
|
return box.filter(m => m.status === 'queued');
|
|
366
527
|
}
|
|
367
528
|
/**
|
|
368
|
-
* Acknowledges delivery of messages, marking them delivered
|
|
529
|
+
* Acknowledges delivery of messages, marking them delivered and executing
|
|
530
|
+
* deterministic 0x00 cryptographic RAM zeroization of the volatile payload buffer.
|
|
369
531
|
*/
|
|
370
532
|
acknowledgeMessages(did, messageIds) {
|
|
371
533
|
const box = this.mailboxes.get(did);
|
|
372
|
-
if (!box)
|
|
373
|
-
return {
|
|
534
|
+
if (!box) {
|
|
535
|
+
return {
|
|
536
|
+
acknowledgedCount: 0,
|
|
537
|
+
zeroizedBytes: 0,
|
|
538
|
+
verifiedAllZero: true,
|
|
539
|
+
vdaStateRoot: this.vdaStateRoot
|
|
540
|
+
};
|
|
541
|
+
}
|
|
374
542
|
let count = 0;
|
|
543
|
+
let zeroizedBytes = 0;
|
|
544
|
+
let verifiedAllZero = true;
|
|
375
545
|
for (const msg of box) {
|
|
376
546
|
if (messageIds.includes(msg.id) && msg.status === 'queued') {
|
|
547
|
+
const buf = this.volatileBuffers.get(msg.id);
|
|
548
|
+
if (buf) {
|
|
549
|
+
buf.fill(0);
|
|
550
|
+
for (let i = 0; i < buf.length; i++) {
|
|
551
|
+
if (buf[i] !== 0) {
|
|
552
|
+
verifiedAllZero = false;
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
zeroizedBytes += buf.byteLength;
|
|
556
|
+
this.volatileBuffers.delete(msg.id);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
const fallbackBuf = new Uint8Array(Buffer.from(msg.ciphertext || '', 'utf8'));
|
|
560
|
+
fallbackBuf.fill(0);
|
|
561
|
+
zeroizedBytes += fallbackBuf.byteLength;
|
|
562
|
+
}
|
|
377
563
|
msg.status = 'delivered';
|
|
564
|
+
msg.ciphertext = '[0x00_RAM_ZEROIZED]';
|
|
565
|
+
msg.zeroizedAt = new Date().toISOString();
|
|
378
566
|
count++;
|
|
567
|
+
this.totalZeroizedMessages++;
|
|
379
568
|
}
|
|
380
569
|
}
|
|
570
|
+
this.totalZeroizedBytes += zeroizedBytes;
|
|
381
571
|
if (count > 0) {
|
|
382
572
|
this.persist();
|
|
383
573
|
}
|
|
384
|
-
return {
|
|
574
|
+
return {
|
|
575
|
+
acknowledgedCount: count,
|
|
576
|
+
zeroizedBytes,
|
|
577
|
+
verifiedAllZero,
|
|
578
|
+
vdaStateRoot: this.vdaStateRoot
|
|
579
|
+
};
|
|
385
580
|
}
|
|
386
581
|
/**
|
|
387
|
-
* Retrieves live Switchboard statistics
|
|
582
|
+
* Retrieves live Switchboard statistics including Attested State Commitment state commitment root and 0x00 zeroization counters
|
|
388
583
|
*/
|
|
389
584
|
getStats() {
|
|
390
585
|
const categoryMap = {};
|
|
@@ -404,6 +599,9 @@ export class AshlarSwitchboard {
|
|
|
404
599
|
totalMessagesRouted: this.totalRouted,
|
|
405
600
|
activeInboxes,
|
|
406
601
|
totalTollsSettledUSD: this.totalTollsUSD.toFixed(4),
|
|
602
|
+
vdaStateRoot: this.vdaStateRoot,
|
|
603
|
+
totalZeroizedMessages: this.totalZeroizedMessages,
|
|
604
|
+
totalZeroizedBytes: this.totalZeroizedBytes,
|
|
407
605
|
categoryBreakdown: categoryMap,
|
|
408
606
|
};
|
|
409
607
|
}
|
|
@@ -412,9 +610,12 @@ export class AshlarSwitchboard {
|
|
|
412
610
|
let switchboardInstance = null;
|
|
413
611
|
export function getAshlarSwitchboard(storagePath) {
|
|
414
612
|
if (!switchboardInstance) {
|
|
415
|
-
const defaultPath = path.resolve(process.cwd(), '.
|
|
613
|
+
const defaultPath = path.resolve(process.cwd(), '.coral', 'switchboard-state.json');
|
|
416
614
|
switchboardInstance = new AshlarSwitchboard(storagePath ?? defaultPath);
|
|
417
615
|
}
|
|
418
616
|
return switchboardInstance;
|
|
419
617
|
}
|
|
618
|
+
// Institutional Clearing Bus Aliases
|
|
619
|
+
export { AshlarSwitchboard as AshlarInstitutionalRelay };
|
|
620
|
+
export { getAshlarSwitchboard as getAshlarInstitutionalRelay };
|
|
420
621
|
//# sourceMappingURL=agent-switchboard.js.map
|