@sanctuary-framework/mcp-server 0.2.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.
@@ -0,0 +1,1207 @@
1
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
2
+
3
+ /**
4
+ * Sanctuary MCP Server — Configuration
5
+ *
6
+ * Loads and validates server configuration from file or environment variables.
7
+ */
8
+ interface SanctuaryConfig {
9
+ version: string;
10
+ storage_path: string;
11
+ principal_id?: string;
12
+ state: {
13
+ encryption: "aes-256-gcm";
14
+ key_protection: "passphrase" | "hardware-key" | "none";
15
+ key_derivation: "argon2id";
16
+ integrity: "merkle-sha256";
17
+ identity_provider: "ed25519";
18
+ };
19
+ execution: {
20
+ environment: "local-process" | "docker" | "tee";
21
+ attestation: boolean;
22
+ resource_limits: {
23
+ max_memory_mb: number;
24
+ max_storage_mb: number;
25
+ max_cpu_percent: number;
26
+ };
27
+ };
28
+ disclosure: {
29
+ proof_system: "groth16" | "plonk" | "commitment-only";
30
+ default_policy: "minimum-necessary" | "withhold-all";
31
+ };
32
+ reputation: {
33
+ mode: "self-custodied" | "service-mediated";
34
+ attestation_format: "eas-compatible";
35
+ export_format: "SANCTUARY_REP_V1";
36
+ service_endpoints: string[];
37
+ };
38
+ transport: "stdio" | "http";
39
+ http_port: number;
40
+ }
41
+ /**
42
+ * Load configuration from file, falling back to defaults.
43
+ */
44
+ declare function loadConfig(configPath?: string): Promise<SanctuaryConfig>;
45
+
46
+ /**
47
+ * Sanctuary MCP Server — Storage Backend Interface
48
+ *
49
+ * Abstract interface for persistent storage. All state flows through this
50
+ * interface, making the storage backend pluggable (filesystem, IPFS, S3, etc.).
51
+ *
52
+ * The storage backend deals in raw bytes — encryption/decryption happens
53
+ * in the StateStore layer above.
54
+ */
55
+ /** Metadata about a stored entry */
56
+ interface StorageEntryMeta {
57
+ key: string;
58
+ namespace: string;
59
+ size_bytes: number;
60
+ modified_at: string;
61
+ }
62
+ /** Abstract storage backend interface */
63
+ interface StorageBackend {
64
+ /**
65
+ * Write raw bytes to storage.
66
+ * @param namespace - Logical grouping
67
+ * @param key - Entry key within namespace
68
+ * @param data - Raw bytes to store
69
+ */
70
+ write(namespace: string, key: string, data: Uint8Array): Promise<void>;
71
+ /**
72
+ * Read raw bytes from storage.
73
+ * @returns The stored bytes, or null if not found
74
+ */
75
+ read(namespace: string, key: string): Promise<Uint8Array | null>;
76
+ /**
77
+ * Delete an entry from storage.
78
+ * @param secureOverwrite - If true, overwrite with random bytes before deletion
79
+ */
80
+ delete(namespace: string, key: string, secureOverwrite?: boolean): Promise<boolean>;
81
+ /**
82
+ * List all entries in a namespace.
83
+ */
84
+ list(namespace: string, prefix?: string): Promise<StorageEntryMeta[]>;
85
+ /**
86
+ * Check if an entry exists.
87
+ */
88
+ exists(namespace: string, key: string): Promise<boolean>;
89
+ /**
90
+ * Get the total size of all stored data.
91
+ */
92
+ totalSize(): Promise<number>;
93
+ }
94
+
95
+ /**
96
+ * Sanctuary MCP Server — AES-256-GCM Encryption
97
+ *
98
+ * All state encryption in Sanctuary uses AES-256-GCM (authenticated encryption).
99
+ * This provides both confidentiality and integrity — a modified ciphertext will
100
+ * fail authentication, detecting tampering.
101
+ *
102
+ * Security invariants:
103
+ * - Every encryption uses a unique 12-byte IV (NIST SP 800-38D)
104
+ * - The 16-byte authentication tag is always verified on decryption
105
+ * - Keys are 256 bits (32 bytes)
106
+ */
107
+ /** Encrypted payload structure stored on disk */
108
+ interface EncryptedPayload {
109
+ /** Format version */
110
+ v: number;
111
+ /** Algorithm identifier */
112
+ alg: "aes-256-gcm";
113
+ /** Initialization vector (base64url) */
114
+ iv: string;
115
+ /** Ciphertext (base64url) */
116
+ ct: string;
117
+ /** Authentication tag (base64url) — included in ciphertext by @noble/ciphers */
118
+ /** Timestamp */
119
+ ts: string;
120
+ }
121
+
122
+ /**
123
+ * Sanctuary MCP Server — L1 Cognitive Sovereignty: StateStore
124
+ *
125
+ * The encrypted state store is the foundation of Sanctuary.
126
+ * Every read and write goes through here. All data is encrypted
127
+ * with namespace-specific keys. All writes are signed by an identity.
128
+ * All reads verify integrity via Merkle proofs.
129
+ *
130
+ * Security invariants:
131
+ * - Plaintext never touches the filesystem
132
+ * - Every write gets a unique IV
133
+ * - Every write is signed (non-repudiation)
134
+ * - Monotonic version numbers prevent rollback
135
+ * - Merkle tree verifies namespace integrity
136
+ * - Secure deletion overwrites before unlinking
137
+ */
138
+
139
+ /** Result of a state write operation */
140
+ interface WriteResult {
141
+ key: string;
142
+ namespace: string;
143
+ version: number;
144
+ merkle_root: string;
145
+ written_at: string;
146
+ size_bytes: number;
147
+ integrity_hash: string;
148
+ }
149
+ /** Result of a state read operation */
150
+ interface ReadResult {
151
+ key: string;
152
+ namespace: string;
153
+ value: string;
154
+ version: number;
155
+ integrity_verified: boolean;
156
+ merkle_proof: string[];
157
+ written_at: string;
158
+ written_by: string;
159
+ }
160
+ /** Options for state write */
161
+ interface WriteOptions {
162
+ content_type?: string;
163
+ ttl_seconds?: number;
164
+ tags?: string[];
165
+ }
166
+ declare class StateStore {
167
+ private storage;
168
+ private masterKey;
169
+ private versionCache;
170
+ private contentHashes;
171
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
172
+ private versionKey;
173
+ /**
174
+ * Get or initialize the content hash map for a namespace.
175
+ */
176
+ private getNamespaceHashes;
177
+ /**
178
+ * Write encrypted state.
179
+ *
180
+ * @param namespace - Logical grouping
181
+ * @param key - State key
182
+ * @param value - Plaintext value (will be encrypted)
183
+ * @param identityId - Identity performing the write
184
+ * @param encryptedPrivateKey - Identity's encrypted private key (for signing)
185
+ * @param identityEncryptionKey - Key to decrypt the identity's private key
186
+ * @param options - Optional metadata
187
+ */
188
+ write(namespace: string, key: string, value: string, identityId: string, encryptedPrivateKey: EncryptedPayload, identityEncryptionKey: Uint8Array, options?: WriteOptions): Promise<WriteResult>;
189
+ /**
190
+ * Read and decrypt state.
191
+ *
192
+ * @param namespace - Logical grouping
193
+ * @param key - State key
194
+ * @param signerPublicKey - Expected signer's public key (for signature verification)
195
+ * @param verifyIntegrity - Whether to verify Merkle proof (default: true)
196
+ */
197
+ read(namespace: string, key: string, signerPublicKey?: Uint8Array, verifyIntegrity?: boolean): Promise<ReadResult | null>;
198
+ /**
199
+ * List keys in a namespace (metadata only — no decryption).
200
+ */
201
+ list(namespace: string, prefix?: string, tags?: string[], limit?: number, offset?: number): Promise<{
202
+ keys: Array<{
203
+ key: string;
204
+ version: number;
205
+ size_bytes: number;
206
+ written_at: string;
207
+ tags: string[];
208
+ }>;
209
+ total: number;
210
+ merkle_root: string;
211
+ }>;
212
+ /**
213
+ * Securely delete state (overwrite with random bytes before removal).
214
+ */
215
+ delete(namespace: string, key: string): Promise<{
216
+ deleted: boolean;
217
+ key: string;
218
+ namespace: string;
219
+ new_merkle_root: string;
220
+ deleted_at: string;
221
+ }>;
222
+ /**
223
+ * Export all state for a namespace as an encrypted bundle.
224
+ */
225
+ export(namespace?: string): Promise<{
226
+ bundle: string;
227
+ namespaces: string[];
228
+ total_keys: number;
229
+ bundle_hash: string;
230
+ exported_at: string;
231
+ }>;
232
+ /**
233
+ * Import a previously exported state bundle.
234
+ */
235
+ import(bundleBase64: string, conflictResolution?: "skip" | "overwrite" | "version"): Promise<{
236
+ imported_keys: number;
237
+ skipped_keys: number;
238
+ conflicts: number;
239
+ namespaces: string[];
240
+ imported_at: string;
241
+ }>;
242
+ }
243
+
244
+ /**
245
+ * Sanctuary MCP Server — L2 Operational Isolation: Audit Log
246
+ *
247
+ * Append-only log of all sovereignty-relevant operations.
248
+ * Stored encrypted under L1 sovereignty.
249
+ *
250
+ * Every tool invocation that modifies state, generates proofs,
251
+ * or records reputation produces an audit entry. The human principal
252
+ * can inspect what their agent has done.
253
+ */
254
+
255
+ interface AuditEntry {
256
+ timestamp: string;
257
+ layer: "l1" | "l2" | "l3" | "l4";
258
+ operation: string;
259
+ identity_id: string;
260
+ result: "success" | "failure";
261
+ details?: Record<string, unknown>;
262
+ }
263
+ declare class AuditLog {
264
+ private storage;
265
+ private encryptionKey;
266
+ private entries;
267
+ private counter;
268
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
269
+ /**
270
+ * Append an audit entry.
271
+ */
272
+ append(layer: AuditEntry["layer"], operation: string, identityId: string, details?: Record<string, unknown>, result?: "success" | "failure"): void;
273
+ private persistEntry;
274
+ /**
275
+ * Query the audit log with filtering.
276
+ */
277
+ query(options: {
278
+ since?: string;
279
+ layer?: AuditEntry["layer"];
280
+ operation_type?: string;
281
+ limit?: number;
282
+ }): Promise<{
283
+ entries: AuditEntry[];
284
+ total: number;
285
+ }>;
286
+ private loadPersistedEntries;
287
+ /**
288
+ * Get total number of entries.
289
+ */
290
+ get size(): number;
291
+ }
292
+
293
+ /**
294
+ * Sanctuary MCP Server — L3 Selective Disclosure: Commitment Schemes
295
+ *
296
+ * Cryptographic commitments allow an agent to commit to a value
297
+ * without revealing it, then later prove what was committed.
298
+ *
299
+ * This is the MVS approach to selective disclosure — simpler than
300
+ * full ZK proofs but still cryptographically sound. The commitment
301
+ * is SHA-256(value || blinding_factor), which is:
302
+ * - Hiding: the commitment reveals nothing about the value
303
+ * - Binding: the committer cannot change the value after committing
304
+ *
305
+ * Security invariants:
306
+ * - Blinding factors are cryptographically random (32 bytes)
307
+ * - Commitments are stored encrypted under L1 sovereignty
308
+ * - Revealed values are verified via constant-time comparison
309
+ */
310
+
311
+ /** A cryptographic commitment */
312
+ interface Commitment {
313
+ /** The commitment hash: SHA-256(value || blinding_factor) as base64url */
314
+ commitment: string;
315
+ /** The blinding factor (must be stored securely for later reveal) */
316
+ blinding_factor: string;
317
+ /** When the commitment was created */
318
+ committed_at: string;
319
+ }
320
+ /** Stored commitment metadata (encrypted at rest) */
321
+ interface StoredCommitment {
322
+ commitment: string;
323
+ blinding_factor: string;
324
+ value: string;
325
+ committed_at: string;
326
+ revealed: boolean;
327
+ revealed_at?: string;
328
+ }
329
+ /**
330
+ * Commitment store — manages commitments encrypted under L1 sovereignty.
331
+ */
332
+ declare class CommitmentStore {
333
+ private storage;
334
+ private encryptionKey;
335
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
336
+ /**
337
+ * Store a commitment (encrypted) for later reference.
338
+ */
339
+ store(commitment: Commitment, value: string): Promise<string>;
340
+ /**
341
+ * Retrieve a stored commitment by ID.
342
+ */
343
+ get(id: string): Promise<StoredCommitment | null>;
344
+ /**
345
+ * Mark a commitment as revealed.
346
+ */
347
+ markRevealed(id: string): Promise<void>;
348
+ }
349
+
350
+ /**
351
+ * Sanctuary MCP Server — L3 Selective Disclosure: Disclosure Policies
352
+ *
353
+ * Disclosure policies define what an agent will and will not disclose
354
+ * in different interaction contexts. Policies are evaluated against
355
+ * incoming disclosure requests to produce per-field decisions.
356
+ *
357
+ * This is the agent's "privacy preferences" layer — it codifies the
358
+ * human principal's intent about what information can flow where.
359
+ *
360
+ * Security invariants:
361
+ * - Policies are stored encrypted under L1 sovereignty
362
+ * - Default action is always "withhold" unless explicitly overridden
363
+ * - Policy evaluation is deterministic (same request → same decision)
364
+ */
365
+
366
+ /** A single disclosure rule within a policy */
367
+ interface DisclosureRule {
368
+ /** Interaction context this rule applies to */
369
+ context: string;
370
+ /** Fields/claims the agent MAY disclose */
371
+ disclose: string[];
372
+ /** Fields/claims the agent MUST NOT disclose */
373
+ withhold: string[];
374
+ /** Fields that require proof rather than plain disclosure */
375
+ proof_required: string[];
376
+ }
377
+ /** A complete disclosure policy */
378
+ interface DisclosurePolicy {
379
+ policy_id: string;
380
+ policy_name: string;
381
+ rules: DisclosureRule[];
382
+ default_action: "withhold" | "ask-principal";
383
+ identity_id?: string;
384
+ created_at: string;
385
+ updated_at: string;
386
+ }
387
+ /**
388
+ * Policy store — manages disclosure policies encrypted under L1 sovereignty.
389
+ */
390
+ declare class PolicyStore {
391
+ private storage;
392
+ private encryptionKey;
393
+ private policies;
394
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
395
+ /**
396
+ * Create and store a new disclosure policy.
397
+ */
398
+ create(policyName: string, rules: DisclosureRule[], defaultAction: "withhold" | "ask-principal", identityId?: string): Promise<DisclosurePolicy>;
399
+ /**
400
+ * Get a policy by ID.
401
+ */
402
+ get(policyId: string): Promise<DisclosurePolicy | null>;
403
+ /**
404
+ * List all policies.
405
+ */
406
+ list(): Promise<DisclosurePolicy[]>;
407
+ /**
408
+ * Load all persisted policies into memory.
409
+ */
410
+ private loadAll;
411
+ private persist;
412
+ }
413
+
414
+ /**
415
+ * Sanctuary MCP Server — Ed25519 Identity Management
416
+ *
417
+ * Sovereign identity based on Ed25519 keypairs.
418
+ * Private keys are always encrypted at rest — never stored in plaintext.
419
+ *
420
+ * Security invariants:
421
+ * - Private keys never appear in any MCP tool response
422
+ * - Private keys are encrypted with identity-specific keys derived from the master key
423
+ * - Key rotation produces a signed rotation event (verifiable chain)
424
+ */
425
+
426
+ /** Public identity information (safe to share) */
427
+ interface PublicIdentity {
428
+ identity_id: string;
429
+ label: string;
430
+ public_key: string;
431
+ did: string;
432
+ created_at: string;
433
+ key_type: "ed25519";
434
+ key_protection: "passphrase" | "hardware-key" | "recovery-key";
435
+ }
436
+ /** Stored identity (private key is encrypted) */
437
+ interface StoredIdentity extends PublicIdentity {
438
+ encrypted_private_key: EncryptedPayload;
439
+ /** Previous public keys (for rotation chain verification) */
440
+ rotation_history: Array<{
441
+ old_public_key: string;
442
+ new_public_key: string;
443
+ rotation_event: string;
444
+ rotated_at: string;
445
+ }>;
446
+ }
447
+
448
+ /**
449
+ * Sanctuary MCP Server — L4 Verifiable Reputation: Reputation Store
450
+ *
451
+ * Records interaction outcomes as signed attestations, queries aggregated
452
+ * reputation data, and supports export/import for cross-platform portability.
453
+ *
454
+ * Attestation format is EAS-compatible (Ethereum Attestation Service) to
455
+ * enable future on-chain anchoring without requiring blockchain for MVS.
456
+ *
457
+ * Security invariants:
458
+ * - All attestations are signed by the recording identity
459
+ * - Attestations are stored encrypted under L1 sovereignty
460
+ * - Reputation queries return aggregates, never raw interaction data
461
+ * - Export bundles include all signatures for independent verification
462
+ * - Import verifies every signature before accepting attestations
463
+ */
464
+
465
+ /** Interaction outcome for recording */
466
+ interface InteractionOutcome {
467
+ type: "transaction" | "negotiation" | "service" | "dispute" | "custom";
468
+ result: "completed" | "partial" | "failed" | "disputed";
469
+ metrics?: Record<string, number>;
470
+ }
471
+ /** A signed attestation of an interaction */
472
+ interface Attestation {
473
+ attestation_id: string;
474
+ schema: "sanctuary-interaction-v1";
475
+ data: {
476
+ interaction_id: string;
477
+ participant_did: string;
478
+ counterparty_did: string;
479
+ outcome_type: string;
480
+ outcome_result: string;
481
+ metrics: Record<string, number>;
482
+ context: string;
483
+ timestamp: string;
484
+ };
485
+ signature: string;
486
+ signer: string;
487
+ }
488
+ /** Stored attestation (encrypted at rest) */
489
+ interface StoredAttestation {
490
+ attestation: Attestation;
491
+ counterparty_attestation?: string;
492
+ counterparty_confirmed: boolean;
493
+ recorded_at: string;
494
+ }
495
+ /** Aggregated metric statistics */
496
+ interface MetricAggregate {
497
+ mean: number;
498
+ median: number;
499
+ min: number;
500
+ max: number;
501
+ count: number;
502
+ }
503
+ /** Reputation query result */
504
+ interface ReputationSummary {
505
+ total_interactions: number;
506
+ completed: number;
507
+ partial: number;
508
+ failed: number;
509
+ disputed: number;
510
+ contexts: string[];
511
+ time_range: {
512
+ start: string;
513
+ end: string;
514
+ };
515
+ aggregate_metrics: Record<string, MetricAggregate>;
516
+ }
517
+ /** Portable reputation bundle */
518
+ interface ReputationBundle {
519
+ version: "SANCTUARY_REP_V1";
520
+ attestations: Attestation[];
521
+ exported_at: string;
522
+ exporter_did: string;
523
+ bundle_signature: string;
524
+ }
525
+ /** Escrow for trust bootstrapping */
526
+ interface Escrow {
527
+ escrow_id: string;
528
+ transaction_terms: string;
529
+ terms_hash: string;
530
+ collateral_amount?: number;
531
+ counterparty_did: string;
532
+ creator_did: string;
533
+ created_at: string;
534
+ expires_at: string;
535
+ status: "pending" | "active" | "released" | "disputed" | "expired";
536
+ }
537
+ /** Principal guarantee for a new agent */
538
+ interface Guarantee {
539
+ guarantee_id: string;
540
+ principal_did: string;
541
+ agent_did: string;
542
+ scope: string;
543
+ max_liability?: number;
544
+ valid_until: string;
545
+ certificate: string;
546
+ created_at: string;
547
+ }
548
+ declare class ReputationStore {
549
+ private storage;
550
+ private encryptionKey;
551
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
552
+ /**
553
+ * Record an interaction outcome as a signed attestation.
554
+ */
555
+ record(interactionId: string, counterpartyDid: string, outcome: InteractionOutcome, context: string, identity: StoredIdentity, identityEncryptionKey: Uint8Array, counterpartyAttestation?: string): Promise<StoredAttestation>;
556
+ /**
557
+ * Query reputation data with filtering.
558
+ * Returns aggregates only — not raw interaction data.
559
+ */
560
+ query(options: {
561
+ context?: string;
562
+ time_range?: {
563
+ start: string;
564
+ end: string;
565
+ };
566
+ metrics?: string[];
567
+ counterparty_did?: string;
568
+ }): Promise<ReputationSummary>;
569
+ /**
570
+ * Export attestations as a portable reputation bundle.
571
+ */
572
+ exportBundle(identity: StoredIdentity, identityEncryptionKey: Uint8Array, context?: string): Promise<ReputationBundle>;
573
+ /**
574
+ * Import attestations from a reputation bundle.
575
+ * Verifies signatures if requested (default: true).
576
+ *
577
+ * @param publicKeys - Map of DID → public key bytes for signature verification
578
+ */
579
+ importBundle(bundle: ReputationBundle, verifySignatures: boolean, publicKeys: Map<string, Uint8Array>): Promise<{
580
+ imported: number;
581
+ invalid: number;
582
+ contexts: string[];
583
+ }>;
584
+ /**
585
+ * Create an escrow for trust bootstrapping.
586
+ */
587
+ createEscrow(transactionTerms: string, counterpartyDid: string, timeoutSeconds: number, creatorDid: string, collateralAmount?: number): Promise<Escrow>;
588
+ /**
589
+ * Get an escrow by ID.
590
+ */
591
+ getEscrow(escrowId: string): Promise<Escrow | null>;
592
+ /**
593
+ * Create a principal's guarantee for a new agent.
594
+ */
595
+ createGuarantee(principalIdentity: StoredIdentity, agentDid: string, scope: string, durationSeconds: number, identityEncryptionKey: Uint8Array, maxLiability?: number): Promise<Guarantee>;
596
+ private loadAll;
597
+ }
598
+
599
+ /**
600
+ * Sanctuary MCP Server — In-Memory Storage Backend
601
+ *
602
+ * Used for testing. Implements the same interface as filesystem storage
603
+ * but stores everything in memory. Data does not persist across restarts.
604
+ */
605
+
606
+ declare class MemoryStorage implements StorageBackend {
607
+ private store;
608
+ private storageKey;
609
+ write(namespace: string, key: string, data: Uint8Array): Promise<void>;
610
+ read(namespace: string, key: string): Promise<Uint8Array | null>;
611
+ delete(namespace: string, key: string, _secureOverwrite?: boolean): Promise<boolean>;
612
+ list(namespace: string, prefix?: string): Promise<StorageEntryMeta[]>;
613
+ exists(namespace: string, key: string): Promise<boolean>;
614
+ totalSize(): Promise<number>;
615
+ /** Clear all stored data (useful in tests) */
616
+ clear(): void;
617
+ }
618
+
619
+ /**
620
+ * Sanctuary MCP Server — Filesystem Storage Backend
621
+ *
622
+ * Default storage backend using the local filesystem.
623
+ * Files are stored as: {basePath}/{namespace}/{key}.enc
624
+ *
625
+ * Security invariants:
626
+ * - Secure deletion overwrites file content with random bytes before unlinking
627
+ * - Directory creation uses restrictive permissions (0o700)
628
+ * - File creation uses restrictive permissions (0o600)
629
+ */
630
+
631
+ declare class FilesystemStorage implements StorageBackend {
632
+ private basePath;
633
+ constructor(basePath: string);
634
+ private entryPath;
635
+ private namespacePath;
636
+ write(namespace: string, key: string, data: Uint8Array): Promise<void>;
637
+ read(namespace: string, key: string): Promise<Uint8Array | null>;
638
+ delete(namespace: string, key: string, secureOverwrite?: boolean): Promise<boolean>;
639
+ list(namespace: string, prefix?: string): Promise<StorageEntryMeta[]>;
640
+ exists(namespace: string, key: string): Promise<boolean>;
641
+ totalSize(): Promise<number>;
642
+ }
643
+
644
+ /**
645
+ * Sanctuary MCP Server — Principal Policy Types
646
+ *
647
+ * Type definitions for the Principal Policy system.
648
+ * The Principal Policy is the human-controlled, agent-immutable
649
+ * configuration that gates operations through approval tiers.
650
+ */
651
+ /** Tier 2 anomaly action: what to do when an anomaly is detected */
652
+ type AnomalyAction = "approve" | "log" | "allow";
653
+ /** Tier 2 anomaly detection configuration */
654
+ interface Tier2Config {
655
+ /** Action when agent accesses a namespace it hasn't used before */
656
+ new_namespace_access: AnomalyAction;
657
+ /** Action when agent interacts with an unknown counterparty DID */
658
+ new_counterparty: AnomalyAction;
659
+ /** Tool call frequency multiplier that triggers anomaly */
660
+ frequency_spike_multiplier: number;
661
+ /** Maximum signing operations per minute before triggering */
662
+ max_signs_per_minute: number;
663
+ /** Reading more than N keys in a namespace within 60 seconds */
664
+ bulk_read_threshold: number;
665
+ /** Policy for first session when no baseline exists */
666
+ first_session_policy: AnomalyAction;
667
+ }
668
+ /** Approval channel configuration */
669
+ interface ApprovalChannelConfig {
670
+ type: "stderr" | "webhook" | "callback";
671
+ timeout_seconds: number;
672
+ auto_deny: boolean;
673
+ webhook_url?: string;
674
+ webhook_secret?: string;
675
+ }
676
+ /** Complete Principal Policy */
677
+ interface PrincipalPolicy {
678
+ version: number;
679
+ /** Operations that always require human approval */
680
+ tier1_always_approve: string[];
681
+ /** Behavioral anomaly detection configuration */
682
+ tier2_anomaly: Tier2Config;
683
+ /** Operations that never require approval (audit only) */
684
+ tier3_always_allow: string[];
685
+ /** How approval requests reach the human */
686
+ approval_channel: ApprovalChannelConfig;
687
+ }
688
+ /** Approval request sent to the human */
689
+ interface ApprovalRequest {
690
+ operation: string;
691
+ tier: 1 | 2;
692
+ reason: string;
693
+ context: Record<string, unknown>;
694
+ timestamp: string;
695
+ }
696
+ /** Approval response from the human */
697
+ interface ApprovalResponse {
698
+ decision: "approve" | "deny";
699
+ decided_at: string;
700
+ decided_by: "human" | "timeout" | "auto";
701
+ }
702
+ /** Result of the approval gate evaluation */
703
+ interface GateResult {
704
+ allowed: boolean;
705
+ tier: 1 | 2 | 3;
706
+ reason: string;
707
+ approval_required: boolean;
708
+ approval_response?: ApprovalResponse;
709
+ }
710
+ /** Behavioral baseline for anomaly detection */
711
+ interface SessionProfile {
712
+ /** Namespaces accessed (read or write) */
713
+ known_namespaces: string[];
714
+ /** Counterparty DIDs seen in reputation operations */
715
+ known_counterparties: string[];
716
+ /** Tool call counts per tool name (lifetime in session) */
717
+ tool_call_counts: Record<string, number>;
718
+ /** Whether this is the first session (no prior baseline) */
719
+ is_first_session: boolean;
720
+ /** Session start time */
721
+ started_at: string;
722
+ /** When the baseline was last saved */
723
+ saved_at?: string;
724
+ }
725
+
726
+ /**
727
+ * Sanctuary MCP Server — Approval Channel
728
+ *
729
+ * Out-of-band communication with the human principal for operation approval.
730
+ * The default channel uses stderr (outside MCP's stdin/stdout protocol),
731
+ * ensuring the agent cannot intercept or forge approval responses.
732
+ *
733
+ * Security invariant:
734
+ * - Approval prompts go through a channel the agent cannot access.
735
+ * - Timeouts result in denial by default (fail closed).
736
+ */
737
+
738
+ /** Abstract approval channel interface */
739
+ interface ApprovalChannel {
740
+ requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
741
+ }
742
+ /**
743
+ * Stderr approval channel — writes prompts to stderr, waits for response.
744
+ *
745
+ * In the MCP stdio model:
746
+ * - stdin/stdout carry the MCP protocol (JSON-RPC)
747
+ * - stderr is available for out-of-band human communication
748
+ *
749
+ * Since many harnesses do not support interactive stdin during tool calls,
750
+ * this channel uses a timeout-based model: the prompt is displayed, and
751
+ * if no response is received within the timeout, the default action applies.
752
+ *
753
+ * For MVS, the channel auto-resolves based on the auto_deny setting.
754
+ * Interactive stdin reading is deferred to a future version with harness support.
755
+ */
756
+ declare class StderrApprovalChannel implements ApprovalChannel {
757
+ private config;
758
+ constructor(config: ApprovalChannelConfig);
759
+ requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
760
+ private formatPrompt;
761
+ }
762
+ /**
763
+ * Programmatic approval channel — for testing and API integration.
764
+ */
765
+ declare class CallbackApprovalChannel implements ApprovalChannel {
766
+ private callback;
767
+ constructor(callback: (request: ApprovalRequest) => Promise<ApprovalResponse>);
768
+ requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
769
+ }
770
+ /**
771
+ * Auto-approve channel — for testing. Approves everything.
772
+ */
773
+ declare class AutoApproveChannel implements ApprovalChannel {
774
+ requestApproval(_request: ApprovalRequest): Promise<ApprovalResponse>;
775
+ }
776
+
777
+ /**
778
+ * Sanctuary MCP Server — Behavioral Baseline Tracker
779
+ *
780
+ * Tracks the agent's behavioral profile during a session and persists
781
+ * it for cross-session anomaly detection. The baseline defines "normal"
782
+ * so that deviations can trigger Tier 2 approval.
783
+ *
784
+ * Security invariants:
785
+ * - Baseline is stored encrypted under L1 sovereignty
786
+ * - Baseline changes are audit-logged
787
+ * - Baseline is integrity-verified via L1 Merkle tree
788
+ * - No MCP tool can directly modify the baseline
789
+ */
790
+
791
+ declare class BaselineTracker {
792
+ private storage;
793
+ private encryptionKey;
794
+ private profile;
795
+ /** Sliding window: timestamps of tool calls per tool name (last 60s) */
796
+ private callWindows;
797
+ /** Sliding window: read counts per namespace (last 60s) */
798
+ private readWindows;
799
+ /** Sliding window: sign call timestamps (last 60s) */
800
+ private signWindow;
801
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
802
+ /**
803
+ * Load the previous session's baseline from storage.
804
+ * If none exists, this is a first session.
805
+ */
806
+ load(): Promise<void>;
807
+ /**
808
+ * Save the current baseline to storage (encrypted).
809
+ * Called at session end or periodically.
810
+ */
811
+ save(): Promise<void>;
812
+ /**
813
+ * Record a tool call for baseline tracking.
814
+ * Returns anomaly information if applicable.
815
+ */
816
+ recordToolCall(toolName: string): void;
817
+ /**
818
+ * Record a namespace access.
819
+ * @returns true if this is a new namespace (not in baseline)
820
+ */
821
+ recordNamespaceAccess(namespace: string): boolean;
822
+ /**
823
+ * Record a namespace read for bulk-read detection.
824
+ * @returns the number of reads in the current 60-second window
825
+ */
826
+ recordNamespaceRead(namespace: string): number;
827
+ /**
828
+ * Record a counterparty DID interaction.
829
+ * @returns true if this is a new counterparty (not in baseline)
830
+ */
831
+ recordCounterparty(did: string): boolean;
832
+ /**
833
+ * Record a signing operation.
834
+ * @returns the number of signs in the current 60-second window
835
+ */
836
+ recordSign(): number;
837
+ /**
838
+ * Get the current call rate for a tool (calls per minute).
839
+ */
840
+ getCallRate(toolName: string): number;
841
+ /**
842
+ * Get the average call rate across all tools in the baseline.
843
+ */
844
+ getAverageCallRate(): number;
845
+ /** Whether this is the first session */
846
+ get isFirstSession(): boolean;
847
+ /** Get a read-only view of the current profile */
848
+ getProfile(): SessionProfile;
849
+ }
850
+
851
+ /**
852
+ * Sanctuary MCP Server — Approval Gate
853
+ *
854
+ * The three-tier approval gate sits between the MCP router and tool handlers.
855
+ * Every tool call passes through the gate before execution.
856
+ *
857
+ * Evaluation order:
858
+ * 1. Tier 1: Is this operation in the always-approve list? → Request approval.
859
+ * 2. Tier 2: Does this call represent a behavioral anomaly? → Request approval.
860
+ * 3. Tier 3 / default: Allow with audit logging.
861
+ *
862
+ * Security invariants:
863
+ * - The gate cannot be bypassed — it wraps every tool handler.
864
+ * - Denial responses do not reveal policy details to the agent.
865
+ * - All gate decisions (approve, deny, allow) are audit-logged.
866
+ */
867
+
868
+ declare class ApprovalGate {
869
+ private policy;
870
+ private baseline;
871
+ private channel;
872
+ private auditLog;
873
+ constructor(policy: PrincipalPolicy, baseline: BaselineTracker, channel: ApprovalChannel, auditLog: AuditLog);
874
+ /**
875
+ * Evaluate a tool call against the Principal Policy.
876
+ *
877
+ * @param toolName - Full MCP tool name (e.g., "sanctuary/state_export")
878
+ * @param args - Tool call arguments (for context extraction)
879
+ * @returns GateResult indicating whether the call is allowed
880
+ */
881
+ evaluate(toolName: string, args: Record<string, unknown>): Promise<GateResult>;
882
+ /**
883
+ * Detect Tier 2 behavioral anomalies.
884
+ */
885
+ private detectAnomaly;
886
+ /**
887
+ * Request approval from the human principal.
888
+ */
889
+ private requestApproval;
890
+ /**
891
+ * Summarize tool arguments for the approval prompt.
892
+ * Strips potentially large values to keep the prompt readable.
893
+ */
894
+ private summarizeArgs;
895
+ /** Get the baseline tracker for saving at session end */
896
+ getBaseline(): BaselineTracker;
897
+ }
898
+
899
+ /**
900
+ * Sanctuary MCP Server — Principal Policy Loader
901
+ *
902
+ * Loads the Principal Policy from a YAML file at server startup.
903
+ * The policy is immutable at runtime — no MCP tool can modify it.
904
+ *
905
+ * Security invariant:
906
+ * - The policy is loaded ONCE at startup and frozen.
907
+ * - No code path exists to modify the policy during a session.
908
+ * - If no policy file exists, a sensible default is generated and saved.
909
+ */
910
+
911
+ /**
912
+ * Load the Principal Policy from disk.
913
+ * If no policy file exists, generate the default and save it.
914
+ * The returned policy is frozen — immutable at runtime.
915
+ */
916
+ declare function loadPrincipalPolicy(storagePath: string): Promise<PrincipalPolicy>;
917
+
918
+ /**
919
+ * Sanctuary MCP Server — L1 Cognitive Sovereignty: Tool Definitions
920
+ *
921
+ * MCP tool wrappers for StateStore and IdentityRoot operations.
922
+ * These tools are the public API that agents interact with.
923
+ */
924
+
925
+ /** Manages all identities — provides storage and retrieval */
926
+ declare class IdentityManager {
927
+ private storage;
928
+ private masterKey;
929
+ private identities;
930
+ private primaryIdentityId;
931
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
932
+ private get encryptionKey();
933
+ /** Load identities from storage on startup */
934
+ load(): Promise<void>;
935
+ /** Save an identity to storage */
936
+ save(identity: StoredIdentity): Promise<void>;
937
+ get(id: string): StoredIdentity | undefined;
938
+ getDefault(): StoredIdentity | undefined;
939
+ list(): PublicIdentity[];
940
+ }
941
+
942
+ /**
943
+ * Sanctuary MCP Server — Sovereignty Health Report (SHR) Types
944
+ *
945
+ * Machine-readable, signed, versioned sovereignty capability advertisement.
946
+ * An agent presents its SHR to counterparties to prove its sovereignty posture.
947
+ * The SHR is signed by one of the instance's Ed25519 identities and can be
948
+ * independently verified by any party without trusting the presenter.
949
+ *
950
+ * SHR version: 1.0
951
+ */
952
+ type LayerStatus = "active" | "degraded" | "inactive";
953
+ type DegradationSeverity = "info" | "warning" | "critical";
954
+ type DegradationCode = "NO_TEE" | "PROCESS_ISOLATION_ONLY" | "COMMITMENT_ONLY" | "NO_ZK_PROOFS" | "SELF_REPORTED_ATTESTATION" | "NO_SELECTIVE_DISCLOSURE" | "BASIC_SYBIL_ONLY";
955
+ interface SHRLayerL1 {
956
+ status: LayerStatus;
957
+ encryption: string;
958
+ key_custody: "self" | "delegated" | "platform";
959
+ integrity: string;
960
+ identity_type: string;
961
+ state_portable: boolean;
962
+ }
963
+ interface SHRLayerL2 {
964
+ status: LayerStatus;
965
+ isolation_type: string;
966
+ attestation_available: boolean;
967
+ }
968
+ interface SHRLayerL3 {
969
+ status: LayerStatus;
970
+ proof_system: string;
971
+ selective_disclosure: boolean;
972
+ }
973
+ interface SHRLayerL4 {
974
+ status: LayerStatus;
975
+ reputation_mode: string;
976
+ attestation_format: string;
977
+ reputation_portable: boolean;
978
+ }
979
+ interface SHRDegradation {
980
+ layer: "l1" | "l2" | "l3" | "l4";
981
+ code: DegradationCode;
982
+ severity: DegradationSeverity;
983
+ description: string;
984
+ mitigation?: string;
985
+ }
986
+ interface SHRCapabilities {
987
+ handshake: boolean;
988
+ shr_exchange: boolean;
989
+ reputation_verify: boolean;
990
+ encrypted_channel: boolean;
991
+ }
992
+ /**
993
+ * The SHR body — the content that gets signed.
994
+ * Canonical form: JSON with sorted keys, no whitespace.
995
+ */
996
+ interface SHRBody {
997
+ shr_version: "1.0";
998
+ instance_id: string;
999
+ generated_at: string;
1000
+ expires_at: string;
1001
+ layers: {
1002
+ l1: SHRLayerL1;
1003
+ l2: SHRLayerL2;
1004
+ l3: SHRLayerL3;
1005
+ l4: SHRLayerL4;
1006
+ };
1007
+ capabilities: SHRCapabilities;
1008
+ degradations: SHRDegradation[];
1009
+ }
1010
+ /**
1011
+ * The complete signed SHR — body + signature envelope.
1012
+ */
1013
+ interface SignedSHR {
1014
+ body: SHRBody;
1015
+ signed_by: string;
1016
+ signature: string;
1017
+ }
1018
+ interface SHRVerificationResult {
1019
+ valid: boolean;
1020
+ errors: string[];
1021
+ warnings: string[];
1022
+ sovereignty_level: "full" | "degraded" | "minimal";
1023
+ counterparty_id: string;
1024
+ expires_at: string;
1025
+ }
1026
+
1027
+ /**
1028
+ * Sanctuary MCP Server — SHR Generator
1029
+ *
1030
+ * Generates a Sovereignty Health Report from current server state,
1031
+ * signs it with a specified identity, and returns the complete signed SHR.
1032
+ */
1033
+
1034
+ interface SHRGeneratorOptions {
1035
+ config: SanctuaryConfig;
1036
+ identityManager: IdentityManager;
1037
+ masterKey: Uint8Array;
1038
+ /** Override validity window (milliseconds). Default: 1 hour. */
1039
+ validityMs?: number;
1040
+ }
1041
+ /**
1042
+ * Generate and sign a Sovereignty Health Report.
1043
+ *
1044
+ * @param identityId - Which identity to sign with (defaults to primary)
1045
+ * @param opts - Generator dependencies
1046
+ * @returns The signed SHR, or an error string
1047
+ */
1048
+ declare function generateSHR(identityId: string | undefined, opts: SHRGeneratorOptions): SignedSHR | string;
1049
+
1050
+ /**
1051
+ * Sanctuary MCP Server — SHR Verifier
1052
+ *
1053
+ * Verifies a counterparty's Sovereignty Health Report:
1054
+ * - Signature validity (Ed25519 over canonical body)
1055
+ * - Temporal validity (not expired)
1056
+ * - Schema completeness
1057
+ * - Sovereignty level assessment
1058
+ */
1059
+
1060
+ /**
1061
+ * Verify a signed SHR.
1062
+ *
1063
+ * @param shr - The signed SHR to verify
1064
+ * @param now - Optional override for current time (for testing)
1065
+ * @returns Verification result with validity, errors, warnings, and sovereignty assessment
1066
+ */
1067
+ declare function verifySHR(shr: SignedSHR, now?: Date): SHRVerificationResult;
1068
+
1069
+ /**
1070
+ * Sanctuary MCP Server — Sovereignty Handshake Types
1071
+ *
1072
+ * The sovereignty handshake is a mutual verification protocol between
1073
+ * two Sanctuary instances. Each party presents its SHR and proves
1074
+ * liveness via nonce challenge-response.
1075
+ *
1076
+ * Protocol:
1077
+ * A → B: HandshakeChallenge (A's SHR + nonce)
1078
+ * B → A: HandshakeResponse (B's SHR + B's nonce + signature over A's nonce)
1079
+ * A → B: HandshakeCompletion (signature over B's nonce)
1080
+ * Result: Both hold a HandshakeResult with verified counterparty status
1081
+ */
1082
+
1083
+ /** Trust tier derived from sovereignty handshake */
1084
+ type TrustTier = "verified-sovereign" | "verified-degraded" | "unverified";
1085
+ /** Sovereignty level from SHR assessment */
1086
+ type SovereigntyLevel = "full" | "degraded" | "minimal" | "unverified";
1087
+ /**
1088
+ * Step 1: Initiator sends challenge
1089
+ */
1090
+ interface HandshakeChallenge {
1091
+ protocol_version: "1.0";
1092
+ shr: SignedSHR;
1093
+ nonce: string;
1094
+ initiated_at: string;
1095
+ }
1096
+ /**
1097
+ * Step 2: Responder sends response
1098
+ */
1099
+ interface HandshakeResponse {
1100
+ protocol_version: "1.0";
1101
+ shr: SignedSHR;
1102
+ responder_nonce: string;
1103
+ initiator_nonce_signature: string;
1104
+ responded_at: string;
1105
+ }
1106
+ /**
1107
+ * Step 3: Initiator sends completion
1108
+ */
1109
+ interface HandshakeCompletion {
1110
+ protocol_version: "1.0";
1111
+ responder_nonce_signature: string;
1112
+ completed_at: string;
1113
+ }
1114
+ /**
1115
+ * Final result: both parties hold this after a successful handshake
1116
+ */
1117
+ interface HandshakeResult {
1118
+ counterparty_id: string;
1119
+ counterparty_shr: SignedSHR;
1120
+ verified: boolean;
1121
+ sovereignty_level: SovereigntyLevel;
1122
+ trust_tier: TrustTier;
1123
+ completed_at: string;
1124
+ expires_at: string;
1125
+ errors: string[];
1126
+ }
1127
+ /**
1128
+ * In-progress handshake state (stored on initiator side)
1129
+ */
1130
+ interface HandshakeSession {
1131
+ session_id: string;
1132
+ role: "initiator" | "responder";
1133
+ state: "initiated" | "responded" | "completed" | "failed";
1134
+ our_nonce: string;
1135
+ their_nonce?: string;
1136
+ our_shr: SignedSHR;
1137
+ their_shr?: SignedSHR;
1138
+ initiated_at: string;
1139
+ result?: HandshakeResult;
1140
+ }
1141
+
1142
+ /**
1143
+ * Sanctuary MCP Server — Sovereignty Handshake Protocol
1144
+ *
1145
+ * Core handshake logic: initiate, respond, complete.
1146
+ * Nonce-based challenge-response prevents replay attacks.
1147
+ * SHR signatures are verified at each step.
1148
+ */
1149
+
1150
+ /**
1151
+ * Step 1: Initiate a handshake.
1152
+ * Generates a challenge containing our SHR and a nonce.
1153
+ */
1154
+ declare function initiateHandshake(ourSHR: SignedSHR): {
1155
+ challenge: HandshakeChallenge;
1156
+ session: HandshakeSession;
1157
+ };
1158
+ /**
1159
+ * Step 2: Respond to a handshake challenge.
1160
+ * Verifies the initiator's SHR, signs their nonce, generates our nonce.
1161
+ */
1162
+ declare function respondToHandshake(challenge: HandshakeChallenge, ourSHR: SignedSHR, identityManager: IdentityManager, masterKey: Uint8Array, identityId?: string): {
1163
+ response: HandshakeResponse;
1164
+ session: HandshakeSession;
1165
+ } | {
1166
+ error: string;
1167
+ };
1168
+ /**
1169
+ * Step 3: Complete the handshake (initiator side).
1170
+ * Verifies the responder's SHR and nonce signature, signs responder's nonce.
1171
+ */
1172
+ declare function completeHandshake(response: HandshakeResponse, session: HandshakeSession, identityManager: IdentityManager, masterKey: Uint8Array, identityId?: string): {
1173
+ completion: HandshakeCompletion;
1174
+ result: HandshakeResult;
1175
+ } | {
1176
+ error: string;
1177
+ };
1178
+ /**
1179
+ * Step 4: Verify completion (responder side).
1180
+ * Verifies the initiator signed our nonce correctly.
1181
+ */
1182
+ declare function verifyCompletion(completion: HandshakeCompletion, session: HandshakeSession): HandshakeResult;
1183
+
1184
+ /**
1185
+ * Sanctuary MCP Server — Main Entry Point
1186
+ *
1187
+ * Initializes and exports the Sanctuary MCP server.
1188
+ * Wires together: config → storage → crypto core → L1-L4 tools → MCP server
1189
+ */
1190
+
1191
+ interface SanctuaryServer {
1192
+ server: Server;
1193
+ config: SanctuaryConfig;
1194
+ }
1195
+ /**
1196
+ * Initialize the Sanctuary MCP Server.
1197
+ *
1198
+ * @param options - Configuration overrides and initialization options
1199
+ * @returns The configured MCP server, ready to connect to a transport
1200
+ */
1201
+ declare function createSanctuaryServer(options?: {
1202
+ configPath?: string;
1203
+ passphrase?: string;
1204
+ storage?: StorageBackend;
1205
+ }): Promise<SanctuaryServer>;
1206
+
1207
+ export { ApprovalGate, AuditLog, AutoApproveChannel, BaselineTracker, CallbackApprovalChannel, CommitmentStore, FilesystemStorage, type GateResult, type HandshakeChallenge, type HandshakeCompletion, type HandshakeResponse, type HandshakeResult, MemoryStorage, PolicyStore, type PrincipalPolicy, ReputationStore, type SHRBody, type SHRVerificationResult, type SanctuaryConfig, type SanctuaryServer, type SignedSHR, StateStore, StderrApprovalChannel, completeHandshake, createSanctuaryServer, generateSHR, initiateHandshake, loadConfig, loadPrincipalPolicy, respondToHandshake, verifyCompletion, verifySHR };