@sanctuary-framework/mcp-server 0.6.1 → 0.8.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 +10 -0
- package/dist/cli.cjs +20148 -14129
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +20153 -14134
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +9075 -4718
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1650 -1599
- package/dist/index.d.ts +1650 -1599
- package/dist/index.js +9075 -4718
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.d.cts
CHANGED
|
@@ -127,157 +127,6 @@ interface StorageBackend {
|
|
|
127
127
|
totalSize(): Promise<number>;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
/**
|
|
131
|
-
* Sanctuary MCP Server — AES-256-GCM Encryption
|
|
132
|
-
*
|
|
133
|
-
* All state encryption in Sanctuary uses AES-256-GCM (authenticated encryption).
|
|
134
|
-
* This provides both confidentiality and integrity — a modified ciphertext will
|
|
135
|
-
* fail authentication, detecting tampering.
|
|
136
|
-
*
|
|
137
|
-
* Security invariants:
|
|
138
|
-
* - Every encryption uses a unique 12-byte IV (NIST SP 800-38D)
|
|
139
|
-
* - The 16-byte authentication tag is always verified on decryption
|
|
140
|
-
* - Keys are 256 bits (32 bytes)
|
|
141
|
-
*/
|
|
142
|
-
/** Encrypted payload structure stored on disk */
|
|
143
|
-
interface EncryptedPayload {
|
|
144
|
-
/** Format version */
|
|
145
|
-
v: number;
|
|
146
|
-
/** Algorithm identifier */
|
|
147
|
-
alg: "aes-256-gcm";
|
|
148
|
-
/** Initialization vector (base64url) */
|
|
149
|
-
iv: string;
|
|
150
|
-
/** Ciphertext (base64url) */
|
|
151
|
-
ct: string;
|
|
152
|
-
/** Authentication tag (base64url) — included in ciphertext by @noble/ciphers */
|
|
153
|
-
/** Timestamp */
|
|
154
|
-
ts: string;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Sanctuary MCP Server — L1 Cognitive Sovereignty: StateStore
|
|
159
|
-
*
|
|
160
|
-
* The encrypted state store is the foundation of Sanctuary.
|
|
161
|
-
* Every read and write goes through here. All data is encrypted
|
|
162
|
-
* with namespace-specific keys. All writes are signed by an identity.
|
|
163
|
-
* All reads verify integrity via Merkle proofs.
|
|
164
|
-
*
|
|
165
|
-
* Security invariants:
|
|
166
|
-
* - Plaintext never touches the filesystem
|
|
167
|
-
* - Every write gets a unique IV
|
|
168
|
-
* - Every write is signed (non-repudiation)
|
|
169
|
-
* - Monotonic version numbers prevent rollback
|
|
170
|
-
* - Merkle tree verifies namespace integrity
|
|
171
|
-
* - Secure deletion overwrites before unlinking
|
|
172
|
-
*/
|
|
173
|
-
|
|
174
|
-
/** Result of a state write operation */
|
|
175
|
-
interface WriteResult {
|
|
176
|
-
key: string;
|
|
177
|
-
namespace: string;
|
|
178
|
-
version: number;
|
|
179
|
-
merkle_root: string;
|
|
180
|
-
written_at: string;
|
|
181
|
-
size_bytes: number;
|
|
182
|
-
integrity_hash: string;
|
|
183
|
-
}
|
|
184
|
-
/** Result of a state read operation */
|
|
185
|
-
interface ReadResult {
|
|
186
|
-
key: string;
|
|
187
|
-
namespace: string;
|
|
188
|
-
value: string;
|
|
189
|
-
version: number;
|
|
190
|
-
integrity_verified: boolean;
|
|
191
|
-
merkle_proof: string[];
|
|
192
|
-
written_at: string;
|
|
193
|
-
written_by: string;
|
|
194
|
-
}
|
|
195
|
-
/** Options for state write */
|
|
196
|
-
interface WriteOptions {
|
|
197
|
-
content_type?: string;
|
|
198
|
-
ttl_seconds?: number;
|
|
199
|
-
tags?: string[];
|
|
200
|
-
}
|
|
201
|
-
declare class StateStore {
|
|
202
|
-
private storage;
|
|
203
|
-
private masterKey;
|
|
204
|
-
private versionCache;
|
|
205
|
-
private contentHashes;
|
|
206
|
-
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
207
|
-
private versionKey;
|
|
208
|
-
/**
|
|
209
|
-
* Get or initialize the content hash map for a namespace.
|
|
210
|
-
*/
|
|
211
|
-
private getNamespaceHashes;
|
|
212
|
-
/**
|
|
213
|
-
* Write encrypted state.
|
|
214
|
-
*
|
|
215
|
-
* @param namespace - Logical grouping
|
|
216
|
-
* @param key - State key
|
|
217
|
-
* @param value - Plaintext value (will be encrypted)
|
|
218
|
-
* @param identityId - Identity performing the write
|
|
219
|
-
* @param encryptedPrivateKey - Identity's encrypted private key (for signing)
|
|
220
|
-
* @param identityEncryptionKey - Key to decrypt the identity's private key
|
|
221
|
-
* @param options - Optional metadata
|
|
222
|
-
*/
|
|
223
|
-
write(namespace: string, key: string, value: string, identityId: string, encryptedPrivateKey: EncryptedPayload, identityEncryptionKey: Uint8Array, options?: WriteOptions): Promise<WriteResult>;
|
|
224
|
-
/**
|
|
225
|
-
* Read and decrypt state.
|
|
226
|
-
*
|
|
227
|
-
* @param namespace - Logical grouping
|
|
228
|
-
* @param key - State key
|
|
229
|
-
* @param signerPublicKey - Expected signer's public key (for signature verification)
|
|
230
|
-
* @param verifyIntegrity - Whether to verify Merkle proof (default: true)
|
|
231
|
-
*/
|
|
232
|
-
read(namespace: string, key: string, signerPublicKey?: Uint8Array, verifyIntegrity?: boolean): Promise<ReadResult | null>;
|
|
233
|
-
/**
|
|
234
|
-
* List keys in a namespace (metadata only — no decryption).
|
|
235
|
-
*/
|
|
236
|
-
list(namespace: string, prefix?: string, tags?: string[], limit?: number, offset?: number): Promise<{
|
|
237
|
-
keys: Array<{
|
|
238
|
-
key: string;
|
|
239
|
-
version: number;
|
|
240
|
-
size_bytes: number;
|
|
241
|
-
written_at: string;
|
|
242
|
-
tags: string[];
|
|
243
|
-
}>;
|
|
244
|
-
total: number;
|
|
245
|
-
merkle_root: string;
|
|
246
|
-
}>;
|
|
247
|
-
/**
|
|
248
|
-
* Securely delete state (overwrite with random bytes before removal).
|
|
249
|
-
*/
|
|
250
|
-
delete(namespace: string, key: string): Promise<{
|
|
251
|
-
deleted: boolean;
|
|
252
|
-
key: string;
|
|
253
|
-
namespace: string;
|
|
254
|
-
new_merkle_root: string;
|
|
255
|
-
deleted_at: string;
|
|
256
|
-
}>;
|
|
257
|
-
/**
|
|
258
|
-
* Export all state for a namespace as an encrypted bundle.
|
|
259
|
-
*/
|
|
260
|
-
export(namespace?: string): Promise<{
|
|
261
|
-
bundle: string;
|
|
262
|
-
namespaces: string[];
|
|
263
|
-
total_keys: number;
|
|
264
|
-
bundle_hash: string;
|
|
265
|
-
exported_at: string;
|
|
266
|
-
}>;
|
|
267
|
-
/**
|
|
268
|
-
* Import a previously exported state bundle.
|
|
269
|
-
*/
|
|
270
|
-
import(bundleBase64: string, conflictResolution: "skip" | "overwrite" | "version" | undefined, publicKeyResolver: (kid: string) => Uint8Array | null): Promise<{
|
|
271
|
-
imported_keys: number;
|
|
272
|
-
skipped_keys: number;
|
|
273
|
-
skipped_invalid_sig: number;
|
|
274
|
-
skipped_unknown_kid: number;
|
|
275
|
-
conflicts: number;
|
|
276
|
-
namespaces: string[];
|
|
277
|
-
imported_at: string;
|
|
278
|
-
}>;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
130
|
/**
|
|
282
131
|
* Sanctuary MCP Server — L2 Operational Isolation: Audit Log
|
|
283
132
|
*
|
|
@@ -328,942 +177,995 @@ declare class AuditLog {
|
|
|
328
177
|
}
|
|
329
178
|
|
|
330
179
|
/**
|
|
331
|
-
* Sanctuary MCP Server —
|
|
332
|
-
*
|
|
333
|
-
* Cryptographic commitments allow an agent to commit to a value
|
|
334
|
-
* without revealing it, then later prove what was committed.
|
|
335
|
-
*
|
|
336
|
-
* This is the MVS approach to selective disclosure — simpler than
|
|
337
|
-
* full ZK proofs but still cryptographically sound. The commitment
|
|
338
|
-
* is SHA-256(value || blinding_factor), which is:
|
|
339
|
-
* - Hiding: the commitment reveals nothing about the value
|
|
340
|
-
* - Binding: the committer cannot change the value after committing
|
|
180
|
+
* Sanctuary MCP Server — Principal Policy Types
|
|
341
181
|
*
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
345
|
-
* - Revealed values are verified via constant-time comparison
|
|
182
|
+
* Type definitions for the Principal Policy system.
|
|
183
|
+
* The Principal Policy is the human-controlled, agent-immutable
|
|
184
|
+
* configuration that gates operations through approval tiers.
|
|
346
185
|
*/
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
/**
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
revealed: boolean;
|
|
364
|
-
revealed_at?: string;
|
|
186
|
+
/** Tier 2 anomaly action: what to do when an anomaly is detected */
|
|
187
|
+
type AnomalyAction = "approve" | "log" | "allow";
|
|
188
|
+
/** Tier 2 anomaly detection configuration */
|
|
189
|
+
interface Tier2Config {
|
|
190
|
+
/** Action when agent accesses a namespace it hasn't used before */
|
|
191
|
+
new_namespace_access: AnomalyAction;
|
|
192
|
+
/** Action when agent interacts with an unknown counterparty DID */
|
|
193
|
+
new_counterparty: AnomalyAction;
|
|
194
|
+
/** Tool call frequency multiplier that triggers anomaly */
|
|
195
|
+
frequency_spike_multiplier: number;
|
|
196
|
+
/** Maximum signing operations per minute before triggering */
|
|
197
|
+
max_signs_per_minute: number;
|
|
198
|
+
/** Reading more than N keys in a namespace within 60 seconds */
|
|
199
|
+
bulk_read_threshold: number;
|
|
200
|
+
/** Policy for first session when no baseline exists */
|
|
201
|
+
first_session_policy: AnomalyAction;
|
|
365
202
|
}
|
|
366
|
-
/**
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
private storage;
|
|
371
|
-
private encryptionKey;
|
|
372
|
-
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
373
|
-
/**
|
|
374
|
-
* Store a commitment (encrypted) for later reference.
|
|
375
|
-
*/
|
|
376
|
-
store(commitment: Commitment, value: string): Promise<string>;
|
|
377
|
-
/**
|
|
378
|
-
* Retrieve a stored commitment by ID.
|
|
379
|
-
*/
|
|
380
|
-
get(id: string): Promise<StoredCommitment | null>;
|
|
203
|
+
/** Approval channel configuration */
|
|
204
|
+
interface ApprovalChannelConfig {
|
|
205
|
+
type: "stderr" | "webhook" | "callback";
|
|
206
|
+
timeout_seconds: number;
|
|
381
207
|
/**
|
|
382
|
-
*
|
|
208
|
+
* SEC-002: auto_deny is hardcoded to true and not configurable.
|
|
209
|
+
* Timeout on any approval channel ALWAYS results in denial.
|
|
210
|
+
* This field is retained for backward compatibility with existing
|
|
211
|
+
* policy files but is ignored — timeout always denies.
|
|
383
212
|
*/
|
|
384
|
-
|
|
213
|
+
auto_deny?: boolean;
|
|
214
|
+
webhook_url?: string;
|
|
215
|
+
webhook_secret?: string;
|
|
385
216
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
* Ristretto255 is available via @noble/curves/ed25519, which we already depend on.
|
|
398
|
-
* This is genuine zero-knowledge — proofs reveal nothing beyond the stated property.
|
|
399
|
-
*
|
|
400
|
-
* Architecture note:
|
|
401
|
-
* The existing commitment scheme (SHA-256 based) remains available for backward
|
|
402
|
-
* compatibility. The ZK proofs operate on a separate Pedersen commitment system
|
|
403
|
-
* that provides algebraic structure for proper ZK properties.
|
|
404
|
-
*
|
|
405
|
-
* Security invariants:
|
|
406
|
-
* - Generator H is derived via hash-to-curve (nothing-up-my-sleeve)
|
|
407
|
-
* - Blinding factors are cryptographically random (32 bytes)
|
|
408
|
-
* - Fiat-Shamir challenges use domain-separated hashing
|
|
409
|
-
* - Range proofs use a bit-decomposition approach (sound but logarithmic size)
|
|
410
|
-
*/
|
|
411
|
-
/** A Pedersen commitment: C = v*G + b*H */
|
|
412
|
-
interface PedersenCommitment {
|
|
413
|
-
/** The commitment point (encoded as base64url) */
|
|
414
|
-
commitment: string;
|
|
415
|
-
/** The blinding factor b (base64url, 32 bytes) — keep secret */
|
|
416
|
-
blinding_factor: string;
|
|
417
|
-
/** When the commitment was created */
|
|
418
|
-
committed_at: string;
|
|
217
|
+
/** Complete Principal Policy */
|
|
218
|
+
interface PrincipalPolicy {
|
|
219
|
+
version: number;
|
|
220
|
+
/** Operations that always require human approval */
|
|
221
|
+
tier1_always_approve: string[];
|
|
222
|
+
/** Behavioral anomaly detection configuration */
|
|
223
|
+
tier2_anomaly: Tier2Config;
|
|
224
|
+
/** Operations that never require approval (audit only) */
|
|
225
|
+
tier3_always_allow: string[];
|
|
226
|
+
/** How approval requests reach the human */
|
|
227
|
+
approval_channel: ApprovalChannelConfig;
|
|
419
228
|
}
|
|
420
|
-
/**
|
|
421
|
-
interface
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
announcement: string;
|
|
428
|
-
/** Response scalar s_v (base64url) */
|
|
429
|
-
response_v: string;
|
|
430
|
-
/** Response scalar s_b (base64url) */
|
|
431
|
-
response_b: string;
|
|
432
|
-
/** Proof generated at */
|
|
433
|
-
generated_at: string;
|
|
229
|
+
/** Approval request sent to the human */
|
|
230
|
+
interface ApprovalRequest {
|
|
231
|
+
operation: string;
|
|
232
|
+
tier: 1 | 2;
|
|
233
|
+
reason: string;
|
|
234
|
+
context: Record<string, unknown>;
|
|
235
|
+
timestamp: string;
|
|
434
236
|
}
|
|
435
|
-
/**
|
|
436
|
-
interface
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
/**
|
|
462
|
-
|
|
237
|
+
/** Approval response from the human */
|
|
238
|
+
interface ApprovalResponse {
|
|
239
|
+
decision: "approve" | "deny";
|
|
240
|
+
decided_at: string;
|
|
241
|
+
decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
|
|
242
|
+
}
|
|
243
|
+
/** Result of the approval gate evaluation */
|
|
244
|
+
interface GateResult {
|
|
245
|
+
allowed: boolean;
|
|
246
|
+
tier: 1 | 2 | 3;
|
|
247
|
+
reason: string;
|
|
248
|
+
approval_required: boolean;
|
|
249
|
+
approval_response?: ApprovalResponse;
|
|
250
|
+
}
|
|
251
|
+
/** Behavioral baseline for anomaly detection */
|
|
252
|
+
interface SessionProfile {
|
|
253
|
+
/** Namespaces accessed (read or write) */
|
|
254
|
+
known_namespaces: string[];
|
|
255
|
+
/** Counterparty DIDs seen in reputation operations */
|
|
256
|
+
known_counterparties: string[];
|
|
257
|
+
/** Tool call counts per tool name (lifetime in session) */
|
|
258
|
+
tool_call_counts: Record<string, number>;
|
|
259
|
+
/** Whether this is the first session (no prior baseline) */
|
|
260
|
+
is_first_session: boolean;
|
|
261
|
+
/** Session start time */
|
|
262
|
+
started_at: string;
|
|
263
|
+
/** When the baseline was last saved */
|
|
264
|
+
saved_at?: string;
|
|
463
265
|
}
|
|
266
|
+
|
|
464
267
|
/**
|
|
465
|
-
*
|
|
466
|
-
*
|
|
467
|
-
* C = v*G + b*H
|
|
468
|
-
*
|
|
469
|
-
* Properties:
|
|
470
|
-
* - Computationally hiding (under discrete log assumption)
|
|
471
|
-
* - Perfectly binding (information-theoretic)
|
|
472
|
-
* - Homomorphic: C(v1) + C(v2) = C(v1+v2) with adjusted blinding
|
|
268
|
+
* Sanctuary MCP Server — Approval Channel
|
|
473
269
|
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
476
|
-
|
|
477
|
-
declare function createPedersenCommitment(value: number): PedersenCommitment;
|
|
478
|
-
/**
|
|
479
|
-
* Verify a Pedersen commitment against a revealed value and blinding factor.
|
|
270
|
+
* Out-of-band communication with the human principal for operation approval.
|
|
271
|
+
* The default channel uses stderr (outside MCP's stdin/stdout protocol),
|
|
272
|
+
* ensuring the agent cannot intercept or forge approval responses.
|
|
480
273
|
*
|
|
481
|
-
*
|
|
274
|
+
* Security invariant:
|
|
275
|
+
* - Approval prompts go through a channel the agent cannot access.
|
|
276
|
+
* - Timeouts result in denial by default (fail closed).
|
|
482
277
|
*/
|
|
483
|
-
|
|
278
|
+
|
|
279
|
+
/** Abstract approval channel interface */
|
|
280
|
+
interface ApprovalChannel {
|
|
281
|
+
requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
282
|
+
}
|
|
484
283
|
/**
|
|
485
|
-
*
|
|
486
|
-
* of a Pedersen commitment C = v*G + b*H.
|
|
487
|
-
*
|
|
488
|
-
* Schnorr sigma protocol with Fiat-Shamir transform:
|
|
489
|
-
* 1. Pick random r_v, r_b
|
|
490
|
-
* 2. Compute R = r_v*G + r_b*H (announcement)
|
|
491
|
-
* 3. Compute e = H_FS(C || R) (challenge via Fiat-Shamir)
|
|
492
|
-
* 4. Compute s_v = r_v + e*v, s_b = r_b + e*b (responses)
|
|
493
|
-
* 5. Proof = (R, s_v, s_b)
|
|
284
|
+
* Stderr approval channel — non-interactive informational channel.
|
|
494
285
|
*
|
|
495
|
-
*
|
|
496
|
-
*
|
|
286
|
+
* In the MCP stdio model:
|
|
287
|
+
* - stdin/stdout carry the MCP protocol (JSON-RPC)
|
|
288
|
+
* - stderr is available for out-of-band human communication
|
|
497
289
|
*
|
|
498
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
|
|
502
|
-
declare function createProofOfKnowledge(value: number, blindingFactor: string, commitment: string): ZKProofOfKnowledge;
|
|
503
|
-
/**
|
|
504
|
-
* Verify a ZK proof of knowledge of a commitment's opening.
|
|
290
|
+
* Because stdin is consumed by the MCP JSON-RPC transport, this channel
|
|
291
|
+
* CANNOT read interactive human input. It is strictly informational:
|
|
292
|
+
* the prompt is displayed so the human sees what is happening, and the
|
|
293
|
+
* operation is denied immediately.
|
|
505
294
|
*
|
|
506
|
-
*
|
|
295
|
+
* SEC-002 + SEC-016 invariants:
|
|
296
|
+
* - This channel ALWAYS denies. No configuration can change this.
|
|
297
|
+
* - There is no timeout or async delay — denial is synchronous.
|
|
298
|
+
* - The `auto_deny` config field is ignored (SEC-002).
|
|
299
|
+
* - For interactive approval, use the dashboard or webhook channel.
|
|
507
300
|
*/
|
|
508
|
-
declare
|
|
301
|
+
declare class StderrApprovalChannel implements ApprovalChannel {
|
|
302
|
+
constructor(_config: ApprovalChannelConfig);
|
|
303
|
+
requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
304
|
+
private formatPrompt;
|
|
305
|
+
}
|
|
509
306
|
/**
|
|
510
|
-
*
|
|
511
|
-
*
|
|
512
|
-
* Approach: bit-decomposition of (value - min) into n bits where 2^n > max - min.
|
|
513
|
-
* Each bit gets a Pedersen commitment and a proof it's 0 or 1.
|
|
514
|
-
* A sum proof shows the bit commitments reconstruct the original commitment
|
|
515
|
-
* (shifted by min).
|
|
516
|
-
*
|
|
517
|
-
* @param value - The committed value
|
|
518
|
-
* @param blindingFactor - The blinding factor (base64url)
|
|
519
|
-
* @param commitment - The commitment (base64url)
|
|
520
|
-
* @param min - Minimum value (inclusive)
|
|
521
|
-
* @param max - Maximum value (inclusive)
|
|
307
|
+
* Programmatic approval channel — for testing and API integration.
|
|
522
308
|
*/
|
|
523
|
-
declare
|
|
524
|
-
|
|
525
|
-
|
|
309
|
+
declare class CallbackApprovalChannel implements ApprovalChannel {
|
|
310
|
+
private callback;
|
|
311
|
+
constructor(callback: (request: ApprovalRequest) => Promise<ApprovalResponse>);
|
|
312
|
+
requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
313
|
+
}
|
|
526
314
|
/**
|
|
527
|
-
*
|
|
315
|
+
* Auto-approve channel — for testing. Approves everything.
|
|
528
316
|
*/
|
|
529
|
-
declare
|
|
317
|
+
declare class AutoApproveChannel implements ApprovalChannel {
|
|
318
|
+
requestApproval(_request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
319
|
+
}
|
|
530
320
|
|
|
531
321
|
/**
|
|
532
|
-
* Sanctuary MCP Server —
|
|
533
|
-
*
|
|
534
|
-
* Disclosure policies define what an agent will and will not disclose
|
|
535
|
-
* in different interaction contexts. Policies are evaluated against
|
|
536
|
-
* incoming disclosure requests to produce per-field decisions.
|
|
322
|
+
* Sanctuary MCP Server — Behavioral Baseline Tracker
|
|
537
323
|
*
|
|
538
|
-
*
|
|
539
|
-
*
|
|
324
|
+
* Tracks the agent's behavioral profile during a session and persists
|
|
325
|
+
* it for cross-session anomaly detection. The baseline defines "normal"
|
|
326
|
+
* so that deviations can trigger Tier 2 approval.
|
|
540
327
|
*
|
|
541
328
|
* Security invariants:
|
|
542
|
-
* -
|
|
543
|
-
* -
|
|
544
|
-
* -
|
|
329
|
+
* - Baseline is stored encrypted under L1 sovereignty
|
|
330
|
+
* - Baseline changes are audit-logged
|
|
331
|
+
* - Baseline is integrity-verified via L1 Merkle tree
|
|
332
|
+
* - No MCP tool can directly modify the baseline
|
|
545
333
|
*/
|
|
546
334
|
|
|
547
|
-
|
|
548
|
-
interface DisclosureRule {
|
|
549
|
-
/** Interaction context this rule applies to */
|
|
550
|
-
context: string;
|
|
551
|
-
/** Fields/claims the agent MAY disclose */
|
|
552
|
-
disclose: string[];
|
|
553
|
-
/** Fields/claims the agent MUST NOT disclose */
|
|
554
|
-
withhold: string[];
|
|
555
|
-
/** Fields that require proof rather than plain disclosure */
|
|
556
|
-
proof_required: string[];
|
|
557
|
-
}
|
|
558
|
-
/** A complete disclosure policy */
|
|
559
|
-
interface DisclosurePolicy {
|
|
560
|
-
policy_id: string;
|
|
561
|
-
policy_name: string;
|
|
562
|
-
rules: DisclosureRule[];
|
|
563
|
-
default_action: "withhold" | "ask-principal";
|
|
564
|
-
identity_id?: string;
|
|
565
|
-
created_at: string;
|
|
566
|
-
updated_at: string;
|
|
567
|
-
}
|
|
568
|
-
/**
|
|
569
|
-
* Policy store — manages disclosure policies encrypted under L1 sovereignty.
|
|
570
|
-
*/
|
|
571
|
-
declare class PolicyStore {
|
|
335
|
+
declare class BaselineTracker {
|
|
572
336
|
private storage;
|
|
573
337
|
private encryptionKey;
|
|
574
|
-
private
|
|
338
|
+
private profile;
|
|
339
|
+
/** Sliding window: timestamps of tool calls per tool name (last 60s) */
|
|
340
|
+
private callWindows;
|
|
341
|
+
/** Sliding window: read counts per namespace (last 60s) */
|
|
342
|
+
private readWindows;
|
|
343
|
+
/** Sliding window: sign call timestamps (last 60s) */
|
|
344
|
+
private signWindow;
|
|
575
345
|
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
576
346
|
/**
|
|
577
|
-
*
|
|
347
|
+
* Load the previous session's baseline from storage.
|
|
348
|
+
* If none exists, this is a first session.
|
|
578
349
|
*/
|
|
579
|
-
|
|
350
|
+
load(): Promise<void>;
|
|
580
351
|
/**
|
|
581
|
-
*
|
|
352
|
+
* Save the current baseline to storage (encrypted).
|
|
353
|
+
* Called at session end or periodically.
|
|
582
354
|
*/
|
|
583
|
-
|
|
355
|
+
save(): Promise<void>;
|
|
584
356
|
/**
|
|
585
|
-
*
|
|
357
|
+
* Record a tool call for baseline tracking.
|
|
358
|
+
* Returns anomaly information if applicable.
|
|
586
359
|
*/
|
|
587
|
-
|
|
360
|
+
recordToolCall(toolName: string): void;
|
|
588
361
|
/**
|
|
589
|
-
*
|
|
362
|
+
* Record a namespace access.
|
|
363
|
+
* @returns true if this is a new namespace (not in baseline)
|
|
590
364
|
*/
|
|
591
|
-
|
|
592
|
-
|
|
365
|
+
recordNamespaceAccess(namespace: string): boolean;
|
|
366
|
+
/**
|
|
367
|
+
* Record a namespace read for bulk-read detection.
|
|
368
|
+
* @returns the number of reads in the current 60-second window
|
|
369
|
+
*/
|
|
370
|
+
recordNamespaceRead(namespace: string): number;
|
|
371
|
+
/**
|
|
372
|
+
* Record a counterparty DID interaction.
|
|
373
|
+
* @returns true if this is a new counterparty (not in baseline)
|
|
374
|
+
*/
|
|
375
|
+
recordCounterparty(did: string): boolean;
|
|
376
|
+
/**
|
|
377
|
+
* Record a signing operation.
|
|
378
|
+
* @returns the number of signs in the current 60-second window
|
|
379
|
+
*/
|
|
380
|
+
recordSign(): number;
|
|
381
|
+
/**
|
|
382
|
+
* Get the current call rate for a tool (calls per minute).
|
|
383
|
+
*/
|
|
384
|
+
getCallRate(toolName: string): number;
|
|
385
|
+
/**
|
|
386
|
+
* Get the average call rate across all tools in the baseline.
|
|
387
|
+
*/
|
|
388
|
+
getAverageCallRate(): number;
|
|
389
|
+
/** Whether this is the first session */
|
|
390
|
+
get isFirstSession(): boolean;
|
|
391
|
+
/** Get a read-only view of the current profile */
|
|
392
|
+
getProfile(): SessionProfile;
|
|
593
393
|
}
|
|
594
394
|
|
|
595
395
|
/**
|
|
596
|
-
* Sanctuary MCP Server —
|
|
597
|
-
*
|
|
598
|
-
* Sovereign identity based on Ed25519 keypairs.
|
|
599
|
-
* Private keys are always encrypted at rest — never stored in plaintext.
|
|
396
|
+
* Sanctuary MCP Server — Prompt Injection Detection Layer
|
|
600
397
|
*
|
|
601
|
-
*
|
|
602
|
-
*
|
|
603
|
-
*
|
|
604
|
-
* - Key rotation produces a signed rotation event (verifiable chain)
|
|
605
|
-
*/
|
|
606
|
-
|
|
607
|
-
/** Public identity information (safe to share) */
|
|
608
|
-
interface PublicIdentity {
|
|
609
|
-
identity_id: string;
|
|
610
|
-
label: string;
|
|
611
|
-
public_key: string;
|
|
612
|
-
did: string;
|
|
613
|
-
created_at: string;
|
|
614
|
-
key_type: "ed25519";
|
|
615
|
-
key_protection: "passphrase" | "hardware-key" | "recovery-key";
|
|
616
|
-
}
|
|
617
|
-
/** Stored identity (private key is encrypted) */
|
|
618
|
-
interface StoredIdentity extends PublicIdentity {
|
|
619
|
-
encrypted_private_key: EncryptedPayload;
|
|
620
|
-
/** Previous public keys (for rotation chain verification) */
|
|
621
|
-
rotation_history: Array<{
|
|
622
|
-
old_public_key: string;
|
|
623
|
-
new_public_key: string;
|
|
624
|
-
rotation_event: string;
|
|
625
|
-
rotated_at: string;
|
|
626
|
-
}>;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
/**
|
|
630
|
-
* Sanctuary MCP Server — Sovereignty Health Report (SHR) Types
|
|
398
|
+
* Fast, zero-dependency detection of common prompt injection patterns.
|
|
399
|
+
* Scans tool arguments for role override, security bypass, encoding evasion,
|
|
400
|
+
* data exfiltration, and prompt stuffing signals.
|
|
631
401
|
*
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
* The SHR is signed by one of the instance's Ed25519 identities and can be
|
|
635
|
-
* independently verified by any party without trusting the presenter.
|
|
402
|
+
* SEC-034/SEC-035: Enhanced with Unicode sanitization pre-pass, decoded content
|
|
403
|
+
* re-scanning, token budget analysis, and outbound content scanning.
|
|
636
404
|
*
|
|
637
|
-
*
|
|
405
|
+
* Security invariants:
|
|
406
|
+
* - Always returns a result, never throws
|
|
407
|
+
* - Typical scan completes in < 5ms
|
|
408
|
+
* - False positives minimized via field-aware scanning
|
|
409
|
+
* - Recursive scanning of nested objects/arrays
|
|
410
|
+
* - Outbound scanning catches secret leaks and injection artifact survival
|
|
638
411
|
*/
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
encryption: string;
|
|
645
|
-
key_custody: "self" | "delegated" | "platform";
|
|
646
|
-
integrity: string;
|
|
647
|
-
identity_type: string;
|
|
648
|
-
state_portable: boolean;
|
|
649
|
-
}
|
|
650
|
-
interface SHRLayerL2 {
|
|
651
|
-
status: LayerStatus;
|
|
652
|
-
isolation_type: string;
|
|
653
|
-
attestation_available: boolean;
|
|
654
|
-
/** Model provenance: what inference model(s) power this agent */
|
|
655
|
-
model_provenance?: {
|
|
656
|
-
model_id: string;
|
|
657
|
-
model_name: string;
|
|
658
|
-
provider: string;
|
|
659
|
-
open_weights: boolean;
|
|
660
|
-
open_source: boolean;
|
|
661
|
-
local_inference: boolean;
|
|
662
|
-
weights_hash?: string;
|
|
663
|
-
};
|
|
664
|
-
}
|
|
665
|
-
interface SHRLayerL3 {
|
|
666
|
-
status: LayerStatus;
|
|
667
|
-
proof_system: string;
|
|
668
|
-
selective_disclosure: boolean;
|
|
669
|
-
}
|
|
670
|
-
interface SHRLayerL4 {
|
|
671
|
-
status: LayerStatus;
|
|
672
|
-
reputation_mode: string;
|
|
673
|
-
attestation_format: string;
|
|
674
|
-
reputation_portable: boolean;
|
|
412
|
+
interface InjectionDetectorConfig {
|
|
413
|
+
enabled: boolean;
|
|
414
|
+
sensitivity: "low" | "medium" | "high";
|
|
415
|
+
on_detection: "escalate" | "block" | "log";
|
|
416
|
+
custom_patterns?: string[];
|
|
675
417
|
}
|
|
676
|
-
interface
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
mitigation?: string;
|
|
418
|
+
interface InjectionSignal {
|
|
419
|
+
type: string;
|
|
420
|
+
pattern: string;
|
|
421
|
+
location: string;
|
|
422
|
+
severity: "low" | "medium" | "high";
|
|
682
423
|
}
|
|
683
|
-
interface
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
424
|
+
interface DetectionResult {
|
|
425
|
+
flagged: boolean;
|
|
426
|
+
confidence: number;
|
|
427
|
+
signals: InjectionSignal[];
|
|
428
|
+
recommendation: "allow" | "escalate" | "block";
|
|
688
429
|
}
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
430
|
+
declare class InjectionDetector {
|
|
431
|
+
private config;
|
|
432
|
+
private stats;
|
|
433
|
+
constructor(config?: Partial<InjectionDetectorConfig>);
|
|
434
|
+
/**
|
|
435
|
+
* Scan tool arguments for injection signals.
|
|
436
|
+
* @param toolName Full tool name (e.g., "state_read")
|
|
437
|
+
* @param args Tool arguments
|
|
438
|
+
* @returns DetectionResult with all detected signals
|
|
439
|
+
*/
|
|
440
|
+
scan(toolName: string, args: Record<string, unknown>): DetectionResult;
|
|
441
|
+
/**
|
|
442
|
+
* SEC-035: Scan outbound content for secret leaks, data exfiltration,
|
|
443
|
+
* internal path exposure, and injection artifact survival.
|
|
444
|
+
*
|
|
445
|
+
* @param content The outbound content string to scan
|
|
446
|
+
* @returns DetectionResult with outbound-specific signal types
|
|
447
|
+
*/
|
|
448
|
+
scanOutbound(content: string): DetectionResult;
|
|
449
|
+
/**
|
|
450
|
+
* Recursively scan a value and all nested values.
|
|
451
|
+
*/
|
|
452
|
+
private scanValue;
|
|
453
|
+
/**
|
|
454
|
+
* Scan a single string for injection signals.
|
|
455
|
+
*/
|
|
456
|
+
private scanString;
|
|
457
|
+
/**
|
|
458
|
+
* Count invisible Unicode characters in a string.
|
|
459
|
+
* Includes zero-width chars, soft hyphens, directional marks,
|
|
460
|
+
* variation selectors, and other invisible categories.
|
|
461
|
+
*/
|
|
462
|
+
private countInvisibleChars;
|
|
463
|
+
/**
|
|
464
|
+
* Strip invisible characters from a string for clean pattern matching.
|
|
465
|
+
* Returns a new string with all invisible chars removed.
|
|
466
|
+
*/
|
|
467
|
+
private stripInvisibleChars;
|
|
468
|
+
/**
|
|
469
|
+
* SEC-034: Token budget attack detection.
|
|
470
|
+
* Some Unicode sequences expand dramatically during tokenization (e.g., CJK
|
|
471
|
+
* ideographs, combining characters, emoji sequences). If the estimated token
|
|
472
|
+
* cost per character is anomalously high, this may be a wallet-drain payload.
|
|
473
|
+
*
|
|
474
|
+
* Heuristic: count chars that typically tokenize into multiple tokens.
|
|
475
|
+
* If the ratio of estimated tokens to char count exceeds 3x, flag it.
|
|
476
|
+
*/
|
|
477
|
+
private detectTokenBudgetAttack;
|
|
478
|
+
/**
|
|
479
|
+
* Detect encoded content (base64, hex, HTML entities, URL encoding),
|
|
480
|
+
* decode it, and re-scan the decoded content through injection patterns.
|
|
481
|
+
* If the decoded content contains injection patterns, flag as encoding_evasion.
|
|
482
|
+
*/
|
|
483
|
+
private detectEncodedPayloads;
|
|
484
|
+
/**
|
|
485
|
+
* Check if a string contains any injection patterns (role override or security bypass).
|
|
486
|
+
*/
|
|
487
|
+
private containsInjectionPatterns;
|
|
488
|
+
/**
|
|
489
|
+
* Safely decode a base64 string. Returns null if it's not valid base64
|
|
490
|
+
* or doesn't decode to a meaningful string.
|
|
491
|
+
*/
|
|
492
|
+
private safeBase64Decode;
|
|
493
|
+
/**
|
|
494
|
+
* Safely decode a hex string. Returns null on failure.
|
|
495
|
+
*/
|
|
496
|
+
private safeHexDecode;
|
|
497
|
+
/**
|
|
498
|
+
* Decode HTML numeric entities (&#xHH; and &#DDD;) in a string.
|
|
499
|
+
*/
|
|
500
|
+
private decodeHtmlEntities;
|
|
501
|
+
/**
|
|
502
|
+
* Safely decode a URL-encoded string. Returns null on failure.
|
|
503
|
+
*/
|
|
504
|
+
private safeUrlDecode;
|
|
505
|
+
/**
|
|
506
|
+
* Heuristic: does this look like readable text (vs. binary garbage)?
|
|
507
|
+
* Checks that most characters are printable ASCII or common Unicode.
|
|
508
|
+
*/
|
|
509
|
+
private looksLikeText;
|
|
510
|
+
/**
|
|
511
|
+
* Detect API keys and secrets in outbound content.
|
|
512
|
+
*/
|
|
513
|
+
private detectSecretPatterns;
|
|
514
|
+
/**
|
|
515
|
+
* Detect data exfiltration via markdown images with data-carrying query params.
|
|
516
|
+
*/
|
|
517
|
+
private detectOutboundExfiltration;
|
|
518
|
+
/**
|
|
519
|
+
* Detect internal filesystem path leaks in outbound content.
|
|
520
|
+
*/
|
|
521
|
+
private detectInternalPathLeaks;
|
|
522
|
+
/**
|
|
523
|
+
* Detect private IP addresses and localhost references in outbound content.
|
|
524
|
+
*/
|
|
525
|
+
private detectPrivateNetworkLeaks;
|
|
526
|
+
/**
|
|
527
|
+
* Detect role markers / prompt template artifacts in outbound content.
|
|
528
|
+
* These should never appear in agent output — their presence indicates
|
|
529
|
+
* injection artifact survival.
|
|
530
|
+
*/
|
|
531
|
+
private detectOutputRoleMarkers;
|
|
532
|
+
/**
|
|
533
|
+
* Detect base64 strings, base64url, and zero-width character evasion.
|
|
534
|
+
*/
|
|
535
|
+
private detectEncodingEvasion;
|
|
536
|
+
/**
|
|
537
|
+
* Detect URLs and emails in fields that shouldn't have them.
|
|
538
|
+
*/
|
|
539
|
+
private detectDataExfiltration;
|
|
540
|
+
/**
|
|
541
|
+
* Detect prompt stuffing: very large strings or high repetition.
|
|
542
|
+
*/
|
|
543
|
+
private detectPromptStuffing;
|
|
544
|
+
/**
|
|
545
|
+
* Determine if this field is inherently safe from role override.
|
|
546
|
+
*/
|
|
547
|
+
private isSafeField;
|
|
548
|
+
/**
|
|
549
|
+
* Determine if this is a tool name field (where tool refs are expected).
|
|
550
|
+
*/
|
|
551
|
+
private isToolNameField;
|
|
552
|
+
/**
|
|
553
|
+
* Determine if this field is safe for URLs.
|
|
554
|
+
*/
|
|
555
|
+
private isUrlSafeField;
|
|
556
|
+
/**
|
|
557
|
+
* Determine if this field is safe for emails.
|
|
558
|
+
*/
|
|
559
|
+
private isEmailSafeField;
|
|
560
|
+
/**
|
|
561
|
+
* Determine if this field is safe for structured data (JSON/XML).
|
|
562
|
+
*/
|
|
563
|
+
private isStructuredField;
|
|
564
|
+
/**
|
|
565
|
+
* SEC-032/SEC-034: Map common cross-script confusable characters to their
|
|
566
|
+
* Latin equivalents. NFKC normalization handles fullwidth and compatibility
|
|
567
|
+
* forms, but does NOT map Cyrillic/Greek/Armenian/Georgian lookalikes to
|
|
568
|
+
* Latin (they're distinct codepoints by design).
|
|
569
|
+
*
|
|
570
|
+
* Extended to 50+ confusable pairs covering Cyrillic, Greek, Armenian,
|
|
571
|
+
* Georgian, Cherokee, and mathematical/symbol lookalikes.
|
|
572
|
+
*/
|
|
573
|
+
private normalizeConfusables;
|
|
574
|
+
/**
|
|
575
|
+
* Compute confidence score based on signals.
|
|
576
|
+
* More high-severity signals = higher confidence.
|
|
577
|
+
*/
|
|
578
|
+
private computeConfidence;
|
|
579
|
+
/**
|
|
580
|
+
* Compute recommendation based on signals and sensitivity.
|
|
581
|
+
*/
|
|
582
|
+
private computeRecommendation;
|
|
583
|
+
/**
|
|
584
|
+
* Get statistics about scans performed.
|
|
585
|
+
*/
|
|
586
|
+
getStats(): {
|
|
587
|
+
total_scans: number;
|
|
588
|
+
total_flags: number;
|
|
589
|
+
total_blocks: number;
|
|
590
|
+
signals_by_type: Record<string, number>;
|
|
708
591
|
};
|
|
709
|
-
|
|
710
|
-
|
|
592
|
+
/**
|
|
593
|
+
* Reset statistics.
|
|
594
|
+
*/
|
|
595
|
+
resetStats(): void;
|
|
711
596
|
}
|
|
597
|
+
|
|
712
598
|
/**
|
|
713
|
-
*
|
|
599
|
+
* Sanctuary MCP Server — Approval Gate
|
|
600
|
+
*
|
|
601
|
+
* The three-tier approval gate sits between the MCP router and tool handlers.
|
|
602
|
+
* Every tool call passes through the gate before execution.
|
|
603
|
+
*
|
|
604
|
+
* Evaluation order:
|
|
605
|
+
* 1. Tier 1: Is this operation in the always-approve list? → Request approval.
|
|
606
|
+
* 2. Tier 2: Does this call represent a behavioral anomaly? → Request approval.
|
|
607
|
+
* 3. Tier 3 / default: Allow with audit logging.
|
|
608
|
+
*
|
|
609
|
+
* Security invariants:
|
|
610
|
+
* - The gate cannot be bypassed — it wraps every tool handler.
|
|
611
|
+
* - Denial responses do not reveal policy details to the agent.
|
|
612
|
+
* - All gate decisions (approve, deny, allow) are audit-logged.
|
|
714
613
|
*/
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
614
|
+
|
|
615
|
+
/** Callback invoked when an injection is detected, for dashboard broadcasting */
|
|
616
|
+
type InjectionAlertCallback = (alert: {
|
|
617
|
+
toolName: string;
|
|
618
|
+
result: DetectionResult;
|
|
619
|
+
timestamp: string;
|
|
620
|
+
}) => void;
|
|
621
|
+
/** Resolver for proxy tool tiers — provided by the ProxyRouter */
|
|
622
|
+
type ProxyTierResolver = (toolName: string) => (1 | 2 | 3) | null;
|
|
623
|
+
declare class ApprovalGate {
|
|
624
|
+
private policy;
|
|
625
|
+
private baseline;
|
|
626
|
+
private channel;
|
|
627
|
+
private auditLog;
|
|
628
|
+
private injectionDetector;
|
|
629
|
+
private onInjectionAlert?;
|
|
630
|
+
private proxyTierResolver?;
|
|
631
|
+
constructor(policy: PrincipalPolicy, baseline: BaselineTracker, channel: ApprovalChannel, auditLog: AuditLog, injectionDetector?: InjectionDetector, onInjectionAlert?: InjectionAlertCallback);
|
|
632
|
+
/**
|
|
633
|
+
* Set the proxy tier resolver. Called after the proxy router is initialized.
|
|
634
|
+
*/
|
|
635
|
+
setProxyTierResolver(resolver: ProxyTierResolver): void;
|
|
636
|
+
/**
|
|
637
|
+
* Evaluate a tool call against the Principal Policy.
|
|
638
|
+
*
|
|
639
|
+
* @param toolName - Full MCP tool name (e.g., "state_export")
|
|
640
|
+
* @param args - Tool call arguments (for context extraction)
|
|
641
|
+
* @returns GateResult indicating whether the call is allowed
|
|
642
|
+
*/
|
|
643
|
+
evaluate(toolName: string, args: Record<string, unknown>): Promise<GateResult>;
|
|
644
|
+
/**
|
|
645
|
+
* Detect Tier 2 behavioral anomalies.
|
|
646
|
+
*/
|
|
647
|
+
private detectAnomaly;
|
|
648
|
+
/**
|
|
649
|
+
* Request approval from the human principal.
|
|
650
|
+
*/
|
|
651
|
+
private requestApproval;
|
|
652
|
+
/**
|
|
653
|
+
* Summarize tool arguments for the approval prompt.
|
|
654
|
+
* Strips potentially large values to keep the prompt readable.
|
|
655
|
+
*/
|
|
656
|
+
private summarizeArgs;
|
|
657
|
+
/** Get the baseline tracker for saving at session end */
|
|
658
|
+
getBaseline(): BaselineTracker;
|
|
659
|
+
/** Get the injection detector for stats/configuration access */
|
|
660
|
+
getInjectionDetector(): InjectionDetector;
|
|
727
661
|
}
|
|
728
662
|
|
|
729
663
|
/**
|
|
730
|
-
* Sanctuary MCP Server —
|
|
664
|
+
* Sanctuary MCP Server — Tool Router
|
|
731
665
|
*
|
|
732
|
-
*
|
|
733
|
-
*
|
|
734
|
-
*
|
|
666
|
+
* Routes sanctuary/* tool calls to their layer-specific handlers.
|
|
667
|
+
* Every tool call passes through schema validation and the ApprovalGate
|
|
668
|
+
* (if configured) before execution. Neither can be bypassed.
|
|
735
669
|
*
|
|
736
|
-
*
|
|
737
|
-
*
|
|
738
|
-
* B → A: HandshakeResponse (B's SHR + B's nonce + signature over A's nonce)
|
|
739
|
-
* A → B: HandshakeCompletion (signature over B's nonce)
|
|
740
|
-
* Result: Both hold a HandshakeResult with verified counterparty status
|
|
670
|
+
* This module is the abstraction boundary for MCP SDK version migration —
|
|
671
|
+
* if the SDK API changes, only this module needs updating.
|
|
741
672
|
*/
|
|
742
673
|
|
|
743
|
-
/**
|
|
744
|
-
type
|
|
745
|
-
|
|
746
|
-
type
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
/**
|
|
757
|
-
* Step 2: Responder sends response
|
|
758
|
-
*/
|
|
759
|
-
interface HandshakeResponse {
|
|
760
|
-
protocol_version: "1.0";
|
|
761
|
-
shr: SignedSHR;
|
|
762
|
-
responder_nonce: string;
|
|
763
|
-
initiator_nonce_signature: string;
|
|
764
|
-
responded_at: string;
|
|
765
|
-
}
|
|
766
|
-
/**
|
|
767
|
-
* Step 3: Initiator sends completion
|
|
768
|
-
*/
|
|
769
|
-
interface HandshakeCompletion {
|
|
770
|
-
protocol_version: "1.0";
|
|
771
|
-
responder_nonce_signature: string;
|
|
772
|
-
completed_at: string;
|
|
773
|
-
}
|
|
774
|
-
/**
|
|
775
|
-
* Final result: both parties hold this after a successful handshake
|
|
776
|
-
*/
|
|
777
|
-
interface HandshakeResult {
|
|
778
|
-
counterparty_id: string;
|
|
779
|
-
counterparty_shr: SignedSHR;
|
|
780
|
-
verified: boolean;
|
|
781
|
-
sovereignty_level: SovereigntyLevel;
|
|
782
|
-
trust_tier: TrustTier;
|
|
783
|
-
completed_at: string;
|
|
784
|
-
expires_at: string;
|
|
785
|
-
errors: string[];
|
|
674
|
+
/** Tool handler function signature */
|
|
675
|
+
type ToolHandler = (args: Record<string, unknown>) => Promise<{
|
|
676
|
+
content: Array<{
|
|
677
|
+
type: "text";
|
|
678
|
+
text: string;
|
|
679
|
+
}>;
|
|
680
|
+
}>;
|
|
681
|
+
/** Tool definition for registration */
|
|
682
|
+
interface ToolDefinition {
|
|
683
|
+
name: string;
|
|
684
|
+
description: string;
|
|
685
|
+
inputSchema: Record<string, unknown>;
|
|
686
|
+
handler: ToolHandler;
|
|
786
687
|
}
|
|
688
|
+
|
|
787
689
|
/**
|
|
788
|
-
*
|
|
690
|
+
* Sanctuary MCP Server — AES-256-GCM Encryption
|
|
691
|
+
*
|
|
692
|
+
* All state encryption in Sanctuary uses AES-256-GCM (authenticated encryption).
|
|
693
|
+
* This provides both confidentiality and integrity — a modified ciphertext will
|
|
694
|
+
* fail authentication, detecting tampering.
|
|
695
|
+
*
|
|
696
|
+
* Security invariants:
|
|
697
|
+
* - Every encryption uses a unique 12-byte IV (NIST SP 800-38D)
|
|
698
|
+
* - The 16-byte authentication tag is always verified on decryption
|
|
699
|
+
* - Keys are 256 bits (32 bytes)
|
|
789
700
|
*/
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
701
|
+
/** Encrypted payload structure stored on disk */
|
|
702
|
+
interface EncryptedPayload {
|
|
703
|
+
/** Format version */
|
|
704
|
+
v: number;
|
|
705
|
+
/** Algorithm identifier */
|
|
706
|
+
alg: "aes-256-gcm";
|
|
707
|
+
/** Initialization vector (base64url) */
|
|
708
|
+
iv: string;
|
|
709
|
+
/** Ciphertext (base64url) */
|
|
710
|
+
ct: string;
|
|
711
|
+
/** Authentication tag (base64url) — included in ciphertext by @noble/ciphers */
|
|
712
|
+
/** Timestamp */
|
|
713
|
+
ts: string;
|
|
800
714
|
}
|
|
801
715
|
|
|
802
716
|
/**
|
|
803
|
-
* Sanctuary MCP Server — Sovereignty
|
|
804
|
-
*
|
|
805
|
-
* Attestations carry a sovereignty_tier field reflecting the signer's
|
|
806
|
-
* sovereignty posture at the time of recording. When querying or evaluating
|
|
807
|
-
* reputation, attestations from verified-sovereign agents carry more weight
|
|
808
|
-
* than those from unverified agents.
|
|
717
|
+
* Sanctuary MCP Server — L1 Cognitive Sovereignty: StateStore
|
|
809
718
|
*
|
|
810
|
-
*
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
* 4. "unverified" — no Sanctuary identity or sovereignty proof
|
|
719
|
+
* The encrypted state store is the foundation of Sanctuary.
|
|
720
|
+
* Every read and write goes through here. All data is encrypted
|
|
721
|
+
* with namespace-specific keys. All writes are signed by an identity.
|
|
722
|
+
* All reads verify integrity via Merkle proofs.
|
|
815
723
|
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
724
|
+
* Security invariants:
|
|
725
|
+
* - Plaintext never touches the filesystem
|
|
726
|
+
* - Every write gets a unique IV
|
|
727
|
+
* - Every write is signed (non-repudiation)
|
|
728
|
+
* - Monotonic version numbers prevent rollback
|
|
729
|
+
* - Merkle tree verifies namespace integrity
|
|
730
|
+
* - Secure deletion overwrites before unlinking
|
|
818
731
|
*/
|
|
819
732
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
733
|
+
/** Result of a state write operation */
|
|
734
|
+
interface WriteResult {
|
|
735
|
+
key: string;
|
|
736
|
+
namespace: string;
|
|
737
|
+
version: number;
|
|
738
|
+
merkle_root: string;
|
|
739
|
+
written_at: string;
|
|
740
|
+
size_bytes: number;
|
|
741
|
+
integrity_hash: string;
|
|
742
|
+
}
|
|
743
|
+
/** Result of a state read operation */
|
|
744
|
+
interface ReadResult {
|
|
745
|
+
key: string;
|
|
746
|
+
namespace: string;
|
|
747
|
+
value: string;
|
|
748
|
+
version: number;
|
|
749
|
+
integrity_verified: boolean;
|
|
750
|
+
merkle_proof: string[];
|
|
751
|
+
written_at: string;
|
|
752
|
+
written_by: string;
|
|
753
|
+
}
|
|
754
|
+
/** Options for state write */
|
|
755
|
+
interface WriteOptions {
|
|
756
|
+
content_type?: string;
|
|
757
|
+
ttl_seconds?: number;
|
|
758
|
+
tags?: string[];
|
|
759
|
+
}
|
|
760
|
+
declare class StateStore {
|
|
761
|
+
private storage;
|
|
762
|
+
private masterKey;
|
|
763
|
+
private versionCache;
|
|
764
|
+
private contentHashes;
|
|
765
|
+
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
766
|
+
private versionKey;
|
|
767
|
+
/**
|
|
768
|
+
* Get or initialize the content hash map for a namespace.
|
|
769
|
+
*/
|
|
770
|
+
private getNamespaceHashes;
|
|
771
|
+
/**
|
|
772
|
+
* Write encrypted state.
|
|
773
|
+
*
|
|
774
|
+
* @param namespace - Logical grouping
|
|
775
|
+
* @param key - State key
|
|
776
|
+
* @param value - Plaintext value (will be encrypted)
|
|
777
|
+
* @param identityId - Identity performing the write
|
|
778
|
+
* @param encryptedPrivateKey - Identity's encrypted private key (for signing)
|
|
779
|
+
* @param identityEncryptionKey - Key to decrypt the identity's private key
|
|
780
|
+
* @param options - Optional metadata
|
|
781
|
+
*/
|
|
782
|
+
write(namespace: string, key: string, value: string, identityId: string, encryptedPrivateKey: EncryptedPayload, identityEncryptionKey: Uint8Array, options?: WriteOptions): Promise<WriteResult>;
|
|
783
|
+
/**
|
|
784
|
+
* Read and decrypt state.
|
|
785
|
+
*
|
|
786
|
+
* @param namespace - Logical grouping
|
|
787
|
+
* @param key - State key
|
|
788
|
+
* @param signerPublicKey - Expected signer's public key (for signature verification)
|
|
789
|
+
* @param verifyIntegrity - Whether to verify Merkle proof (default: true)
|
|
790
|
+
*/
|
|
791
|
+
read(namespace: string, key: string, signerPublicKey?: Uint8Array, verifyIntegrity?: boolean): Promise<ReadResult | null>;
|
|
792
|
+
/**
|
|
793
|
+
* List keys in a namespace (metadata only — no decryption).
|
|
794
|
+
*/
|
|
795
|
+
list(namespace: string, prefix?: string, tags?: string[], limit?: number, offset?: number): Promise<{
|
|
796
|
+
keys: Array<{
|
|
797
|
+
key: string;
|
|
798
|
+
version: number;
|
|
799
|
+
size_bytes: number;
|
|
800
|
+
written_at: string;
|
|
801
|
+
tags: string[];
|
|
802
|
+
}>;
|
|
803
|
+
total: number;
|
|
804
|
+
merkle_root: string;
|
|
805
|
+
}>;
|
|
806
|
+
/**
|
|
807
|
+
* Securely delete state (overwrite with random bytes before removal).
|
|
808
|
+
*/
|
|
809
|
+
delete(namespace: string, key: string): Promise<{
|
|
810
|
+
deleted: boolean;
|
|
811
|
+
key: string;
|
|
812
|
+
namespace: string;
|
|
813
|
+
new_merkle_root: string;
|
|
814
|
+
deleted_at: string;
|
|
815
|
+
}>;
|
|
816
|
+
/**
|
|
817
|
+
* Export all state for a namespace as an encrypted bundle.
|
|
818
|
+
*/
|
|
819
|
+
export(namespace?: string): Promise<{
|
|
820
|
+
bundle: string;
|
|
821
|
+
namespaces: string[];
|
|
822
|
+
total_keys: number;
|
|
823
|
+
bundle_hash: string;
|
|
824
|
+
exported_at: string;
|
|
825
|
+
}>;
|
|
826
|
+
/**
|
|
827
|
+
* Import a previously exported state bundle.
|
|
828
|
+
*/
|
|
829
|
+
import(bundleBase64: string, conflictResolution: "skip" | "overwrite" | "version" | undefined, publicKeyResolver: (kid: string) => Uint8Array | null): Promise<{
|
|
830
|
+
imported_keys: number;
|
|
831
|
+
skipped_keys: number;
|
|
832
|
+
skipped_invalid_sig: number;
|
|
833
|
+
skipped_unknown_kid: number;
|
|
834
|
+
conflicts: number;
|
|
835
|
+
namespaces: string[];
|
|
836
|
+
imported_at: string;
|
|
837
|
+
}>;
|
|
830
838
|
}
|
|
839
|
+
|
|
831
840
|
/**
|
|
832
|
-
*
|
|
841
|
+
* Sanctuary MCP Server — Ed25519 Identity Management
|
|
833
842
|
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
*
|
|
837
|
-
*
|
|
843
|
+
* Sovereign identity based on Ed25519 keypairs.
|
|
844
|
+
* Private keys are always encrypted at rest — never stored in plaintext.
|
|
845
|
+
*
|
|
846
|
+
* Security invariants:
|
|
847
|
+
* - Private keys never appear in any MCP tool response
|
|
848
|
+
* - Private keys are encrypted with identity-specific keys derived from the master key
|
|
849
|
+
* - Key rotation produces a signed rotation event (verifiable chain)
|
|
838
850
|
*/
|
|
839
|
-
|
|
840
|
-
/**
|
|
841
|
-
interface
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
851
|
+
|
|
852
|
+
/** Public identity information (safe to share) */
|
|
853
|
+
interface PublicIdentity {
|
|
854
|
+
identity_id: string;
|
|
855
|
+
label: string;
|
|
856
|
+
public_key: string;
|
|
857
|
+
did: string;
|
|
858
|
+
created_at: string;
|
|
859
|
+
key_type: "ed25519";
|
|
860
|
+
key_protection: "passphrase" | "hardware-key" | "recovery-key";
|
|
861
|
+
}
|
|
862
|
+
/** Stored identity (private key is encrypted) */
|
|
863
|
+
interface StoredIdentity extends PublicIdentity {
|
|
864
|
+
encrypted_private_key: EncryptedPayload;
|
|
865
|
+
/** Previous public keys (for rotation chain verification) */
|
|
866
|
+
rotation_history: Array<{
|
|
867
|
+
old_public_key: string;
|
|
868
|
+
new_public_key: string;
|
|
869
|
+
rotation_event: string;
|
|
870
|
+
rotated_at: string;
|
|
871
|
+
}>;
|
|
846
872
|
}
|
|
873
|
+
|
|
847
874
|
/**
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
* Each attestation's contribution is multiplied by its tier weight.
|
|
851
|
-
* The result is normalized by total weight (not count), so adding
|
|
852
|
-
* low-tier attestations doesn't dilute high-tier ones.
|
|
875
|
+
* Sanctuary MCP Server — L1 Cognitive Sovereignty: Tool Definitions
|
|
853
876
|
*
|
|
854
|
-
*
|
|
855
|
-
*
|
|
856
|
-
*/
|
|
857
|
-
declare function computeWeightedScore(attestations: TieredAttestation[]): number | null;
|
|
858
|
-
/**
|
|
859
|
-
* Compute a tier distribution summary for a set of attestations.
|
|
877
|
+
* MCP tool wrappers for StateStore and IdentityRoot operations.
|
|
878
|
+
* These tools are the public API that agents interact with.
|
|
860
879
|
*/
|
|
861
|
-
|
|
880
|
+
|
|
881
|
+
/** Manages all identities — provides storage and retrieval */
|
|
882
|
+
declare class IdentityManager {
|
|
883
|
+
private storage;
|
|
884
|
+
private masterKey;
|
|
885
|
+
private identities;
|
|
886
|
+
private primaryIdentityId;
|
|
887
|
+
private metadataKey;
|
|
888
|
+
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
889
|
+
private get encryptionKey();
|
|
890
|
+
/** Load identities from storage on startup.
|
|
891
|
+
* Returns { total: number of encrypted files found, loaded: number successfully decrypted }.
|
|
892
|
+
* A mismatch (total > 0, loaded === 0) indicates a wrong master key / missing passphrase.
|
|
893
|
+
*/
|
|
894
|
+
load(): Promise<{
|
|
895
|
+
total: number;
|
|
896
|
+
loaded: number;
|
|
897
|
+
failed: number;
|
|
898
|
+
}>;
|
|
899
|
+
/** Save an identity to storage */
|
|
900
|
+
save(identity: StoredIdentity): Promise<void>;
|
|
901
|
+
/** Set which identity is the default/primary identity */
|
|
902
|
+
setPrimary(identityId: string): Promise<boolean>;
|
|
903
|
+
/** Persist the primary identity ID to storage */
|
|
904
|
+
private savePrimaryIdentityId;
|
|
905
|
+
get(id: string): StoredIdentity | undefined;
|
|
906
|
+
getDefault(): StoredIdentity | undefined;
|
|
907
|
+
getPrimaryIdentityId(): string | null;
|
|
908
|
+
list(): PublicIdentity[];
|
|
909
|
+
}
|
|
862
910
|
|
|
863
911
|
/**
|
|
864
|
-
* Sanctuary MCP Server —
|
|
912
|
+
* Sanctuary MCP Server — L3 Selective Disclosure: Commitment Schemes
|
|
865
913
|
*
|
|
866
|
-
*
|
|
867
|
-
*
|
|
914
|
+
* Cryptographic commitments allow an agent to commit to a value
|
|
915
|
+
* without revealing it, then later prove what was committed.
|
|
868
916
|
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
917
|
+
* This is the MVS approach to selective disclosure — simpler than
|
|
918
|
+
* full ZK proofs but still cryptographically sound. The commitment
|
|
919
|
+
* is SHA-256(value || blinding_factor), which is:
|
|
920
|
+
* - Hiding: the commitment reveals nothing about the value
|
|
921
|
+
* - Binding: the committer cannot change the value after committing
|
|
871
922
|
*
|
|
872
923
|
* Security invariants:
|
|
873
|
-
* -
|
|
874
|
-
* -
|
|
875
|
-
* -
|
|
876
|
-
* - Export bundles include all signatures for independent verification
|
|
877
|
-
* - Import verifies every signature before accepting attestations
|
|
924
|
+
* - Blinding factors are cryptographically random (32 bytes)
|
|
925
|
+
* - Commitments are stored encrypted under L1 sovereignty
|
|
926
|
+
* - Revealed values are verified via constant-time comparison
|
|
878
927
|
*/
|
|
879
928
|
|
|
880
|
-
/**
|
|
881
|
-
interface
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
/**
|
|
887
|
-
|
|
888
|
-
attestation_id: string;
|
|
889
|
-
schema: "sanctuary-interaction-v1";
|
|
890
|
-
data: {
|
|
891
|
-
interaction_id: string;
|
|
892
|
-
participant_did: string;
|
|
893
|
-
counterparty_did: string;
|
|
894
|
-
outcome_type: string;
|
|
895
|
-
outcome_result: string;
|
|
896
|
-
metrics: Record<string, number>;
|
|
897
|
-
context: string;
|
|
898
|
-
timestamp: string;
|
|
899
|
-
/** Sovereignty tier of the signer at time of recording */
|
|
900
|
-
sovereignty_tier?: SovereigntyTier;
|
|
901
|
-
};
|
|
902
|
-
signature: string;
|
|
903
|
-
signer: string;
|
|
904
|
-
}
|
|
905
|
-
/** Stored attestation (encrypted at rest) */
|
|
906
|
-
interface StoredAttestation {
|
|
907
|
-
attestation: Attestation;
|
|
908
|
-
counterparty_attestation?: string;
|
|
909
|
-
counterparty_confirmed: boolean;
|
|
910
|
-
recorded_at: string;
|
|
911
|
-
}
|
|
912
|
-
/** Aggregated metric statistics */
|
|
913
|
-
interface MetricAggregate {
|
|
914
|
-
mean: number;
|
|
915
|
-
median: number;
|
|
916
|
-
min: number;
|
|
917
|
-
max: number;
|
|
918
|
-
count: number;
|
|
919
|
-
}
|
|
920
|
-
/** Reputation query result */
|
|
921
|
-
interface ReputationSummary {
|
|
922
|
-
total_interactions: number;
|
|
923
|
-
completed: number;
|
|
924
|
-
partial: number;
|
|
925
|
-
failed: number;
|
|
926
|
-
disputed: number;
|
|
927
|
-
contexts: string[];
|
|
928
|
-
time_range: {
|
|
929
|
-
start: string;
|
|
930
|
-
end: string;
|
|
931
|
-
};
|
|
932
|
-
aggregate_metrics: Record<string, MetricAggregate>;
|
|
933
|
-
}
|
|
934
|
-
/** Portable reputation bundle */
|
|
935
|
-
interface ReputationBundle {
|
|
936
|
-
version: "SANCTUARY_REP_V1";
|
|
937
|
-
attestations: Attestation[];
|
|
938
|
-
exported_at: string;
|
|
939
|
-
exporter_did: string;
|
|
940
|
-
bundle_signature: string;
|
|
941
|
-
}
|
|
942
|
-
/** Escrow for trust bootstrapping */
|
|
943
|
-
interface Escrow {
|
|
944
|
-
escrow_id: string;
|
|
945
|
-
transaction_terms: string;
|
|
946
|
-
terms_hash: string;
|
|
947
|
-
collateral_amount?: number;
|
|
948
|
-
counterparty_did: string;
|
|
949
|
-
creator_did: string;
|
|
950
|
-
created_at: string;
|
|
951
|
-
expires_at: string;
|
|
952
|
-
status: "pending" | "active" | "released" | "disputed" | "expired";
|
|
929
|
+
/** A cryptographic commitment */
|
|
930
|
+
interface Commitment {
|
|
931
|
+
/** The commitment hash: SHA-256(value || blinding_factor) as base64url */
|
|
932
|
+
commitment: string;
|
|
933
|
+
/** The blinding factor (must be stored securely for later reveal) */
|
|
934
|
+
blinding_factor: string;
|
|
935
|
+
/** When the commitment was created */
|
|
936
|
+
committed_at: string;
|
|
953
937
|
}
|
|
954
|
-
/**
|
|
955
|
-
interface
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
certificate: string;
|
|
963
|
-
created_at: string;
|
|
938
|
+
/** Stored commitment metadata (encrypted at rest) */
|
|
939
|
+
interface StoredCommitment {
|
|
940
|
+
commitment: string;
|
|
941
|
+
blinding_factor: string;
|
|
942
|
+
value: string;
|
|
943
|
+
committed_at: string;
|
|
944
|
+
revealed: boolean;
|
|
945
|
+
revealed_at?: string;
|
|
964
946
|
}
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
record(interactionId: string, counterpartyDid: string, outcome: InteractionOutcome, context: string, identity: StoredIdentity, identityEncryptionKey: Uint8Array, counterpartyAttestation?: string, sovereigntyTier?: SovereigntyTier): Promise<StoredAttestation>;
|
|
973
|
-
/**
|
|
974
|
-
* Query reputation data with filtering.
|
|
975
|
-
* Returns aggregates only — not raw interaction data.
|
|
976
|
-
*/
|
|
977
|
-
query(options: {
|
|
978
|
-
context?: string;
|
|
979
|
-
time_range?: {
|
|
980
|
-
start: string;
|
|
981
|
-
end: string;
|
|
982
|
-
};
|
|
983
|
-
metrics?: string[];
|
|
984
|
-
counterparty_did?: string;
|
|
985
|
-
}): Promise<ReputationSummary>;
|
|
986
|
-
/**
|
|
987
|
-
* Export attestations as a portable reputation bundle.
|
|
988
|
-
*/
|
|
989
|
-
exportBundle(identity: StoredIdentity, identityEncryptionKey: Uint8Array, context?: string): Promise<ReputationBundle>;
|
|
990
|
-
/**
|
|
991
|
-
* Import attestations from a reputation bundle.
|
|
992
|
-
* Verifies signatures if requested (default: true).
|
|
993
|
-
*
|
|
994
|
-
* @param publicKeys - Map of DID → public key bytes for signature verification
|
|
995
|
-
*/
|
|
996
|
-
importBundle(bundle: ReputationBundle, verifySignatures: boolean, publicKeys: Map<string, Uint8Array>): Promise<{
|
|
997
|
-
imported: number;
|
|
998
|
-
invalid: number;
|
|
999
|
-
contexts: string[];
|
|
1000
|
-
}>;
|
|
1001
|
-
/**
|
|
1002
|
-
* Create an escrow for trust bootstrapping.
|
|
1003
|
-
*/
|
|
1004
|
-
createEscrow(transactionTerms: string, counterpartyDid: string, timeoutSeconds: number, creatorDid: string, collateralAmount?: number): Promise<Escrow>;
|
|
947
|
+
/**
|
|
948
|
+
* Commitment store — manages commitments encrypted under L1 sovereignty.
|
|
949
|
+
*/
|
|
950
|
+
declare class CommitmentStore {
|
|
951
|
+
private storage;
|
|
952
|
+
private encryptionKey;
|
|
953
|
+
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
1005
954
|
/**
|
|
1006
|
-
*
|
|
955
|
+
* Store a commitment (encrypted) for later reference.
|
|
1007
956
|
*/
|
|
1008
|
-
|
|
957
|
+
store(commitment: Commitment, value: string): Promise<string>;
|
|
1009
958
|
/**
|
|
1010
|
-
*
|
|
959
|
+
* Retrieve a stored commitment by ID.
|
|
1011
960
|
*/
|
|
1012
|
-
|
|
961
|
+
get(id: string): Promise<StoredCommitment | null>;
|
|
1013
962
|
/**
|
|
1014
|
-
*
|
|
1015
|
-
* Applies basic context/counterparty filtering, returns full StoredAttestations
|
|
1016
|
-
* so callers can access sovereignty_tier from attestation data.
|
|
963
|
+
* Mark a commitment as revealed.
|
|
1017
964
|
*/
|
|
1018
|
-
|
|
1019
|
-
context?: string;
|
|
1020
|
-
counterparty_did?: string;
|
|
1021
|
-
}): Promise<StoredAttestation[]>;
|
|
1022
|
-
private loadAll;
|
|
965
|
+
markRevealed(id: string): Promise<void>;
|
|
1023
966
|
}
|
|
1024
967
|
|
|
1025
968
|
/**
|
|
1026
|
-
* Sanctuary MCP Server —
|
|
969
|
+
* Sanctuary MCP Server — L3 Selective Disclosure: Zero-Knowledge Proofs
|
|
1027
970
|
*
|
|
1028
|
-
*
|
|
1029
|
-
*
|
|
1030
|
-
* 2. Establish mutual trust (sovereignty handshake)
|
|
1031
|
-
* 3. Exchange signed reputation attestations
|
|
1032
|
-
* 4. Evaluate trust for cross-instance interactions
|
|
971
|
+
* Upgrades the commitment-only L3 to support real zero-knowledge proofs.
|
|
972
|
+
* Uses Ristretto255 (prime-order curve group, no cofactor issues) for:
|
|
1033
973
|
*
|
|
1034
|
-
*
|
|
1035
|
-
*
|
|
974
|
+
* 1. Pedersen commitments: C = v*G + b*H (computationally hiding, perfectly binding)
|
|
975
|
+
* 2. ZK proof of knowledge: Schnorr sigma protocol via Fiat-Shamir
|
|
976
|
+
* 3. ZK range proofs: Prove value ∈ [min, max] without revealing it
|
|
1036
977
|
*
|
|
1037
|
-
*
|
|
1038
|
-
*
|
|
1039
|
-
*
|
|
978
|
+
* Ristretto255 is available via @noble/curves/ed25519, which we already depend on.
|
|
979
|
+
* This is genuine zero-knowledge — proofs reveal nothing beyond the stated property.
|
|
980
|
+
*
|
|
981
|
+
* Architecture note:
|
|
982
|
+
* The existing commitment scheme (SHA-256 based) remains available for backward
|
|
983
|
+
* compatibility. The ZK proofs operate on a separate Pedersen commitment system
|
|
984
|
+
* that provides algebraic structure for proper ZK properties.
|
|
985
|
+
*
|
|
986
|
+
* Security invariants:
|
|
987
|
+
* - Generator H is derived via hash-to-curve (nothing-up-my-sleeve)
|
|
988
|
+
* - Blinding factors are cryptographically random (32 bytes)
|
|
989
|
+
* - Fiat-Shamir challenges use domain-separated hashing
|
|
990
|
+
* - Range proofs use a bit-decomposition approach (sound but logarithmic size)
|
|
1040
991
|
*/
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
first_seen: string;
|
|
1050
|
-
/** When last handshake completed */
|
|
1051
|
-
last_handshake: string;
|
|
1052
|
-
/** Current trust tier from most recent handshake */
|
|
1053
|
-
trust_tier: SovereigntyTier;
|
|
1054
|
-
/** Handshake result (for tier resolution) */
|
|
1055
|
-
handshake_result: HandshakeResult;
|
|
1056
|
-
/** Peer's advertised capabilities (from their SIM) */
|
|
1057
|
-
capabilities: FederationCapabilities;
|
|
1058
|
-
/** Whether we have a valid (non-expired) handshake */
|
|
1059
|
-
active: boolean;
|
|
992
|
+
/** A Pedersen commitment: C = v*G + b*H */
|
|
993
|
+
interface PedersenCommitment {
|
|
994
|
+
/** The commitment point (encoded as base64url) */
|
|
995
|
+
commitment: string;
|
|
996
|
+
/** The blinding factor b (base64url, 32 bytes) — keep secret */
|
|
997
|
+
blinding_factor: string;
|
|
998
|
+
/** When the commitment was created */
|
|
999
|
+
committed_at: string;
|
|
1060
1000
|
}
|
|
1061
|
-
/**
|
|
1062
|
-
interface
|
|
1063
|
-
/**
|
|
1064
|
-
|
|
1065
|
-
/**
|
|
1066
|
-
|
|
1067
|
-
/**
|
|
1068
|
-
|
|
1069
|
-
/**
|
|
1070
|
-
|
|
1001
|
+
/** A non-interactive ZK proof of knowledge of a commitment's opening */
|
|
1002
|
+
interface ZKProofOfKnowledge {
|
|
1003
|
+
/** Proof type identifier */
|
|
1004
|
+
type: "schnorr-pedersen-ristretto255";
|
|
1005
|
+
/** The commitment this proof is for */
|
|
1006
|
+
commitment: string;
|
|
1007
|
+
/** Announcement point R (base64url) */
|
|
1008
|
+
announcement: string;
|
|
1009
|
+
/** Response scalar s_v (base64url) */
|
|
1010
|
+
response_v: string;
|
|
1011
|
+
/** Response scalar s_b (base64url) */
|
|
1012
|
+
response_b: string;
|
|
1013
|
+
/** Proof generated at */
|
|
1014
|
+
generated_at: string;
|
|
1071
1015
|
}
|
|
1072
|
-
/**
|
|
1073
|
-
interface
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1016
|
+
/** A ZK range proof: proves value ∈ [min, max] */
|
|
1017
|
+
interface ZKRangeProof {
|
|
1018
|
+
/** Proof type identifier */
|
|
1019
|
+
type: "range-pedersen-ristretto255";
|
|
1020
|
+
/** The commitment this proof is for */
|
|
1021
|
+
commitment: string;
|
|
1022
|
+
/** Minimum value (inclusive) */
|
|
1023
|
+
min: number;
|
|
1024
|
+
/** Maximum value (inclusive) */
|
|
1025
|
+
max: number;
|
|
1026
|
+
/** Bit commitments for the shifted value (v - min) */
|
|
1027
|
+
bit_commitments: string[];
|
|
1028
|
+
/** Proofs that each bit commitment is 0 or 1 */
|
|
1029
|
+
bit_proofs: Array<{
|
|
1030
|
+
announcement_0: string;
|
|
1031
|
+
announcement_1: string;
|
|
1032
|
+
challenge_0: string;
|
|
1033
|
+
challenge_1: string;
|
|
1034
|
+
response_0: string;
|
|
1035
|
+
response_1: string;
|
|
1036
|
+
}>;
|
|
1037
|
+
/** Sum proof: bit commitments sum to the value commitment */
|
|
1038
|
+
sum_proof: {
|
|
1039
|
+
announcement: string;
|
|
1040
|
+
response: string;
|
|
1041
|
+
};
|
|
1042
|
+
/** Proof generated at */
|
|
1043
|
+
generated_at: string;
|
|
1089
1044
|
}
|
|
1090
|
-
|
|
1091
1045
|
/**
|
|
1092
|
-
*
|
|
1046
|
+
* Create a Pedersen commitment to a numeric value.
|
|
1093
1047
|
*
|
|
1094
|
-
*
|
|
1095
|
-
* and tracked for ongoing federation operations.
|
|
1048
|
+
* C = v*G + b*H
|
|
1096
1049
|
*
|
|
1097
|
-
*
|
|
1098
|
-
* -
|
|
1099
|
-
* -
|
|
1100
|
-
* -
|
|
1050
|
+
* Properties:
|
|
1051
|
+
* - Computationally hiding (under discrete log assumption)
|
|
1052
|
+
* - Perfectly binding (information-theoretic)
|
|
1053
|
+
* - Homomorphic: C(v1) + C(v2) = C(v1+v2) with adjusted blinding
|
|
1101
1054
|
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
* - Trust tiers degrade automatically when handshakes expire
|
|
1105
|
-
* - Peer data is stored encrypted under L1 sovereignty
|
|
1055
|
+
* @param value - The value to commit to (integer)
|
|
1056
|
+
* @returns The commitment and blinding factor
|
|
1106
1057
|
*/
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1058
|
+
declare function createPedersenCommitment(value: number): PedersenCommitment;
|
|
1059
|
+
/**
|
|
1060
|
+
* Verify a Pedersen commitment against a revealed value and blinding factor.
|
|
1061
|
+
*
|
|
1062
|
+
* Recomputes C' = v*G + b*H and checks C' == C.
|
|
1063
|
+
*/
|
|
1064
|
+
declare function verifyPedersenCommitment(commitment: string, value: number, blindingFactor: string): boolean;
|
|
1065
|
+
/**
|
|
1066
|
+
* Create a non-interactive ZK proof that you know the opening (v, b)
|
|
1067
|
+
* of a Pedersen commitment C = v*G + b*H.
|
|
1068
|
+
*
|
|
1069
|
+
* Schnorr sigma protocol with Fiat-Shamir transform:
|
|
1070
|
+
* 1. Pick random r_v, r_b
|
|
1071
|
+
* 2. Compute R = r_v*G + r_b*H (announcement)
|
|
1072
|
+
* 3. Compute e = H_FS(C || R) (challenge via Fiat-Shamir)
|
|
1073
|
+
* 4. Compute s_v = r_v + e*v, s_b = r_b + e*b (responses)
|
|
1074
|
+
* 5. Proof = (R, s_v, s_b)
|
|
1075
|
+
*
|
|
1076
|
+
* Zero-knowledge: the transcript (R, e, s_v, s_b) can be simulated
|
|
1077
|
+
* without knowing (v, b), so it reveals nothing.
|
|
1078
|
+
*
|
|
1079
|
+
* @param value - The committed value
|
|
1080
|
+
* @param blindingFactor - The blinding factor (base64url)
|
|
1081
|
+
* @param commitment - The commitment (base64url)
|
|
1082
|
+
*/
|
|
1083
|
+
declare function createProofOfKnowledge(value: number, blindingFactor: string, commitment: string): ZKProofOfKnowledge;
|
|
1084
|
+
/**
|
|
1085
|
+
* Verify a ZK proof of knowledge of a commitment's opening.
|
|
1086
|
+
*
|
|
1087
|
+
* Check: s_v*G + s_b*H == R + e*C
|
|
1088
|
+
*/
|
|
1089
|
+
declare function verifyProofOfKnowledge(proof: ZKProofOfKnowledge): boolean;
|
|
1090
|
+
/**
|
|
1091
|
+
* Create a ZK range proof: prove value ∈ [min, max] without revealing value.
|
|
1092
|
+
*
|
|
1093
|
+
* Approach: bit-decomposition of (value - min) into n bits where 2^n > max - min.
|
|
1094
|
+
* Each bit gets a Pedersen commitment and a proof it's 0 or 1.
|
|
1095
|
+
* A sum proof shows the bit commitments reconstruct the original commitment
|
|
1096
|
+
* (shifted by min).
|
|
1097
|
+
*
|
|
1098
|
+
* @param value - The committed value
|
|
1099
|
+
* @param blindingFactor - The blinding factor (base64url)
|
|
1100
|
+
* @param commitment - The commitment (base64url)
|
|
1101
|
+
* @param min - Minimum value (inclusive)
|
|
1102
|
+
* @param max - Maximum value (inclusive)
|
|
1103
|
+
*/
|
|
1104
|
+
declare function createRangeProof(value: number, blindingFactor: string, commitment: string, min: number, max: number): ZKRangeProof | {
|
|
1105
|
+
error: string;
|
|
1106
|
+
};
|
|
1107
|
+
/**
|
|
1108
|
+
* Verify a ZK range proof.
|
|
1109
|
+
*/
|
|
1110
|
+
declare function verifyRangeProof(proof: ZKRangeProof): boolean;
|
|
1145
1111
|
|
|
1146
1112
|
/**
|
|
1147
|
-
* Sanctuary MCP Server —
|
|
1148
|
-
*
|
|
1149
|
-
* Context gating controls what information leaves the sovereignty boundary
|
|
1150
|
-
* when an agent makes outbound calls — especially inference calls to remote
|
|
1151
|
-
* LLM providers. This is the "minimum-necessary context" enforcement layer.
|
|
1152
|
-
*
|
|
1153
|
-
* The problem: When an agent sends a request to a remote LLM provider (Claude,
|
|
1154
|
-
* GPT, etc.), most harnesses send the agent's full context — conversation
|
|
1155
|
-
* history, memory, tool results, preferences, internal reasoning. The agent
|
|
1156
|
-
* has no control over what the provider sees.
|
|
1113
|
+
* Sanctuary MCP Server — L3 Selective Disclosure: Disclosure Policies
|
|
1157
1114
|
*
|
|
1158
|
-
*
|
|
1159
|
-
*
|
|
1160
|
-
*
|
|
1161
|
-
* - What must always be redacted (secrets, internal reasoning, PII, etc.)
|
|
1162
|
-
* - What requires transformation (hashing, summarizing, anonymizing)
|
|
1115
|
+
* Disclosure policies define what an agent will and will not disclose
|
|
1116
|
+
* in different interaction contexts. Policies are evaluated against
|
|
1117
|
+
* incoming disclosure requests to produce per-field decisions.
|
|
1163
1118
|
*
|
|
1164
|
-
* This
|
|
1165
|
-
*
|
|
1166
|
-
* to-agent trust negotiation with cryptographic proofs; context gating
|
|
1167
|
-
* handles agent-to-infrastructure information flow.
|
|
1119
|
+
* This is the agent's "privacy preferences" layer — it codifies the
|
|
1120
|
+
* human principal's intent about what information can flow where.
|
|
1168
1121
|
*
|
|
1169
1122
|
* Security invariants:
|
|
1170
|
-
* - Redact rules take absolute priority (like withhold in L3)
|
|
1171
1123
|
* - Policies are stored encrypted under L1 sovereignty
|
|
1172
|
-
* -
|
|
1173
|
-
*
|
|
1174
|
-
* - Default policy: redact everything not explicitly allowed
|
|
1124
|
+
* - Default action is always "withhold" unless explicitly overridden
|
|
1125
|
+
* - Policy evaluation is deterministic (same request → same decision)
|
|
1175
1126
|
*/
|
|
1176
1127
|
|
|
1177
|
-
/**
|
|
1178
|
-
|
|
1179
|
-
/**
|
|
1180
|
-
|
|
1181
|
-
/**
|
|
1182
|
-
|
|
1183
|
-
/**
|
|
1184
|
-
|
|
1185
|
-
/** Fields
|
|
1186
|
-
|
|
1187
|
-
/** Fields/patterns that must be redacted (highest priority) */
|
|
1188
|
-
redact: string[];
|
|
1189
|
-
/** Fields/patterns that should be hashed */
|
|
1190
|
-
hash: string[];
|
|
1191
|
-
/** Fields/patterns that should be summarized (advisory) */
|
|
1192
|
-
summarize: string[];
|
|
1128
|
+
/** A single disclosure rule within a policy */
|
|
1129
|
+
interface DisclosureRule {
|
|
1130
|
+
/** Interaction context this rule applies to */
|
|
1131
|
+
context: string;
|
|
1132
|
+
/** Fields/claims the agent MAY disclose */
|
|
1133
|
+
disclose: string[];
|
|
1134
|
+
/** Fields/claims the agent MUST NOT disclose */
|
|
1135
|
+
withhold: string[];
|
|
1136
|
+
/** Fields that require proof rather than plain disclosure */
|
|
1137
|
+
proof_required: string[];
|
|
1193
1138
|
}
|
|
1194
|
-
/** A complete
|
|
1195
|
-
interface
|
|
1139
|
+
/** A complete disclosure policy */
|
|
1140
|
+
interface DisclosurePolicy {
|
|
1196
1141
|
policy_id: string;
|
|
1197
1142
|
policy_name: string;
|
|
1198
|
-
rules:
|
|
1199
|
-
|
|
1200
|
-
default_action: "redact" | "deny";
|
|
1201
|
-
/** Identity this policy is bound to (optional) */
|
|
1143
|
+
rules: DisclosureRule[];
|
|
1144
|
+
default_action: "withhold" | "ask-principal";
|
|
1202
1145
|
identity_id?: string;
|
|
1203
1146
|
created_at: string;
|
|
1204
1147
|
updated_at: string;
|
|
1205
1148
|
}
|
|
1206
|
-
/** Result of filtering a single field */
|
|
1207
|
-
interface FieldFilterResult {
|
|
1208
|
-
field: string;
|
|
1209
|
-
action: ContextAction;
|
|
1210
|
-
reason: string;
|
|
1211
|
-
/** If action is "hash", contains the hash */
|
|
1212
|
-
hash_value?: string;
|
|
1213
|
-
}
|
|
1214
|
-
/** Result of a full context filter operation */
|
|
1215
|
-
interface ContextFilterResult {
|
|
1216
|
-
policy_id: string;
|
|
1217
|
-
provider: ProviderCategory | string;
|
|
1218
|
-
fields_allowed: number;
|
|
1219
|
-
fields_redacted: number;
|
|
1220
|
-
fields_hashed: number;
|
|
1221
|
-
fields_summarized: number;
|
|
1222
|
-
fields_denied: number;
|
|
1223
|
-
decisions: FieldFilterResult[];
|
|
1224
|
-
/** SHA-256 hash of the original context (for audit trail) */
|
|
1225
|
-
original_context_hash: string;
|
|
1226
|
-
/** SHA-256 hash of the filtered output (for audit trail) */
|
|
1227
|
-
filtered_context_hash: string;
|
|
1228
|
-
filtered_at: string;
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Evaluate a context field against a policy for a given provider.
|
|
1232
|
-
*
|
|
1233
|
-
* Priority order (same as L3 disclosure):
|
|
1234
|
-
* 1. Redact (blocks — highest priority)
|
|
1235
|
-
* 2. Deny (blocks entire request)
|
|
1236
|
-
* 3. Hash (transforms)
|
|
1237
|
-
* 4. Summarize (advisory transform)
|
|
1238
|
-
* 5. Allow (passes through)
|
|
1239
|
-
* 6. Default action
|
|
1240
|
-
*/
|
|
1241
|
-
declare function evaluateField(policy: ContextGatePolicy, provider: ProviderCategory | string, field: string): FieldFilterResult;
|
|
1242
|
-
/**
|
|
1243
|
-
* Filter a full context object against a policy for a given provider.
|
|
1244
|
-
* Returns per-field decisions and content hashes for the audit trail.
|
|
1245
|
-
*/
|
|
1246
|
-
declare function filterContext(policy: ContextGatePolicy, provider: ProviderCategory | string, context: Record<string, unknown>): ContextFilterResult;
|
|
1247
1149
|
/**
|
|
1248
|
-
*
|
|
1150
|
+
* Policy store — manages disclosure policies encrypted under L1 sovereignty.
|
|
1249
1151
|
*/
|
|
1250
|
-
declare class
|
|
1152
|
+
declare class PolicyStore {
|
|
1251
1153
|
private storage;
|
|
1252
1154
|
private encryptionKey;
|
|
1253
1155
|
private policies;
|
|
1254
1156
|
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
1255
1157
|
/**
|
|
1256
|
-
* Create and store a new
|
|
1158
|
+
* Create and store a new disclosure policy.
|
|
1257
1159
|
*/
|
|
1258
|
-
create(policyName: string, rules:
|
|
1160
|
+
create(policyName: string, rules: DisclosureRule[], defaultAction: "withhold" | "ask-principal", identityId?: string): Promise<DisclosurePolicy>;
|
|
1259
1161
|
/**
|
|
1260
1162
|
* Get a policy by ID.
|
|
1261
1163
|
*/
|
|
1262
|
-
get(policyId: string): Promise<
|
|
1164
|
+
get(policyId: string): Promise<DisclosurePolicy | null>;
|
|
1263
1165
|
/**
|
|
1264
|
-
* List all
|
|
1166
|
+
* List all policies.
|
|
1265
1167
|
*/
|
|
1266
|
-
list(): Promise<
|
|
1168
|
+
list(): Promise<DisclosurePolicy[]>;
|
|
1267
1169
|
/**
|
|
1268
1170
|
* Load all persisted policies into memory.
|
|
1269
1171
|
*/
|
|
@@ -1272,736 +1174,871 @@ declare class ContextGatePolicyStore {
|
|
|
1272
1174
|
}
|
|
1273
1175
|
|
|
1274
1176
|
/**
|
|
1275
|
-
* Sanctuary MCP Server —
|
|
1276
|
-
*
|
|
1277
|
-
* Pre-built policies for common use cases. These are starting points —
|
|
1278
|
-
* users should customize them for their specific context structure.
|
|
1279
|
-
*
|
|
1280
|
-
* Templates:
|
|
1281
|
-
*
|
|
1282
|
-
* inference-minimal
|
|
1283
|
-
* Only the current task and query reach the LLM. Everything else
|
|
1284
|
-
* is redacted. Secrets, PII, memory, reasoning, and history are
|
|
1285
|
-
* all blocked. IDs are hashed. Maximum privacy, minimum context.
|
|
1286
|
-
*
|
|
1287
|
-
* inference-standard
|
|
1288
|
-
* Task, query, and tool results pass through. Conversation history
|
|
1289
|
-
* is flagged for summarization (compress before sending). Secrets,
|
|
1290
|
-
* PII, and internal reasoning are redacted. IDs are hashed.
|
|
1291
|
-
* Balanced: the LLM has enough context to be useful without seeing
|
|
1292
|
-
* everything the agent knows.
|
|
1177
|
+
* Sanctuary MCP Server — Sovereignty Health Report (SHR) Types
|
|
1293
1178
|
*
|
|
1294
|
-
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1298
|
-
* content exposure.
|
|
1179
|
+
* Machine-readable, signed, versioned sovereignty capability advertisement.
|
|
1180
|
+
* An agent presents its SHR to counterparties to prove its sovereignty posture.
|
|
1181
|
+
* The SHR is signed by one of the instance's Ed25519 identities and can be
|
|
1182
|
+
* independently verified by any party without trusting the presenter.
|
|
1299
1183
|
*
|
|
1300
|
-
*
|
|
1301
|
-
* Allows tool-specific parameters and the current task, redacts
|
|
1302
|
-
* memory, history, secrets, and PII. Hashes IDs. For outbound
|
|
1303
|
-
* calls to external APIs (search, database, etc.) where you need
|
|
1304
|
-
* to send query parameters but not your agent's full state.
|
|
1184
|
+
* SHR version: 1.0
|
|
1305
1185
|
*/
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1186
|
+
type LayerStatus = "active" | "degraded" | "inactive";
|
|
1187
|
+
type DegradationSeverity = "info" | "warning" | "critical";
|
|
1188
|
+
type DegradationCode = "NO_TEE" | "PROCESS_ISOLATION_ONLY" | "COMMITMENT_ONLY" | "NO_ZK_PROOFS" | "SELF_REPORTED_ATTESTATION" | "NO_SELECTIVE_DISCLOSURE" | "BASIC_SYBIL_ONLY";
|
|
1189
|
+
interface SHRLayerL1 {
|
|
1190
|
+
status: LayerStatus;
|
|
1191
|
+
encryption: string;
|
|
1192
|
+
key_custody: "self" | "delegated" | "platform";
|
|
1193
|
+
integrity: string;
|
|
1194
|
+
identity_type: string;
|
|
1195
|
+
state_portable: boolean;
|
|
1196
|
+
}
|
|
1197
|
+
interface SHRLayerL2 {
|
|
1198
|
+
status: LayerStatus;
|
|
1199
|
+
isolation_type: string;
|
|
1200
|
+
attestation_available: boolean;
|
|
1201
|
+
/** Model provenance: what inference model(s) power this agent */
|
|
1202
|
+
model_provenance?: {
|
|
1203
|
+
model_id: string;
|
|
1204
|
+
model_name: string;
|
|
1205
|
+
provider: string;
|
|
1206
|
+
open_weights: boolean;
|
|
1207
|
+
open_source: boolean;
|
|
1208
|
+
local_inference: boolean;
|
|
1209
|
+
weights_hash?: string;
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
interface SHRLayerL3 {
|
|
1213
|
+
status: LayerStatus;
|
|
1214
|
+
proof_system: string;
|
|
1215
|
+
selective_disclosure: boolean;
|
|
1216
|
+
}
|
|
1217
|
+
interface SHRLayerL4 {
|
|
1218
|
+
status: LayerStatus;
|
|
1219
|
+
reputation_mode: string;
|
|
1220
|
+
attestation_format: string;
|
|
1221
|
+
reputation_portable: boolean;
|
|
1222
|
+
}
|
|
1223
|
+
interface SHRDegradation {
|
|
1224
|
+
layer: "l1" | "l2" | "l3" | "l4";
|
|
1225
|
+
code: DegradationCode;
|
|
1226
|
+
severity: DegradationSeverity;
|
|
1314
1227
|
description: string;
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1228
|
+
mitigation?: string;
|
|
1229
|
+
}
|
|
1230
|
+
interface SHRCapabilities {
|
|
1231
|
+
handshake: boolean;
|
|
1232
|
+
shr_exchange: boolean;
|
|
1233
|
+
reputation_verify: boolean;
|
|
1234
|
+
encrypted_channel: boolean;
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* The SHR body — the content that gets signed.
|
|
1238
|
+
* Canonical form: JSON with sorted keys, no whitespace.
|
|
1239
|
+
*/
|
|
1240
|
+
interface SHRBody {
|
|
1241
|
+
shr_version: "1.0";
|
|
1242
|
+
implementation: {
|
|
1243
|
+
sanctuary_version: string;
|
|
1244
|
+
node_version: string;
|
|
1245
|
+
generated_by: string;
|
|
1246
|
+
};
|
|
1247
|
+
instance_id: string;
|
|
1248
|
+
generated_at: string;
|
|
1249
|
+
expires_at: string;
|
|
1250
|
+
layers: {
|
|
1251
|
+
l1: SHRLayerL1;
|
|
1252
|
+
l2: SHRLayerL2;
|
|
1253
|
+
l3: SHRLayerL3;
|
|
1254
|
+
l4: SHRLayerL4;
|
|
1255
|
+
};
|
|
1256
|
+
capabilities: SHRCapabilities;
|
|
1257
|
+
degradations: SHRDegradation[];
|
|
1258
|
+
}
|
|
1259
|
+
/**
|
|
1260
|
+
* The complete signed SHR — body + signature envelope.
|
|
1261
|
+
*/
|
|
1262
|
+
interface SignedSHR {
|
|
1263
|
+
body: SHRBody;
|
|
1264
|
+
signed_by: string;
|
|
1265
|
+
signature: string;
|
|
1266
|
+
}
|
|
1267
|
+
interface SHRVerificationResult {
|
|
1268
|
+
valid: boolean;
|
|
1269
|
+
errors: string[];
|
|
1270
|
+
warnings: string[];
|
|
1271
|
+
sovereignty_level: "full" | "degraded" | "minimal";
|
|
1272
|
+
counterparty_id: string;
|
|
1273
|
+
expires_at: string;
|
|
1321
1274
|
}
|
|
1322
|
-
/** All available templates, keyed by ID */
|
|
1323
|
-
declare const TEMPLATES: Record<string, ContextGateTemplate>;
|
|
1324
|
-
/** List all available template IDs */
|
|
1325
|
-
declare function listTemplateIds(): string[];
|
|
1326
|
-
/** Get a template by ID (returns undefined if not found) */
|
|
1327
|
-
declare function getTemplate(id: string): ContextGateTemplate | undefined;
|
|
1328
1275
|
|
|
1329
1276
|
/**
|
|
1330
|
-
* Sanctuary MCP Server —
|
|
1331
|
-
*
|
|
1332
|
-
* Analyzes a sample context object and recommends a context-gating policy
|
|
1333
|
-
* based on field name heuristics. The agent (or human) can then review,
|
|
1334
|
-
* adjust, and apply the recommendation.
|
|
1335
|
-
*
|
|
1336
|
-
* This is deliberately conservative: when in doubt, it recommends redact.
|
|
1337
|
-
* A false redaction is a usability issue; a false allow is a privacy leak.
|
|
1277
|
+
* Sanctuary MCP Server — Sovereignty Handshake Types
|
|
1338
1278
|
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
*
|
|
1342
|
-
* - Known internal state patterns → redact (high confidence)
|
|
1343
|
-
* - Known ID patterns → hash (medium confidence)
|
|
1344
|
-
* - Known history patterns → summarize (medium confidence)
|
|
1345
|
-
* - Known task/query patterns → allow (medium confidence)
|
|
1346
|
-
* - Everything else → redact (conservative default)
|
|
1279
|
+
* The sovereignty handshake is a mutual verification protocol between
|
|
1280
|
+
* two Sanctuary instances. Each party presents its SHR and proves
|
|
1281
|
+
* liveness via nonce challenge-response.
|
|
1347
1282
|
*
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
1352
|
-
*
|
|
1283
|
+
* Protocol:
|
|
1284
|
+
* A → B: HandshakeChallenge (A's SHR + nonce)
|
|
1285
|
+
* B → A: HandshakeResponse (B's SHR + B's nonce + signature over A's nonce)
|
|
1286
|
+
* A → B: HandshakeCompletion (signature over B's nonce)
|
|
1287
|
+
* Result: Both hold a HandshakeResult with verified counterparty status
|
|
1353
1288
|
*/
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
confidence: "high" | "medium" | "low";
|
|
1360
|
-
/** Pattern that matched, if any */
|
|
1361
|
-
matched_pattern: string | null;
|
|
1362
|
-
}
|
|
1363
|
-
/** Full recommendation result */
|
|
1364
|
-
interface PolicyRecommendation {
|
|
1365
|
-
provider: string;
|
|
1366
|
-
classifications: FieldClassification[];
|
|
1367
|
-
recommended_rules: {
|
|
1368
|
-
allow: string[];
|
|
1369
|
-
redact: string[];
|
|
1370
|
-
hash: string[];
|
|
1371
|
-
summarize: string[];
|
|
1372
|
-
};
|
|
1373
|
-
default_action: "redact";
|
|
1374
|
-
summary: {
|
|
1375
|
-
total_fields: number;
|
|
1376
|
-
allow: number;
|
|
1377
|
-
redact: number;
|
|
1378
|
-
hash: number;
|
|
1379
|
-
summarize: number;
|
|
1380
|
-
};
|
|
1381
|
-
warnings: string[];
|
|
1382
|
-
}
|
|
1289
|
+
|
|
1290
|
+
/** Trust tier derived from sovereignty handshake */
|
|
1291
|
+
type TrustTier = "verified-sovereign" | "verified-degraded" | "unverified";
|
|
1292
|
+
/** Sovereignty level from SHR assessment */
|
|
1293
|
+
type SovereigntyLevel = "full" | "degraded" | "minimal" | "unverified";
|
|
1383
1294
|
/**
|
|
1384
|
-
*
|
|
1295
|
+
* Step 1: Initiator sends challenge
|
|
1385
1296
|
*/
|
|
1386
|
-
|
|
1297
|
+
interface HandshakeChallenge {
|
|
1298
|
+
protocol_version: "1.0";
|
|
1299
|
+
shr: SignedSHR;
|
|
1300
|
+
nonce: string;
|
|
1301
|
+
initiated_at: string;
|
|
1302
|
+
}
|
|
1387
1303
|
/**
|
|
1388
|
-
*
|
|
1304
|
+
* Step 2: Responder sends response
|
|
1389
1305
|
*/
|
|
1390
|
-
|
|
1391
|
-
|
|
1306
|
+
interface HandshakeResponse {
|
|
1307
|
+
protocol_version: "1.0";
|
|
1308
|
+
shr: SignedSHR;
|
|
1309
|
+
responder_nonce: string;
|
|
1310
|
+
initiator_nonce_signature: string;
|
|
1311
|
+
responded_at: string;
|
|
1312
|
+
}
|
|
1392
1313
|
/**
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
* Declares and attests to the model(s) powering this agent.
|
|
1396
|
-
*
|
|
1397
|
-
* Vitalik Buterin's "Secure LLM" post (April 2026) identified a critical gap:
|
|
1398
|
-
* open-weights-but-not-open-source models can have trained-in backdoors. Model
|
|
1399
|
-
* provenance declaration lets agents and their operators verify the integrity
|
|
1400
|
-
* of the inference backbone.
|
|
1401
|
-
*
|
|
1402
|
-
* Tracks: model name, version, weights hash, license, open-source status,
|
|
1403
|
-
* training data hash (if available). Included in SHR L2 section.
|
|
1404
|
-
*
|
|
1405
|
-
* This sits in L2 (Operational Isolation) because it's part of the runtime
|
|
1406
|
-
* attestation surface — the agent declares what model(s) it's actually running.
|
|
1314
|
+
* Step 3: Initiator sends completion
|
|
1407
1315
|
*/
|
|
1316
|
+
interface HandshakeCompletion {
|
|
1317
|
+
protocol_version: "1.0";
|
|
1318
|
+
responder_nonce_signature: string;
|
|
1319
|
+
completed_at: string;
|
|
1320
|
+
}
|
|
1408
1321
|
/**
|
|
1409
|
-
*
|
|
1322
|
+
* Final result: both parties hold this after a successful handshake
|
|
1410
1323
|
*/
|
|
1411
|
-
interface
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
/** SHA-256 of model weights file, if available and verifiable */
|
|
1421
|
-
weights_hash?: string;
|
|
1422
|
-
/** SHA-256 of training data manifest or metadata, if available */
|
|
1423
|
-
training_data_hash?: string;
|
|
1424
|
-
/** License identifier (e.g., "Apache-2.0", "CC-BY-4.0", "proprietary", "unknown") */
|
|
1425
|
-
license: string;
|
|
1426
|
-
/** True if model weights are publicly available (even if training is proprietary) */
|
|
1427
|
-
open_weights: boolean;
|
|
1428
|
-
/** True if full training code, data, and methodology are publicly available */
|
|
1429
|
-
open_source: boolean;
|
|
1430
|
-
/** True if inference runs on the local agent's hardware (not delegated to cloud API) */
|
|
1431
|
-
local_inference: boolean;
|
|
1432
|
-
/** ISO 8601 timestamp when this provenance was declared */
|
|
1433
|
-
declared_at: string;
|
|
1324
|
+
interface HandshakeResult {
|
|
1325
|
+
counterparty_id: string;
|
|
1326
|
+
counterparty_shr: SignedSHR;
|
|
1327
|
+
verified: boolean;
|
|
1328
|
+
sovereignty_level: SovereigntyLevel;
|
|
1329
|
+
trust_tier: TrustTier;
|
|
1330
|
+
completed_at: string;
|
|
1331
|
+
expires_at: string;
|
|
1332
|
+
errors: string[];
|
|
1434
1333
|
}
|
|
1435
1334
|
/**
|
|
1436
|
-
* In-
|
|
1437
|
-
* Declarations are encrypted under L1 sovereignty.
|
|
1335
|
+
* In-progress handshake state (stored on initiator side)
|
|
1438
1336
|
*/
|
|
1439
|
-
interface
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
* List all declared models.
|
|
1450
|
-
*/
|
|
1451
|
-
list(): ModelProvenance[];
|
|
1452
|
-
/**
|
|
1453
|
-
* Get the primary/main model (the one the agent uses by default for inference).
|
|
1454
|
-
*/
|
|
1455
|
-
primary(): ModelProvenance | undefined;
|
|
1456
|
-
/**
|
|
1457
|
-
* Set which model is the primary.
|
|
1458
|
-
*/
|
|
1459
|
-
setPrimary(model_id: string): void;
|
|
1337
|
+
interface HandshakeSession {
|
|
1338
|
+
session_id: string;
|
|
1339
|
+
role: "initiator" | "responder";
|
|
1340
|
+
state: "initiated" | "responded" | "completed" | "failed";
|
|
1341
|
+
our_nonce: string;
|
|
1342
|
+
their_nonce?: string;
|
|
1343
|
+
our_shr: SignedSHR;
|
|
1344
|
+
their_shr?: SignedSHR;
|
|
1345
|
+
initiated_at: string;
|
|
1346
|
+
result?: HandshakeResult;
|
|
1460
1347
|
}
|
|
1348
|
+
|
|
1461
1349
|
/**
|
|
1462
|
-
*
|
|
1463
|
-
*
|
|
1350
|
+
* Sanctuary MCP Server — Sovereignty-Gated Reputation Tiers
|
|
1351
|
+
*
|
|
1352
|
+
* Attestations carry a sovereignty_tier field reflecting the signer's
|
|
1353
|
+
* sovereignty posture at the time of recording. When querying or evaluating
|
|
1354
|
+
* reputation, attestations from verified-sovereign agents carry more weight
|
|
1355
|
+
* than those from unverified agents.
|
|
1356
|
+
*
|
|
1357
|
+
* Tier hierarchy (descending credibility):
|
|
1358
|
+
* 1. "verified-sovereign" — signer completed a handshake with full sovereignty
|
|
1359
|
+
* 2. "verified-degraded" — signer completed a handshake with degraded sovereignty
|
|
1360
|
+
* 3. "self-attested" — signer has a Sanctuary identity but no handshake verification
|
|
1361
|
+
* 4. "unverified" — no Sanctuary identity or sovereignty proof
|
|
1362
|
+
*
|
|
1363
|
+
* Weight multipliers are applied during reputation scoring. They are NOT
|
|
1364
|
+
* gatekeeping — unverified attestations still count, just less.
|
|
1464
1365
|
*/
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1366
|
+
|
|
1367
|
+
type SovereigntyTier = "verified-sovereign" | "verified-degraded" | "self-attested" | "unverified";
|
|
1368
|
+
/** Weight multipliers for each tier */
|
|
1369
|
+
declare const TIER_WEIGHTS: Record<SovereigntyTier, number>;
|
|
1370
|
+
/** Tier metadata embedded in attestations */
|
|
1371
|
+
interface TierMetadata {
|
|
1372
|
+
sovereignty_tier: SovereigntyTier;
|
|
1373
|
+
/** If verified, the handshake that established it */
|
|
1374
|
+
handshake_completed_at?: string;
|
|
1375
|
+
/** Counterparty ID from handshake (if applicable) */
|
|
1376
|
+
verified_by?: string;
|
|
1473
1377
|
}
|
|
1474
1378
|
/**
|
|
1475
|
-
*
|
|
1379
|
+
* Resolve the sovereignty tier for a counterparty based on handshake history.
|
|
1380
|
+
*
|
|
1381
|
+
* @param counterpartyId - The counterparty's instance ID
|
|
1382
|
+
* @param handshakeResults - Map of counterparty ID → most recent handshake result
|
|
1383
|
+
* @param hasSanctuaryIdentity - Whether the counterparty has a known Sanctuary identity
|
|
1384
|
+
* @returns TierMetadata for embedding in attestations
|
|
1476
1385
|
*/
|
|
1477
|
-
declare
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
/**
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1386
|
+
declare function resolveTier(counterpartyId: string, handshakeResults: Map<string, HandshakeResult>, hasSanctuaryIdentity: boolean): TierMetadata;
|
|
1387
|
+
/** An attestation with its tier for weighted scoring */
|
|
1388
|
+
interface TieredAttestation {
|
|
1389
|
+
/** The raw metric value */
|
|
1390
|
+
value: number;
|
|
1391
|
+
/** The sovereignty tier of the attestation signer */
|
|
1392
|
+
tier: SovereigntyTier;
|
|
1393
|
+
}
|
|
1394
|
+
/**
|
|
1395
|
+
* Compute a weighted reputation score from tiered attestations.
|
|
1396
|
+
*
|
|
1397
|
+
* Each attestation's contribution is multiplied by its tier weight.
|
|
1398
|
+
* The result is normalized by total weight (not count), so adding
|
|
1399
|
+
* low-tier attestations doesn't dilute high-tier ones.
|
|
1400
|
+
*
|
|
1401
|
+
* @param attestations - Array of value + tier pairs
|
|
1402
|
+
* @returns Weighted score, or null if no attestations
|
|
1403
|
+
*/
|
|
1404
|
+
declare function computeWeightedScore(attestations: TieredAttestation[]): number | null;
|
|
1405
|
+
/**
|
|
1406
|
+
* Compute a tier distribution summary for a set of attestations.
|
|
1407
|
+
*/
|
|
1408
|
+
declare function tierDistribution(tiers: SovereigntyTier[]): Record<SovereigntyTier, number>;
|
|
1495
1409
|
|
|
1496
1410
|
/**
|
|
1497
|
-
* Sanctuary MCP Server —
|
|
1411
|
+
* Sanctuary MCP Server — L4 Verifiable Reputation: Reputation Store
|
|
1498
1412
|
*
|
|
1499
|
-
*
|
|
1500
|
-
*
|
|
1501
|
-
* data exfiltration, and prompt stuffing signals.
|
|
1413
|
+
* Records interaction outcomes as signed attestations, queries aggregated
|
|
1414
|
+
* reputation data, and supports export/import for cross-platform portability.
|
|
1502
1415
|
*
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1416
|
+
* Attestation format is EAS-compatible (Ethereum Attestation Service) to
|
|
1417
|
+
* enable future on-chain anchoring without requiring blockchain for MVS.
|
|
1505
1418
|
*
|
|
1506
1419
|
* Security invariants:
|
|
1507
|
-
* -
|
|
1508
|
-
* -
|
|
1509
|
-
* -
|
|
1510
|
-
* -
|
|
1511
|
-
* -
|
|
1420
|
+
* - All attestations are signed by the recording identity
|
|
1421
|
+
* - Attestations are stored encrypted under L1 sovereignty
|
|
1422
|
+
* - Reputation queries return aggregates, never raw interaction data
|
|
1423
|
+
* - Export bundles include all signatures for independent verification
|
|
1424
|
+
* - Import verifies every signature before accepting attestations
|
|
1512
1425
|
*/
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1426
|
+
|
|
1427
|
+
/** Interaction outcome for recording */
|
|
1428
|
+
interface InteractionOutcome {
|
|
1429
|
+
type: "transaction" | "negotiation" | "service" | "dispute" | "custom";
|
|
1430
|
+
result: "completed" | "partial" | "failed" | "disputed";
|
|
1431
|
+
metrics?: Record<string, number>;
|
|
1518
1432
|
}
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1433
|
+
/** A signed attestation of an interaction */
|
|
1434
|
+
interface Attestation {
|
|
1435
|
+
attestation_id: string;
|
|
1436
|
+
schema: "sanctuary-interaction-v1";
|
|
1437
|
+
data: {
|
|
1438
|
+
interaction_id: string;
|
|
1439
|
+
participant_did: string;
|
|
1440
|
+
counterparty_did: string;
|
|
1441
|
+
outcome_type: string;
|
|
1442
|
+
outcome_result: string;
|
|
1443
|
+
metrics: Record<string, number>;
|
|
1444
|
+
context: string;
|
|
1445
|
+
timestamp: string;
|
|
1446
|
+
/** Sovereignty tier of the signer at time of recording */
|
|
1447
|
+
sovereignty_tier?: SovereigntyTier;
|
|
1448
|
+
};
|
|
1449
|
+
signature: string;
|
|
1450
|
+
signer: string;
|
|
1524
1451
|
}
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1452
|
+
/** Stored attestation (encrypted at rest) */
|
|
1453
|
+
interface StoredAttestation {
|
|
1454
|
+
attestation: Attestation;
|
|
1455
|
+
counterparty_attestation?: string;
|
|
1456
|
+
counterparty_confirmed: boolean;
|
|
1457
|
+
recorded_at: string;
|
|
1530
1458
|
}
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1459
|
+
/** Aggregated metric statistics */
|
|
1460
|
+
interface MetricAggregate {
|
|
1461
|
+
mean: number;
|
|
1462
|
+
median: number;
|
|
1463
|
+
min: number;
|
|
1464
|
+
max: number;
|
|
1465
|
+
count: number;
|
|
1466
|
+
}
|
|
1467
|
+
/** Reputation query result */
|
|
1468
|
+
interface ReputationSummary {
|
|
1469
|
+
total_interactions: number;
|
|
1470
|
+
completed: number;
|
|
1471
|
+
partial: number;
|
|
1472
|
+
failed: number;
|
|
1473
|
+
disputed: number;
|
|
1474
|
+
contexts: string[];
|
|
1475
|
+
time_range: {
|
|
1476
|
+
start: string;
|
|
1477
|
+
end: string;
|
|
1478
|
+
};
|
|
1479
|
+
aggregate_metrics: Record<string, MetricAggregate>;
|
|
1480
|
+
}
|
|
1481
|
+
/** Portable reputation bundle */
|
|
1482
|
+
interface ReputationBundle {
|
|
1483
|
+
version: "SANCTUARY_REP_V1";
|
|
1484
|
+
attestations: Attestation[];
|
|
1485
|
+
exported_at: string;
|
|
1486
|
+
exporter_did: string;
|
|
1487
|
+
bundle_signature: string;
|
|
1488
|
+
}
|
|
1489
|
+
/** Escrow for trust bootstrapping */
|
|
1490
|
+
interface Escrow {
|
|
1491
|
+
escrow_id: string;
|
|
1492
|
+
transaction_terms: string;
|
|
1493
|
+
terms_hash: string;
|
|
1494
|
+
collateral_amount?: number;
|
|
1495
|
+
counterparty_did: string;
|
|
1496
|
+
creator_did: string;
|
|
1497
|
+
created_at: string;
|
|
1498
|
+
expires_at: string;
|
|
1499
|
+
status: "pending" | "active" | "released" | "disputed" | "expired";
|
|
1500
|
+
}
|
|
1501
|
+
/** Principal guarantee for a new agent */
|
|
1502
|
+
interface Guarantee {
|
|
1503
|
+
guarantee_id: string;
|
|
1504
|
+
principal_did: string;
|
|
1505
|
+
agent_did: string;
|
|
1506
|
+
scope: string;
|
|
1507
|
+
max_liability?: number;
|
|
1508
|
+
valid_until: string;
|
|
1509
|
+
certificate: string;
|
|
1510
|
+
created_at: string;
|
|
1511
|
+
}
|
|
1512
|
+
declare class ReputationStore {
|
|
1513
|
+
private storage;
|
|
1514
|
+
private encryptionKey;
|
|
1515
|
+
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
1554
1516
|
/**
|
|
1555
|
-
*
|
|
1517
|
+
* Record an interaction outcome as a signed attestation.
|
|
1556
1518
|
*/
|
|
1557
|
-
|
|
1519
|
+
record(interactionId: string, counterpartyDid: string, outcome: InteractionOutcome, context: string, identity: StoredIdentity, identityEncryptionKey: Uint8Array, counterpartyAttestation?: string, sovereigntyTier?: SovereigntyTier): Promise<StoredAttestation>;
|
|
1558
1520
|
/**
|
|
1559
|
-
*
|
|
1560
|
-
*
|
|
1561
|
-
* variation selectors, and other invisible categories.
|
|
1521
|
+
* Query reputation data with filtering.
|
|
1522
|
+
* Returns aggregates only — not raw interaction data.
|
|
1562
1523
|
*/
|
|
1563
|
-
|
|
1524
|
+
query(options: {
|
|
1525
|
+
context?: string;
|
|
1526
|
+
time_range?: {
|
|
1527
|
+
start: string;
|
|
1528
|
+
end: string;
|
|
1529
|
+
};
|
|
1530
|
+
metrics?: string[];
|
|
1531
|
+
counterparty_did?: string;
|
|
1532
|
+
}): Promise<ReputationSummary>;
|
|
1564
1533
|
/**
|
|
1565
|
-
*
|
|
1566
|
-
* Returns a new string with all invisible chars removed.
|
|
1534
|
+
* Export attestations as a portable reputation bundle.
|
|
1567
1535
|
*/
|
|
1568
|
-
|
|
1536
|
+
exportBundle(identity: StoredIdentity, identityEncryptionKey: Uint8Array, context?: string): Promise<ReputationBundle>;
|
|
1569
1537
|
/**
|
|
1570
|
-
*
|
|
1571
|
-
*
|
|
1572
|
-
* ideographs, combining characters, emoji sequences). If the estimated token
|
|
1573
|
-
* cost per character is anomalously high, this may be a wallet-drain payload.
|
|
1538
|
+
* Import attestations from a reputation bundle.
|
|
1539
|
+
* Verifies signatures if requested (default: true).
|
|
1574
1540
|
*
|
|
1575
|
-
*
|
|
1576
|
-
* If the ratio of estimated tokens to char count exceeds 3x, flag it.
|
|
1577
|
-
*/
|
|
1578
|
-
private detectTokenBudgetAttack;
|
|
1579
|
-
/**
|
|
1580
|
-
* Detect encoded content (base64, hex, HTML entities, URL encoding),
|
|
1581
|
-
* decode it, and re-scan the decoded content through injection patterns.
|
|
1582
|
-
* If the decoded content contains injection patterns, flag as encoding_evasion.
|
|
1583
|
-
*/
|
|
1584
|
-
private detectEncodedPayloads;
|
|
1585
|
-
/**
|
|
1586
|
-
* Check if a string contains any injection patterns (role override or security bypass).
|
|
1587
|
-
*/
|
|
1588
|
-
private containsInjectionPatterns;
|
|
1589
|
-
/**
|
|
1590
|
-
* Safely decode a base64 string. Returns null if it's not valid base64
|
|
1591
|
-
* or doesn't decode to a meaningful string.
|
|
1592
|
-
*/
|
|
1593
|
-
private safeBase64Decode;
|
|
1594
|
-
/**
|
|
1595
|
-
* Safely decode a hex string. Returns null on failure.
|
|
1596
|
-
*/
|
|
1597
|
-
private safeHexDecode;
|
|
1598
|
-
/**
|
|
1599
|
-
* Decode HTML numeric entities (&#xHH; and &#DDD;) in a string.
|
|
1600
|
-
*/
|
|
1601
|
-
private decodeHtmlEntities;
|
|
1602
|
-
/**
|
|
1603
|
-
* Safely decode a URL-encoded string. Returns null on failure.
|
|
1604
|
-
*/
|
|
1605
|
-
private safeUrlDecode;
|
|
1606
|
-
/**
|
|
1607
|
-
* Heuristic: does this look like readable text (vs. binary garbage)?
|
|
1608
|
-
* Checks that most characters are printable ASCII or common Unicode.
|
|
1609
|
-
*/
|
|
1610
|
-
private looksLikeText;
|
|
1611
|
-
/**
|
|
1612
|
-
* Detect API keys and secrets in outbound content.
|
|
1613
|
-
*/
|
|
1614
|
-
private detectSecretPatterns;
|
|
1615
|
-
/**
|
|
1616
|
-
* Detect data exfiltration via markdown images with data-carrying query params.
|
|
1617
|
-
*/
|
|
1618
|
-
private detectOutboundExfiltration;
|
|
1619
|
-
/**
|
|
1620
|
-
* Detect internal filesystem path leaks in outbound content.
|
|
1621
|
-
*/
|
|
1622
|
-
private detectInternalPathLeaks;
|
|
1623
|
-
/**
|
|
1624
|
-
* Detect private IP addresses and localhost references in outbound content.
|
|
1625
|
-
*/
|
|
1626
|
-
private detectPrivateNetworkLeaks;
|
|
1627
|
-
/**
|
|
1628
|
-
* Detect role markers / prompt template artifacts in outbound content.
|
|
1629
|
-
* These should never appear in agent output — their presence indicates
|
|
1630
|
-
* injection artifact survival.
|
|
1631
|
-
*/
|
|
1632
|
-
private detectOutputRoleMarkers;
|
|
1633
|
-
/**
|
|
1634
|
-
* Detect base64 strings, base64url, and zero-width character evasion.
|
|
1541
|
+
* @param publicKeys - Map of DID → public key bytes for signature verification
|
|
1635
1542
|
*/
|
|
1636
|
-
|
|
1543
|
+
importBundle(bundle: ReputationBundle, verifySignatures: boolean, publicKeys: Map<string, Uint8Array>): Promise<{
|
|
1544
|
+
imported: number;
|
|
1545
|
+
invalid: number;
|
|
1546
|
+
contexts: string[];
|
|
1547
|
+
}>;
|
|
1637
1548
|
/**
|
|
1638
|
-
*
|
|
1549
|
+
* Create an escrow for trust bootstrapping.
|
|
1639
1550
|
*/
|
|
1640
|
-
|
|
1551
|
+
createEscrow(transactionTerms: string, counterpartyDid: string, timeoutSeconds: number, creatorDid: string, collateralAmount?: number): Promise<Escrow>;
|
|
1641
1552
|
/**
|
|
1642
|
-
*
|
|
1553
|
+
* Get an escrow by ID.
|
|
1643
1554
|
*/
|
|
1644
|
-
|
|
1555
|
+
getEscrow(escrowId: string): Promise<Escrow | null>;
|
|
1645
1556
|
/**
|
|
1646
|
-
*
|
|
1557
|
+
* Create a principal's guarantee for a new agent.
|
|
1647
1558
|
*/
|
|
1648
|
-
|
|
1559
|
+
createGuarantee(principalIdentity: StoredIdentity, agentDid: string, scope: string, durationSeconds: number, identityEncryptionKey: Uint8Array, maxLiability?: number): Promise<Guarantee>;
|
|
1649
1560
|
/**
|
|
1650
|
-
*
|
|
1561
|
+
* Load attestations for tier-weighted scoring.
|
|
1562
|
+
* Applies basic context/counterparty filtering, returns full StoredAttestations
|
|
1563
|
+
* so callers can access sovereignty_tier from attestation data.
|
|
1651
1564
|
*/
|
|
1652
|
-
|
|
1565
|
+
loadAllForTierScoring(options?: {
|
|
1566
|
+
context?: string;
|
|
1567
|
+
counterparty_did?: string;
|
|
1568
|
+
}): Promise<StoredAttestation[]>;
|
|
1569
|
+
private loadAll;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
/**
|
|
1573
|
+
* Sanctuary MCP Server — Federation Types
|
|
1574
|
+
*
|
|
1575
|
+
* MCP-to-MCP federation enables two Sanctuary instances to:
|
|
1576
|
+
* 1. Discover each other's capabilities (SIM exchange)
|
|
1577
|
+
* 2. Establish mutual trust (sovereignty handshake)
|
|
1578
|
+
* 3. Exchange signed reputation attestations
|
|
1579
|
+
* 4. Evaluate trust for cross-instance interactions
|
|
1580
|
+
*
|
|
1581
|
+
* Federation operates as a protocol layer atop the handshake:
|
|
1582
|
+
* Handshake establishes trust → Federation uses that trust for ongoing operations.
|
|
1583
|
+
*
|
|
1584
|
+
* Key constraint: Federation MUST respect each party's sovereignty.
|
|
1585
|
+
* Neither party can compel the other to share data they haven't disclosed
|
|
1586
|
+
* via their disclosure policy.
|
|
1587
|
+
*/
|
|
1588
|
+
|
|
1589
|
+
/** A known federation peer */
|
|
1590
|
+
interface FederationPeer {
|
|
1591
|
+
/** The peer's instance ID (from their SHR) */
|
|
1592
|
+
peer_id: string;
|
|
1593
|
+
/** The peer's DID (identity) */
|
|
1594
|
+
peer_did: string;
|
|
1595
|
+
/** When this peer was first seen */
|
|
1596
|
+
first_seen: string;
|
|
1597
|
+
/** When last handshake completed */
|
|
1598
|
+
last_handshake: string;
|
|
1599
|
+
/** Current trust tier from most recent handshake */
|
|
1600
|
+
trust_tier: SovereigntyTier;
|
|
1601
|
+
/** Handshake result (for tier resolution) */
|
|
1602
|
+
handshake_result: HandshakeResult;
|
|
1603
|
+
/** Peer's advertised capabilities (from their SIM) */
|
|
1604
|
+
capabilities: FederationCapabilities;
|
|
1605
|
+
/** Whether we have a valid (non-expired) handshake */
|
|
1606
|
+
active: boolean;
|
|
1607
|
+
}
|
|
1608
|
+
/** Capabilities a peer advertises for federation */
|
|
1609
|
+
interface FederationCapabilities {
|
|
1610
|
+
/** Whether the peer supports reputation exchange */
|
|
1611
|
+
reputation_exchange: boolean;
|
|
1612
|
+
/** Whether the peer supports mutual attestation */
|
|
1613
|
+
mutual_attestation: boolean;
|
|
1614
|
+
/** Whether the peer supports encrypted channels */
|
|
1615
|
+
encrypted_channel: boolean;
|
|
1616
|
+
/** Supported attestation formats */
|
|
1617
|
+
attestation_formats: string[];
|
|
1618
|
+
}
|
|
1619
|
+
/** Trust evaluation result for a federation peer */
|
|
1620
|
+
interface PeerTrustEvaluation {
|
|
1621
|
+
peer_id: string;
|
|
1622
|
+
/** Current sovereignty tier (from handshake) */
|
|
1623
|
+
sovereignty_tier: SovereigntyTier;
|
|
1624
|
+
/** Whether handshake is current (not expired) */
|
|
1625
|
+
handshake_current: boolean;
|
|
1626
|
+
/** Reputation summary (if available) */
|
|
1627
|
+
reputation_score?: number;
|
|
1628
|
+
/** How many mutual attestations we share */
|
|
1629
|
+
mutual_attestation_count: number;
|
|
1630
|
+
/** Overall trust assessment */
|
|
1631
|
+
trust_level: "high" | "medium" | "low" | "none";
|
|
1632
|
+
/** Reasoning for the assessment */
|
|
1633
|
+
factors: string[];
|
|
1634
|
+
/** Evaluated at */
|
|
1635
|
+
evaluated_at: string;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
/**
|
|
1639
|
+
* Sanctuary MCP Server — Federation Peer Registry
|
|
1640
|
+
*
|
|
1641
|
+
* Manages known federation peers. Peers are discovered through handshakes
|
|
1642
|
+
* and tracked for ongoing federation operations.
|
|
1643
|
+
*
|
|
1644
|
+
* The registry is the source of truth for:
|
|
1645
|
+
* - Who we've federated with
|
|
1646
|
+
* - Current trust status of each peer
|
|
1647
|
+
* - Peer capabilities (what operations they support)
|
|
1648
|
+
*
|
|
1649
|
+
* Security invariants:
|
|
1650
|
+
* - Peers are ONLY added through completed handshakes (not self-registration)
|
|
1651
|
+
* - Trust tiers degrade automatically when handshakes expire
|
|
1652
|
+
* - Peer data is stored encrypted under L1 sovereignty
|
|
1653
|
+
*/
|
|
1654
|
+
|
|
1655
|
+
declare class FederationRegistry {
|
|
1656
|
+
private peers;
|
|
1653
1657
|
/**
|
|
1654
|
-
*
|
|
1658
|
+
* Register or update a peer from a completed handshake.
|
|
1659
|
+
* This is the ONLY way peers enter the registry.
|
|
1655
1660
|
*/
|
|
1656
|
-
|
|
1661
|
+
registerFromHandshake(result: HandshakeResult, peerDid: string, capabilities?: Partial<FederationCapabilities>): FederationPeer;
|
|
1657
1662
|
/**
|
|
1658
|
-
*
|
|
1663
|
+
* Get a peer by instance ID.
|
|
1664
|
+
* Automatically updates active status based on handshake expiry.
|
|
1659
1665
|
*/
|
|
1660
|
-
|
|
1666
|
+
getPeer(peerId: string): FederationPeer | null;
|
|
1661
1667
|
/**
|
|
1662
|
-
*
|
|
1668
|
+
* List all known peers, optionally filtered by status.
|
|
1663
1669
|
*/
|
|
1664
|
-
|
|
1670
|
+
listPeers(filter?: {
|
|
1671
|
+
active_only?: boolean;
|
|
1672
|
+
}): FederationPeer[];
|
|
1665
1673
|
/**
|
|
1666
|
-
*
|
|
1667
|
-
* Latin equivalents. NFKC normalization handles fullwidth and compatibility
|
|
1668
|
-
* forms, but does NOT map Cyrillic/Greek/Armenian/Georgian lookalikes to
|
|
1669
|
-
* Latin (they're distinct codepoints by design).
|
|
1674
|
+
* Evaluate trust for a federation peer.
|
|
1670
1675
|
*
|
|
1671
|
-
*
|
|
1672
|
-
*
|
|
1676
|
+
* Trust assessment considers:
|
|
1677
|
+
* - Handshake status (current vs expired)
|
|
1678
|
+
* - Sovereignty tier (verified-sovereign vs degraded vs unverified)
|
|
1679
|
+
* - Reputation data (if available)
|
|
1680
|
+
* - Mutual attestation history
|
|
1673
1681
|
*/
|
|
1674
|
-
|
|
1682
|
+
evaluateTrust(peerId: string, mutualAttestationCount?: number, reputationScore?: number): PeerTrustEvaluation;
|
|
1675
1683
|
/**
|
|
1676
|
-
*
|
|
1677
|
-
* More high-severity signals = higher confidence.
|
|
1684
|
+
* Remove a peer from the registry.
|
|
1678
1685
|
*/
|
|
1679
|
-
|
|
1686
|
+
removePeer(peerId: string): boolean;
|
|
1687
|
+
/**
|
|
1688
|
+
* Get the handshake results map (for tier resolution integration).
|
|
1689
|
+
*/
|
|
1690
|
+
getHandshakeResults(): Map<string, HandshakeResult>;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
/**
|
|
1694
|
+
* Sanctuary MCP Server — L2 Operational Isolation: Context Gating
|
|
1695
|
+
*
|
|
1696
|
+
* Context gating controls what information leaves the sovereignty boundary
|
|
1697
|
+
* when an agent makes outbound calls — especially inference calls to remote
|
|
1698
|
+
* LLM providers. This is the "minimum-necessary context" enforcement layer.
|
|
1699
|
+
*
|
|
1700
|
+
* The problem: When an agent sends a request to a remote LLM provider (Claude,
|
|
1701
|
+
* GPT, etc.), most harnesses send the agent's full context — conversation
|
|
1702
|
+
* history, memory, tool results, preferences, internal reasoning. The agent
|
|
1703
|
+
* has no control over what the provider sees.
|
|
1704
|
+
*
|
|
1705
|
+
* Context gating lets the agent define:
|
|
1706
|
+
* - Provider categories (inference, tool-api, logging, analytics, etc.)
|
|
1707
|
+
* - What fields/categories of context may flow to each provider type
|
|
1708
|
+
* - What must always be redacted (secrets, internal reasoning, PII, etc.)
|
|
1709
|
+
* - What requires transformation (hashing, summarizing, anonymizing)
|
|
1710
|
+
*
|
|
1711
|
+
* This sits in L2 (Operational Isolation) because it controls information
|
|
1712
|
+
* flow at the execution boundary. L3 (Selective Disclosure) handles agent-
|
|
1713
|
+
* to-agent trust negotiation with cryptographic proofs; context gating
|
|
1714
|
+
* handles agent-to-infrastructure information flow.
|
|
1715
|
+
*
|
|
1716
|
+
* Security invariants:
|
|
1717
|
+
* - Redact rules take absolute priority (like withhold in L3)
|
|
1718
|
+
* - Policies are stored encrypted under L1 sovereignty
|
|
1719
|
+
* - Every filter operation is audit-logged with a content hash
|
|
1720
|
+
* (what was sent, what was redacted — without storing the content itself)
|
|
1721
|
+
* - Default policy: redact everything not explicitly allowed
|
|
1722
|
+
*/
|
|
1723
|
+
|
|
1724
|
+
/** Provider categories that context may flow to */
|
|
1725
|
+
type ProviderCategory = "inference" | "tool-api" | "logging" | "analytics" | "peer-agent" | "custom";
|
|
1726
|
+
/** Actions that can be taken on a context field */
|
|
1727
|
+
type ContextAction = "allow" | "redact" | "hash" | "summarize" | "deny";
|
|
1728
|
+
/** A rule within a context-gating policy */
|
|
1729
|
+
interface ContextGateRule {
|
|
1730
|
+
/** Provider category this rule applies to */
|
|
1731
|
+
provider: ProviderCategory | "*";
|
|
1732
|
+
/** Fields/patterns that may pass through */
|
|
1733
|
+
allow: string[];
|
|
1734
|
+
/** Fields/patterns that must be redacted (highest priority) */
|
|
1735
|
+
redact: string[];
|
|
1736
|
+
/** Fields/patterns that should be hashed */
|
|
1737
|
+
hash: string[];
|
|
1738
|
+
/** Fields/patterns that should be summarized (advisory) */
|
|
1739
|
+
summarize: string[];
|
|
1740
|
+
}
|
|
1741
|
+
/** A complete context-gating policy */
|
|
1742
|
+
interface ContextGatePolicy {
|
|
1743
|
+
policy_id: string;
|
|
1744
|
+
policy_name: string;
|
|
1745
|
+
rules: ContextGateRule[];
|
|
1746
|
+
/** Default action when no rule matches a field */
|
|
1747
|
+
default_action: "redact" | "deny";
|
|
1748
|
+
/** Identity this policy is bound to (optional) */
|
|
1749
|
+
identity_id?: string;
|
|
1750
|
+
created_at: string;
|
|
1751
|
+
updated_at: string;
|
|
1752
|
+
}
|
|
1753
|
+
/** Result of filtering a single field */
|
|
1754
|
+
interface FieldFilterResult {
|
|
1755
|
+
field: string;
|
|
1756
|
+
action: ContextAction;
|
|
1757
|
+
reason: string;
|
|
1758
|
+
/** If action is "hash", contains the hash */
|
|
1759
|
+
hash_value?: string;
|
|
1760
|
+
}
|
|
1761
|
+
/** Result of a full context filter operation */
|
|
1762
|
+
interface ContextFilterResult {
|
|
1763
|
+
policy_id: string;
|
|
1764
|
+
provider: ProviderCategory | string;
|
|
1765
|
+
fields_allowed: number;
|
|
1766
|
+
fields_redacted: number;
|
|
1767
|
+
fields_hashed: number;
|
|
1768
|
+
fields_summarized: number;
|
|
1769
|
+
fields_denied: number;
|
|
1770
|
+
decisions: FieldFilterResult[];
|
|
1771
|
+
/** SHA-256 hash of the original context (for audit trail) */
|
|
1772
|
+
original_context_hash: string;
|
|
1773
|
+
/** SHA-256 hash of the filtered output (for audit trail) */
|
|
1774
|
+
filtered_context_hash: string;
|
|
1775
|
+
filtered_at: string;
|
|
1776
|
+
}
|
|
1777
|
+
/**
|
|
1778
|
+
* Evaluate a context field against a policy for a given provider.
|
|
1779
|
+
*
|
|
1780
|
+
* Priority order (same as L3 disclosure):
|
|
1781
|
+
* 1. Redact (blocks — highest priority)
|
|
1782
|
+
* 2. Deny (blocks entire request)
|
|
1783
|
+
* 3. Hash (transforms)
|
|
1784
|
+
* 4. Summarize (advisory transform)
|
|
1785
|
+
* 5. Allow (passes through)
|
|
1786
|
+
* 6. Default action
|
|
1787
|
+
*/
|
|
1788
|
+
declare function evaluateField(policy: ContextGatePolicy, provider: ProviderCategory | string, field: string): FieldFilterResult;
|
|
1789
|
+
/**
|
|
1790
|
+
* Filter a full context object against a policy for a given provider.
|
|
1791
|
+
* Returns per-field decisions and content hashes for the audit trail.
|
|
1792
|
+
*/
|
|
1793
|
+
declare function filterContext(policy: ContextGatePolicy, provider: ProviderCategory | string, context: Record<string, unknown>): ContextFilterResult;
|
|
1794
|
+
/**
|
|
1795
|
+
* Context gate policy store — encrypted under L1 sovereignty.
|
|
1796
|
+
*/
|
|
1797
|
+
declare class ContextGatePolicyStore {
|
|
1798
|
+
private storage;
|
|
1799
|
+
private encryptionKey;
|
|
1800
|
+
private policies;
|
|
1801
|
+
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
1680
1802
|
/**
|
|
1681
|
-
*
|
|
1803
|
+
* Create and store a new context-gating policy.
|
|
1682
1804
|
*/
|
|
1683
|
-
|
|
1805
|
+
create(policyName: string, rules: ContextGateRule[], defaultAction: "redact" | "deny", identityId?: string): Promise<ContextGatePolicy>;
|
|
1684
1806
|
/**
|
|
1685
|
-
* Get
|
|
1807
|
+
* Get a policy by ID.
|
|
1686
1808
|
*/
|
|
1687
|
-
|
|
1688
|
-
total_scans: number;
|
|
1689
|
-
total_flags: number;
|
|
1690
|
-
total_blocks: number;
|
|
1691
|
-
signals_by_type: Record<string, number>;
|
|
1692
|
-
};
|
|
1809
|
+
get(policyId: string): Promise<ContextGatePolicy | null>;
|
|
1693
1810
|
/**
|
|
1694
|
-
*
|
|
1811
|
+
* List all context-gating policies.
|
|
1695
1812
|
*/
|
|
1696
|
-
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
/**
|
|
1700
|
-
* Sanctuary MCP Server — Principal Policy Types
|
|
1701
|
-
*
|
|
1702
|
-
* Type definitions for the Principal Policy system.
|
|
1703
|
-
* The Principal Policy is the human-controlled, agent-immutable
|
|
1704
|
-
* configuration that gates operations through approval tiers.
|
|
1705
|
-
*/
|
|
1706
|
-
/** Tier 2 anomaly action: what to do when an anomaly is detected */
|
|
1707
|
-
type AnomalyAction = "approve" | "log" | "allow";
|
|
1708
|
-
/** Tier 2 anomaly detection configuration */
|
|
1709
|
-
interface Tier2Config {
|
|
1710
|
-
/** Action when agent accesses a namespace it hasn't used before */
|
|
1711
|
-
new_namespace_access: AnomalyAction;
|
|
1712
|
-
/** Action when agent interacts with an unknown counterparty DID */
|
|
1713
|
-
new_counterparty: AnomalyAction;
|
|
1714
|
-
/** Tool call frequency multiplier that triggers anomaly */
|
|
1715
|
-
frequency_spike_multiplier: number;
|
|
1716
|
-
/** Maximum signing operations per minute before triggering */
|
|
1717
|
-
max_signs_per_minute: number;
|
|
1718
|
-
/** Reading more than N keys in a namespace within 60 seconds */
|
|
1719
|
-
bulk_read_threshold: number;
|
|
1720
|
-
/** Policy for first session when no baseline exists */
|
|
1721
|
-
first_session_policy: AnomalyAction;
|
|
1722
|
-
}
|
|
1723
|
-
/** Approval channel configuration */
|
|
1724
|
-
interface ApprovalChannelConfig {
|
|
1725
|
-
type: "stderr" | "webhook" | "callback";
|
|
1726
|
-
timeout_seconds: number;
|
|
1813
|
+
list(): Promise<ContextGatePolicy[]>;
|
|
1727
1814
|
/**
|
|
1728
|
-
*
|
|
1729
|
-
* Timeout on any approval channel ALWAYS results in denial.
|
|
1730
|
-
* This field is retained for backward compatibility with existing
|
|
1731
|
-
* policy files but is ignored — timeout always denies.
|
|
1815
|
+
* Load all persisted policies into memory.
|
|
1732
1816
|
*/
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
webhook_secret?: string;
|
|
1736
|
-
}
|
|
1737
|
-
/** Complete Principal Policy */
|
|
1738
|
-
interface PrincipalPolicy {
|
|
1739
|
-
version: number;
|
|
1740
|
-
/** Operations that always require human approval */
|
|
1741
|
-
tier1_always_approve: string[];
|
|
1742
|
-
/** Behavioral anomaly detection configuration */
|
|
1743
|
-
tier2_anomaly: Tier2Config;
|
|
1744
|
-
/** Operations that never require approval (audit only) */
|
|
1745
|
-
tier3_always_allow: string[];
|
|
1746
|
-
/** How approval requests reach the human */
|
|
1747
|
-
approval_channel: ApprovalChannelConfig;
|
|
1748
|
-
}
|
|
1749
|
-
/** Approval request sent to the human */
|
|
1750
|
-
interface ApprovalRequest {
|
|
1751
|
-
operation: string;
|
|
1752
|
-
tier: 1 | 2;
|
|
1753
|
-
reason: string;
|
|
1754
|
-
context: Record<string, unknown>;
|
|
1755
|
-
timestamp: string;
|
|
1756
|
-
}
|
|
1757
|
-
/** Approval response from the human */
|
|
1758
|
-
interface ApprovalResponse {
|
|
1759
|
-
decision: "approve" | "deny";
|
|
1760
|
-
decided_at: string;
|
|
1761
|
-
decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
|
|
1762
|
-
}
|
|
1763
|
-
/** Result of the approval gate evaluation */
|
|
1764
|
-
interface GateResult {
|
|
1765
|
-
allowed: boolean;
|
|
1766
|
-
tier: 1 | 2 | 3;
|
|
1767
|
-
reason: string;
|
|
1768
|
-
approval_required: boolean;
|
|
1769
|
-
approval_response?: ApprovalResponse;
|
|
1770
|
-
}
|
|
1771
|
-
/** Behavioral baseline for anomaly detection */
|
|
1772
|
-
interface SessionProfile {
|
|
1773
|
-
/** Namespaces accessed (read or write) */
|
|
1774
|
-
known_namespaces: string[];
|
|
1775
|
-
/** Counterparty DIDs seen in reputation operations */
|
|
1776
|
-
known_counterparties: string[];
|
|
1777
|
-
/** Tool call counts per tool name (lifetime in session) */
|
|
1778
|
-
tool_call_counts: Record<string, number>;
|
|
1779
|
-
/** Whether this is the first session (no prior baseline) */
|
|
1780
|
-
is_first_session: boolean;
|
|
1781
|
-
/** Session start time */
|
|
1782
|
-
started_at: string;
|
|
1783
|
-
/** When the baseline was last saved */
|
|
1784
|
-
saved_at?: string;
|
|
1817
|
+
private loadAll;
|
|
1818
|
+
private persist;
|
|
1785
1819
|
}
|
|
1786
1820
|
|
|
1787
1821
|
/**
|
|
1788
|
-
* Sanctuary MCP Server —
|
|
1822
|
+
* Sanctuary MCP Server — L2 Context Gating: Starter Policy Templates
|
|
1789
1823
|
*
|
|
1790
|
-
*
|
|
1791
|
-
*
|
|
1792
|
-
* ensuring the agent cannot intercept or forge approval responses.
|
|
1824
|
+
* Pre-built policies for common use cases. These are starting points —
|
|
1825
|
+
* users should customize them for their specific context structure.
|
|
1793
1826
|
*
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
1796
|
-
*
|
|
1827
|
+
* Templates:
|
|
1828
|
+
*
|
|
1829
|
+
* inference-minimal
|
|
1830
|
+
* Only the current task and query reach the LLM. Everything else
|
|
1831
|
+
* is redacted. Secrets, PII, memory, reasoning, and history are
|
|
1832
|
+
* all blocked. IDs are hashed. Maximum privacy, minimum context.
|
|
1833
|
+
*
|
|
1834
|
+
* inference-standard
|
|
1835
|
+
* Task, query, and tool results pass through. Conversation history
|
|
1836
|
+
* is flagged for summarization (compress before sending). Secrets,
|
|
1837
|
+
* PII, and internal reasoning are redacted. IDs are hashed.
|
|
1838
|
+
* Balanced: the LLM has enough context to be useful without seeing
|
|
1839
|
+
* everything the agent knows.
|
|
1840
|
+
*
|
|
1841
|
+
* logging-strict
|
|
1842
|
+
* Redacts everything for logging/analytics providers. Only
|
|
1843
|
+
* operation names and timestamps pass through. Use this for
|
|
1844
|
+
* telemetry services where you want usage metrics without
|
|
1845
|
+
* content exposure.
|
|
1846
|
+
*
|
|
1847
|
+
* tool-api-scoped
|
|
1848
|
+
* Allows tool-specific parameters and the current task, redacts
|
|
1849
|
+
* memory, history, secrets, and PII. Hashes IDs. For outbound
|
|
1850
|
+
* calls to external APIs (search, database, etc.) where you need
|
|
1851
|
+
* to send query parameters but not your agent's full state.
|
|
1797
1852
|
*/
|
|
1798
1853
|
|
|
1799
|
-
/**
|
|
1800
|
-
interface
|
|
1801
|
-
|
|
1854
|
+
/** A template definition ready to be applied via the policy store */
|
|
1855
|
+
interface ContextGateTemplate {
|
|
1856
|
+
/** Machine-readable template ID */
|
|
1857
|
+
id: string;
|
|
1858
|
+
/** Human-readable name */
|
|
1859
|
+
name: string;
|
|
1860
|
+
/** One-line description */
|
|
1861
|
+
description: string;
|
|
1862
|
+
/** When to use this template */
|
|
1863
|
+
use_when: string;
|
|
1864
|
+
/** The rules that make up this template */
|
|
1865
|
+
rules: ContextGateRule[];
|
|
1866
|
+
/** Default action for unmatched fields */
|
|
1867
|
+
default_action: "redact" | "deny";
|
|
1802
1868
|
}
|
|
1869
|
+
/** All available templates, keyed by ID */
|
|
1870
|
+
declare const TEMPLATES: Record<string, ContextGateTemplate>;
|
|
1871
|
+
/** List all available template IDs */
|
|
1872
|
+
declare function listTemplateIds(): string[];
|
|
1873
|
+
/** Get a template by ID (returns undefined if not found) */
|
|
1874
|
+
declare function getTemplate(id: string): ContextGateTemplate | undefined;
|
|
1875
|
+
|
|
1803
1876
|
/**
|
|
1804
|
-
*
|
|
1877
|
+
* Sanctuary MCP Server — L2 Context Gating: Policy Recommendation Engine
|
|
1805
1878
|
*
|
|
1806
|
-
*
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1879
|
+
* Analyzes a sample context object and recommends a context-gating policy
|
|
1880
|
+
* based on field name heuristics. The agent (or human) can then review,
|
|
1881
|
+
* adjust, and apply the recommendation.
|
|
1809
1882
|
*
|
|
1810
|
-
*
|
|
1811
|
-
*
|
|
1812
|
-
* the prompt is displayed so the human sees what is happening, and the
|
|
1813
|
-
* operation is denied immediately.
|
|
1883
|
+
* This is deliberately conservative: when in doubt, it recommends redact.
|
|
1884
|
+
* A false redaction is a usability issue; a false allow is a privacy leak.
|
|
1814
1885
|
*
|
|
1815
|
-
*
|
|
1816
|
-
* -
|
|
1817
|
-
* -
|
|
1818
|
-
* -
|
|
1819
|
-
* -
|
|
1886
|
+
* Classification heuristics:
|
|
1887
|
+
* - Known secret patterns → redact (highest confidence)
|
|
1888
|
+
* - Known PII patterns → redact (high confidence)
|
|
1889
|
+
* - Known internal state patterns → redact (high confidence)
|
|
1890
|
+
* - Known ID patterns → hash (medium confidence)
|
|
1891
|
+
* - Known history patterns → summarize (medium confidence)
|
|
1892
|
+
* - Known task/query patterns → allow (medium confidence)
|
|
1893
|
+
* - Everything else → redact (conservative default)
|
|
1894
|
+
*
|
|
1895
|
+
* WARNING: Fields like 'tool_results' and 'tool_output' are classified as
|
|
1896
|
+
* "allow" (medium confidence) but may contain sensitive data from external
|
|
1897
|
+
* API responses, including auth tokens, user data, or PII. Always review
|
|
1898
|
+
* recommendations before applying — the heuristic classifies by field NAME,
|
|
1899
|
+
* not field CONTENT.
|
|
1820
1900
|
*/
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1901
|
+
/** Classification result for a single field */
|
|
1902
|
+
interface FieldClassification {
|
|
1903
|
+
field: string;
|
|
1904
|
+
recommended_action: "allow" | "redact" | "hash" | "summarize";
|
|
1905
|
+
reason: string;
|
|
1906
|
+
confidence: "high" | "medium" | "low";
|
|
1907
|
+
/** Pattern that matched, if any */
|
|
1908
|
+
matched_pattern: string | null;
|
|
1909
|
+
}
|
|
1910
|
+
/** Full recommendation result */
|
|
1911
|
+
interface PolicyRecommendation {
|
|
1912
|
+
provider: string;
|
|
1913
|
+
classifications: FieldClassification[];
|
|
1914
|
+
recommended_rules: {
|
|
1915
|
+
allow: string[];
|
|
1916
|
+
redact: string[];
|
|
1917
|
+
hash: string[];
|
|
1918
|
+
summarize: string[];
|
|
1919
|
+
};
|
|
1920
|
+
default_action: "redact";
|
|
1921
|
+
summary: {
|
|
1922
|
+
total_fields: number;
|
|
1923
|
+
allow: number;
|
|
1924
|
+
redact: number;
|
|
1925
|
+
hash: number;
|
|
1926
|
+
summarize: number;
|
|
1927
|
+
};
|
|
1928
|
+
warnings: string[];
|
|
1825
1929
|
}
|
|
1826
1930
|
/**
|
|
1827
|
-
*
|
|
1931
|
+
* Classify a single field name and return a recommendation.
|
|
1932
|
+
*/
|
|
1933
|
+
declare function classifyField(fieldName: string): FieldClassification;
|
|
1934
|
+
/**
|
|
1935
|
+
* Analyze a full context object and recommend a policy.
|
|
1936
|
+
*/
|
|
1937
|
+
declare function recommendPolicy(context: Record<string, unknown>, provider?: string): PolicyRecommendation;
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* Sanctuary MCP Server — L2 Model Provenance
|
|
1941
|
+
*
|
|
1942
|
+
* Declares and attests to the model(s) powering this agent.
|
|
1943
|
+
*
|
|
1944
|
+
* Vitalik Buterin's "Secure LLM" post (April 2026) identified a critical gap:
|
|
1945
|
+
* open-weights-but-not-open-source models can have trained-in backdoors. Model
|
|
1946
|
+
* provenance declaration lets agents and their operators verify the integrity
|
|
1947
|
+
* of the inference backbone.
|
|
1948
|
+
*
|
|
1949
|
+
* Tracks: model name, version, weights hash, license, open-source status,
|
|
1950
|
+
* training data hash (if available). Included in SHR L2 section.
|
|
1951
|
+
*
|
|
1952
|
+
* This sits in L2 (Operational Isolation) because it's part of the runtime
|
|
1953
|
+
* attestation surface — the agent declares what model(s) it's actually running.
|
|
1828
1954
|
*/
|
|
1829
|
-
declare class CallbackApprovalChannel implements ApprovalChannel {
|
|
1830
|
-
private callback;
|
|
1831
|
-
constructor(callback: (request: ApprovalRequest) => Promise<ApprovalResponse>);
|
|
1832
|
-
requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
|
|
1833
|
-
}
|
|
1834
1955
|
/**
|
|
1835
|
-
*
|
|
1956
|
+
* Metadata about a single model powering this agent.
|
|
1836
1957
|
*/
|
|
1837
|
-
|
|
1838
|
-
|
|
1958
|
+
interface ModelProvenance {
|
|
1959
|
+
/** Machine-readable model ID (e.g., "qwen3.5-35b", "claude-opus-4", "llama-3.3-70b-instruct") */
|
|
1960
|
+
model_id: string;
|
|
1961
|
+
/** Human-readable model name (e.g., "Qwen 3.5", "Claude Opus 4", "Llama 3.3 70B Instruct") */
|
|
1962
|
+
model_name: string;
|
|
1963
|
+
/** Semantic version (e.g., "3.5", "4.0", "3.3") */
|
|
1964
|
+
model_version: string;
|
|
1965
|
+
/** Provider/vendor (e.g., "Alibaba Cloud", "Anthropic", "Meta", "local") */
|
|
1966
|
+
provider: string;
|
|
1967
|
+
/** SHA-256 of model weights file, if available and verifiable */
|
|
1968
|
+
weights_hash?: string;
|
|
1969
|
+
/** SHA-256 of training data manifest or metadata, if available */
|
|
1970
|
+
training_data_hash?: string;
|
|
1971
|
+
/** License identifier (e.g., "Apache-2.0", "CC-BY-4.0", "proprietary", "unknown") */
|
|
1972
|
+
license: string;
|
|
1973
|
+
/** True if model weights are publicly available (even if training is proprietary) */
|
|
1974
|
+
open_weights: boolean;
|
|
1975
|
+
/** True if full training code, data, and methodology are publicly available */
|
|
1976
|
+
open_source: boolean;
|
|
1977
|
+
/** True if inference runs on the local agent's hardware (not delegated to cloud API) */
|
|
1978
|
+
local_inference: boolean;
|
|
1979
|
+
/** ISO 8601 timestamp when this provenance was declared */
|
|
1980
|
+
declared_at: string;
|
|
1839
1981
|
}
|
|
1840
|
-
|
|
1841
1982
|
/**
|
|
1842
|
-
*
|
|
1843
|
-
*
|
|
1844
|
-
* Tracks the agent's behavioral profile during a session and persists
|
|
1845
|
-
* it for cross-session anomaly detection. The baseline defines "normal"
|
|
1846
|
-
* so that deviations can trigger Tier 2 approval.
|
|
1847
|
-
*
|
|
1848
|
-
* Security invariants:
|
|
1849
|
-
* - Baseline is stored encrypted under L1 sovereignty
|
|
1850
|
-
* - Baseline changes are audit-logged
|
|
1851
|
-
* - Baseline is integrity-verified via L1 Merkle tree
|
|
1852
|
-
* - No MCP tool can directly modify the baseline
|
|
1983
|
+
* In-memory and persistent store for model provenance declarations.
|
|
1984
|
+
* Declarations are encrypted under L1 sovereignty.
|
|
1853
1985
|
*/
|
|
1854
|
-
|
|
1855
|
-
declare class BaselineTracker {
|
|
1856
|
-
private storage;
|
|
1857
|
-
private encryptionKey;
|
|
1858
|
-
private profile;
|
|
1859
|
-
/** Sliding window: timestamps of tool calls per tool name (last 60s) */
|
|
1860
|
-
private callWindows;
|
|
1861
|
-
/** Sliding window: read counts per namespace (last 60s) */
|
|
1862
|
-
private readWindows;
|
|
1863
|
-
/** Sliding window: sign call timestamps (last 60s) */
|
|
1864
|
-
private signWindow;
|
|
1865
|
-
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
1866
|
-
/**
|
|
1867
|
-
* Load the previous session's baseline from storage.
|
|
1868
|
-
* If none exists, this is a first session.
|
|
1869
|
-
*/
|
|
1870
|
-
load(): Promise<void>;
|
|
1871
|
-
/**
|
|
1872
|
-
* Save the current baseline to storage (encrypted).
|
|
1873
|
-
* Called at session end or periodically.
|
|
1874
|
-
*/
|
|
1875
|
-
save(): Promise<void>;
|
|
1876
|
-
/**
|
|
1877
|
-
* Record a tool call for baseline tracking.
|
|
1878
|
-
* Returns anomaly information if applicable.
|
|
1879
|
-
*/
|
|
1880
|
-
recordToolCall(toolName: string): void;
|
|
1881
|
-
/**
|
|
1882
|
-
* Record a namespace access.
|
|
1883
|
-
* @returns true if this is a new namespace (not in baseline)
|
|
1884
|
-
*/
|
|
1885
|
-
recordNamespaceAccess(namespace: string): boolean;
|
|
1986
|
+
interface ModelProvenanceStore {
|
|
1886
1987
|
/**
|
|
1887
|
-
*
|
|
1888
|
-
* @returns the number of reads in the current 60-second window
|
|
1988
|
+
* Declare a model's provenance and add it to the store.
|
|
1889
1989
|
*/
|
|
1890
|
-
|
|
1990
|
+
declare(provenance: ModelProvenance): void;
|
|
1891
1991
|
/**
|
|
1892
|
-
*
|
|
1893
|
-
* @returns true if this is a new counterparty (not in baseline)
|
|
1992
|
+
* Retrieve a model's provenance by ID.
|
|
1894
1993
|
*/
|
|
1895
|
-
|
|
1994
|
+
get(model_id: string): ModelProvenance | undefined;
|
|
1896
1995
|
/**
|
|
1897
|
-
*
|
|
1898
|
-
* @returns the number of signs in the current 60-second window
|
|
1996
|
+
* List all declared models.
|
|
1899
1997
|
*/
|
|
1900
|
-
|
|
1998
|
+
list(): ModelProvenance[];
|
|
1901
1999
|
/**
|
|
1902
|
-
* Get the
|
|
2000
|
+
* Get the primary/main model (the one the agent uses by default for inference).
|
|
1903
2001
|
*/
|
|
1904
|
-
|
|
2002
|
+
primary(): ModelProvenance | undefined;
|
|
1905
2003
|
/**
|
|
1906
|
-
*
|
|
2004
|
+
* Set which model is the primary.
|
|
1907
2005
|
*/
|
|
1908
|
-
|
|
1909
|
-
/** Whether this is the first session */
|
|
1910
|
-
get isFirstSession(): boolean;
|
|
1911
|
-
/** Get a read-only view of the current profile */
|
|
1912
|
-
getProfile(): SessionProfile;
|
|
2006
|
+
setPrimary(model_id: string): void;
|
|
1913
2007
|
}
|
|
1914
|
-
|
|
1915
2008
|
/**
|
|
1916
|
-
*
|
|
1917
|
-
*
|
|
1918
|
-
* The three-tier approval gate sits between the MCP router and tool handlers.
|
|
1919
|
-
* Every tool call passes through the gate before execution.
|
|
1920
|
-
*
|
|
1921
|
-
* Evaluation order:
|
|
1922
|
-
* 1. Tier 1: Is this operation in the always-approve list? → Request approval.
|
|
1923
|
-
* 2. Tier 2: Does this call represent a behavioral anomaly? → Request approval.
|
|
1924
|
-
* 3. Tier 3 / default: Allow with audit logging.
|
|
1925
|
-
*
|
|
1926
|
-
* Security invariants:
|
|
1927
|
-
* - The gate cannot be bypassed — it wraps every tool handler.
|
|
1928
|
-
* - Denial responses do not reveal policy details to the agent.
|
|
1929
|
-
* - All gate decisions (approve, deny, allow) are audit-logged.
|
|
2009
|
+
* In-memory implementation of ModelProvenanceStore.
|
|
2010
|
+
* Suitable for most use cases. For encrypted persistence, integrate with L1 state store.
|
|
1930
2011
|
*/
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
private auditLog;
|
|
1945
|
-
private injectionDetector;
|
|
1946
|
-
private onInjectionAlert?;
|
|
1947
|
-
private proxyTierResolver?;
|
|
1948
|
-
constructor(policy: PrincipalPolicy, baseline: BaselineTracker, channel: ApprovalChannel, auditLog: AuditLog, injectionDetector?: InjectionDetector, onInjectionAlert?: InjectionAlertCallback);
|
|
1949
|
-
/**
|
|
1950
|
-
* Set the proxy tier resolver. Called after the proxy router is initialized.
|
|
1951
|
-
*/
|
|
1952
|
-
setProxyTierResolver(resolver: ProxyTierResolver): void;
|
|
2012
|
+
declare class InMemoryModelProvenanceStore implements ModelProvenanceStore {
|
|
2013
|
+
private models;
|
|
2014
|
+
private primaryModelId;
|
|
2015
|
+
declare(provenance: ModelProvenance): void;
|
|
2016
|
+
get(model_id: string): ModelProvenance | undefined;
|
|
2017
|
+
list(): ModelProvenance[];
|
|
2018
|
+
primary(): ModelProvenance | undefined;
|
|
2019
|
+
setPrimary(model_id: string): void;
|
|
2020
|
+
}
|
|
2021
|
+
/**
|
|
2022
|
+
* Common model provenance presets for quick initialization.
|
|
2023
|
+
*/
|
|
2024
|
+
declare const MODEL_PRESETS: {
|
|
1953
2025
|
/**
|
|
1954
|
-
*
|
|
1955
|
-
*
|
|
1956
|
-
* @param toolName - Full MCP tool name (e.g., "sanctuary/state_export")
|
|
1957
|
-
* @param args - Tool call arguments (for context extraction)
|
|
1958
|
-
* @returns GateResult indicating whether the call is allowed
|
|
2026
|
+
* Claude Opus 4 via Anthropic API (cloud inference, closed weights/source)
|
|
1959
2027
|
*/
|
|
1960
|
-
|
|
2028
|
+
claudeOpus4: () => ModelProvenance;
|
|
1961
2029
|
/**
|
|
1962
|
-
*
|
|
2030
|
+
* Qwen 3.5 via local inference (open weights, proprietary training)
|
|
1963
2031
|
*/
|
|
1964
|
-
|
|
2032
|
+
qwen35Local: () => ModelProvenance;
|
|
1965
2033
|
/**
|
|
1966
|
-
*
|
|
2034
|
+
* Llama 3.3 70B via local inference (open weights and code)
|
|
1967
2035
|
*/
|
|
1968
|
-
|
|
2036
|
+
llama33Local: () => ModelProvenance;
|
|
1969
2037
|
/**
|
|
1970
|
-
*
|
|
1971
|
-
* Strips potentially large values to keep the prompt readable.
|
|
2038
|
+
* Mistral 7B (open weights, open code, local inference)
|
|
1972
2039
|
*/
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
getBaseline(): BaselineTracker;
|
|
1976
|
-
/** Get the injection detector for stats/configuration access */
|
|
1977
|
-
getInjectionDetector(): InjectionDetector;
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
|
-
/**
|
|
1981
|
-
* Sanctuary MCP Server — Tool Router
|
|
1982
|
-
*
|
|
1983
|
-
* Routes sanctuary/* tool calls to their layer-specific handlers.
|
|
1984
|
-
* Every tool call passes through schema validation and the ApprovalGate
|
|
1985
|
-
* (if configured) before execution. Neither can be bypassed.
|
|
1986
|
-
*
|
|
1987
|
-
* This module is the abstraction boundary for MCP SDK version migration —
|
|
1988
|
-
* if the SDK API changes, only this module needs updating.
|
|
1989
|
-
*/
|
|
1990
|
-
|
|
1991
|
-
/** Tool handler function signature */
|
|
1992
|
-
type ToolHandler = (args: Record<string, unknown>) => Promise<{
|
|
1993
|
-
content: Array<{
|
|
1994
|
-
type: "text";
|
|
1995
|
-
text: string;
|
|
1996
|
-
}>;
|
|
1997
|
-
}>;
|
|
1998
|
-
/** Tool definition for registration */
|
|
1999
|
-
interface ToolDefinition {
|
|
2000
|
-
name: string;
|
|
2001
|
-
description: string;
|
|
2002
|
-
inputSchema: Record<string, unknown>;
|
|
2003
|
-
handler: ToolHandler;
|
|
2004
|
-
}
|
|
2040
|
+
mistral7bLocal: () => ModelProvenance;
|
|
2041
|
+
};
|
|
2005
2042
|
|
|
2006
2043
|
/**
|
|
2007
2044
|
* Sanctuary MCP Server — L2 Context Gating: Automatic Enforcer
|
|
@@ -2028,7 +2065,7 @@ interface EnforcerConfig {
|
|
|
2028
2065
|
enabled: boolean;
|
|
2029
2066
|
/** Policy ID to use when no specific one is set */
|
|
2030
2067
|
default_policy_id?: string;
|
|
2031
|
-
/** Tool name prefixes to skip filtering (e.g., ["
|
|
2068
|
+
/** Tool name prefixes to skip filtering (e.g., ["*"] to skip all system tools) */
|
|
2032
2069
|
bypass_prefixes: string[];
|
|
2033
2070
|
/** Log but don't filter — for gradual rollout (default: false) */
|
|
2034
2071
|
log_only: boolean;
|
|
@@ -2083,10 +2120,13 @@ declare class ContextGateEnforcer {
|
|
|
2083
2120
|
* Check if a tool should be filtered based on bypass prefixes.
|
|
2084
2121
|
*
|
|
2085
2122
|
* SEC-033: Uses exact namespace component matching, not bare startsWith().
|
|
2086
|
-
* A prefix of "
|
|
2087
|
-
*
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
2123
|
+
* A prefix of "proxy/" matches "proxy/server/tool" but NOT "proxyevil/steal".
|
|
2124
|
+
* The prefix must match exactly up to its length, and the prefix must end
|
|
2125
|
+
* with "/" to enforce namespace boundaries (if it doesn't, we add one).
|
|
2126
|
+
*
|
|
2127
|
+
* Special sentinel: "*" bypasses ALL tools (used when all Sanctuary-internal
|
|
2128
|
+
* tools should skip context gating — the default). Only proxy/external tools
|
|
2129
|
+
* should be filtered in production.
|
|
2090
2130
|
*/
|
|
2091
2131
|
shouldFilter(toolName: string): boolean;
|
|
2092
2132
|
/**
|
|
@@ -2481,6 +2521,15 @@ interface ProxyRouterOptions {
|
|
|
2481
2521
|
contextGateFilter?: (toolName: string, args: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
2482
2522
|
/** Optional call governor for runtime governance */
|
|
2483
2523
|
governor?: CallGovernor;
|
|
2524
|
+
/** Optional callback after each proxy call decision (for dashboard feed) */
|
|
2525
|
+
onProxyCall?: (data: {
|
|
2526
|
+
tool: string;
|
|
2527
|
+
server: string;
|
|
2528
|
+
decision: string;
|
|
2529
|
+
reason?: string;
|
|
2530
|
+
tier?: number;
|
|
2531
|
+
timestamp: string;
|
|
2532
|
+
}) => void;
|
|
2484
2533
|
}
|
|
2485
2534
|
declare class ProxyRouter {
|
|
2486
2535
|
private clientManager;
|
|
@@ -2511,6 +2560,10 @@ declare class ProxyRouter {
|
|
|
2511
2560
|
* The handler runs the full enforcement chain before forwarding.
|
|
2512
2561
|
*/
|
|
2513
2562
|
private createHandler;
|
|
2563
|
+
/**
|
|
2564
|
+
* Notify the onProxyCall callback if configured.
|
|
2565
|
+
*/
|
|
2566
|
+
private notifyProxyCall;
|
|
2514
2567
|
/**
|
|
2515
2568
|
* Call an upstream tool with a timeout.
|
|
2516
2569
|
*/
|
|
@@ -2604,37 +2657,6 @@ declare class FilesystemStorage implements StorageBackend {
|
|
|
2604
2657
|
*/
|
|
2605
2658
|
declare function loadPrincipalPolicy(storagePath: string): Promise<PrincipalPolicy>;
|
|
2606
2659
|
|
|
2607
|
-
/**
|
|
2608
|
-
* Sanctuary MCP Server — L1 Cognitive Sovereignty: Tool Definitions
|
|
2609
|
-
*
|
|
2610
|
-
* MCP tool wrappers for StateStore and IdentityRoot operations.
|
|
2611
|
-
* These tools are the public API that agents interact with.
|
|
2612
|
-
*/
|
|
2613
|
-
|
|
2614
|
-
/** Manages all identities — provides storage and retrieval */
|
|
2615
|
-
declare class IdentityManager {
|
|
2616
|
-
private storage;
|
|
2617
|
-
private masterKey;
|
|
2618
|
-
private identities;
|
|
2619
|
-
private primaryIdentityId;
|
|
2620
|
-
constructor(storage: StorageBackend, masterKey: Uint8Array);
|
|
2621
|
-
private get encryptionKey();
|
|
2622
|
-
/** Load identities from storage on startup.
|
|
2623
|
-
* Returns { total: number of encrypted files found, loaded: number successfully decrypted }.
|
|
2624
|
-
* A mismatch (total > 0, loaded === 0) indicates a wrong master key / missing passphrase.
|
|
2625
|
-
*/
|
|
2626
|
-
load(): Promise<{
|
|
2627
|
-
total: number;
|
|
2628
|
-
loaded: number;
|
|
2629
|
-
failed: number;
|
|
2630
|
-
}>;
|
|
2631
|
-
/** Save an identity to storage */
|
|
2632
|
-
save(identity: StoredIdentity): Promise<void>;
|
|
2633
|
-
get(id: string): StoredIdentity | undefined;
|
|
2634
|
-
getDefault(): StoredIdentity | undefined;
|
|
2635
|
-
list(): PublicIdentity[];
|
|
2636
|
-
}
|
|
2637
|
-
|
|
2638
2660
|
/**
|
|
2639
2661
|
* Sanctuary MCP Server — SHR Generator
|
|
2640
2662
|
*
|
|
@@ -2710,6 +2732,7 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2710
2732
|
private profileStore;
|
|
2711
2733
|
private clientManager;
|
|
2712
2734
|
private dashboardHTML;
|
|
2735
|
+
private fortressHTML;
|
|
2713
2736
|
private loginHTML;
|
|
2714
2737
|
private authToken;
|
|
2715
2738
|
private useTLS;
|
|
@@ -2815,6 +2838,24 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
|
|
|
2815
2838
|
private handleSessionExchange;
|
|
2816
2839
|
private serveLoginPage;
|
|
2817
2840
|
private serveDashboard;
|
|
2841
|
+
private serveFortressView;
|
|
2842
|
+
/**
|
|
2843
|
+
* Enable Fortress View (Cocoon mode) with the given upstream server count.
|
|
2844
|
+
* Once enabled, the root path `/` serves the Fortress View instead of the
|
|
2845
|
+
* standard dashboard. The standard dashboard remains available at `/dashboard`.
|
|
2846
|
+
*/
|
|
2847
|
+
enableFortressView(upstreamServerCount: number): void;
|
|
2848
|
+
/**
|
|
2849
|
+
* Broadcast a proxy call event to connected dashboards (Fortress View feed).
|
|
2850
|
+
*/
|
|
2851
|
+
broadcastProxyCall(data: {
|
|
2852
|
+
tool: string;
|
|
2853
|
+
server: string;
|
|
2854
|
+
decision: string;
|
|
2855
|
+
reason?: string;
|
|
2856
|
+
tier?: number;
|
|
2857
|
+
timestamp: string;
|
|
2858
|
+
}): void;
|
|
2818
2859
|
private handleSSE;
|
|
2819
2860
|
private handleStatus;
|
|
2820
2861
|
private handlePendingList;
|
|
@@ -3333,6 +3374,16 @@ declare function verifyBridgeCommitment(commitment: BridgeCommitment, outcome: C
|
|
|
3333
3374
|
interface SanctuaryServer {
|
|
3334
3375
|
server: Server;
|
|
3335
3376
|
config: SanctuaryConfig;
|
|
3377
|
+
/**
|
|
3378
|
+
* Runtime dependencies exposed for embedding callers that need
|
|
3379
|
+
* direct access to the Sanctuary substrate (e.g., the EU AI Act
|
|
3380
|
+
* compliance CLI subcommand). Most callers only use `server` and
|
|
3381
|
+
* `config`; these are optional-usage extras.
|
|
3382
|
+
*/
|
|
3383
|
+
identityManager: IdentityManager;
|
|
3384
|
+
masterKey: Uint8Array;
|
|
3385
|
+
auditLog: AuditLog;
|
|
3386
|
+
policy: PrincipalPolicy;
|
|
3336
3387
|
}
|
|
3337
3388
|
/**
|
|
3338
3389
|
* Initialize the Sanctuary MCP Server.
|