@sanctuary-framework/mcp-server 0.3.0 → 0.4.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/dist/index.d.ts CHANGED
@@ -255,9 +255,11 @@ declare class StateStore {
255
255
  /**
256
256
  * Import a previously exported state bundle.
257
257
  */
258
- import(bundleBase64: string, conflictResolution?: "skip" | "overwrite" | "version"): Promise<{
258
+ import(bundleBase64: string, conflictResolution: "skip" | "overwrite" | "version" | undefined, publicKeyResolver: (kid: string) => Uint8Array | null): Promise<{
259
259
  imported_keys: number;
260
260
  skipped_keys: number;
261
+ skipped_invalid_sig: number;
262
+ skipped_unknown_kid: number;
261
263
  conflicts: number;
262
264
  namespaces: string[];
263
265
  imported_at: string;
@@ -1114,6 +1116,252 @@ declare class FederationRegistry {
1114
1116
  getHandshakeResults(): Map<string, HandshakeResult>;
1115
1117
  }
1116
1118
 
1119
+ /**
1120
+ * Sanctuary MCP Server — L2 Operational Isolation: Context Gating
1121
+ *
1122
+ * Context gating controls what information leaves the sovereignty boundary
1123
+ * when an agent makes outbound calls — especially inference calls to remote
1124
+ * LLM providers. This is the "minimum-necessary context" enforcement layer.
1125
+ *
1126
+ * The problem: When an agent sends a request to a remote LLM provider (Claude,
1127
+ * GPT, etc.), most harnesses send the agent's full context — conversation
1128
+ * history, memory, tool results, preferences, internal reasoning. The agent
1129
+ * has no control over what the provider sees.
1130
+ *
1131
+ * Context gating lets the agent define:
1132
+ * - Provider categories (inference, tool-api, logging, analytics, etc.)
1133
+ * - What fields/categories of context may flow to each provider type
1134
+ * - What must always be redacted (secrets, internal reasoning, PII, etc.)
1135
+ * - What requires transformation (hashing, summarizing, anonymizing)
1136
+ *
1137
+ * This sits in L2 (Operational Isolation) because it controls information
1138
+ * flow at the execution boundary. L3 (Selective Disclosure) handles agent-
1139
+ * to-agent trust negotiation with cryptographic proofs; context gating
1140
+ * handles agent-to-infrastructure information flow.
1141
+ *
1142
+ * Security invariants:
1143
+ * - Redact rules take absolute priority (like withhold in L3)
1144
+ * - Policies are stored encrypted under L1 sovereignty
1145
+ * - Every filter operation is audit-logged with a content hash
1146
+ * (what was sent, what was redacted — without storing the content itself)
1147
+ * - Default policy: redact everything not explicitly allowed
1148
+ */
1149
+
1150
+ /** Provider categories that context may flow to */
1151
+ type ProviderCategory = "inference" | "tool-api" | "logging" | "analytics" | "peer-agent" | "custom";
1152
+ /** Actions that can be taken on a context field */
1153
+ type ContextAction = "allow" | "redact" | "hash" | "summarize" | "deny";
1154
+ /** A rule within a context-gating policy */
1155
+ interface ContextGateRule {
1156
+ /** Provider category this rule applies to */
1157
+ provider: ProviderCategory | "*";
1158
+ /** Fields/patterns that may pass through */
1159
+ allow: string[];
1160
+ /** Fields/patterns that must be redacted (highest priority) */
1161
+ redact: string[];
1162
+ /** Fields/patterns that should be hashed */
1163
+ hash: string[];
1164
+ /** Fields/patterns that should be summarized (advisory) */
1165
+ summarize: string[];
1166
+ }
1167
+ /** A complete context-gating policy */
1168
+ interface ContextGatePolicy {
1169
+ policy_id: string;
1170
+ policy_name: string;
1171
+ rules: ContextGateRule[];
1172
+ /** Default action when no rule matches a field */
1173
+ default_action: "redact" | "deny";
1174
+ /** Identity this policy is bound to (optional) */
1175
+ identity_id?: string;
1176
+ created_at: string;
1177
+ updated_at: string;
1178
+ }
1179
+ /** Result of filtering a single field */
1180
+ interface FieldFilterResult {
1181
+ field: string;
1182
+ action: ContextAction;
1183
+ reason: string;
1184
+ /** If action is "hash", contains the hash */
1185
+ hash_value?: string;
1186
+ }
1187
+ /** Result of a full context filter operation */
1188
+ interface ContextFilterResult {
1189
+ policy_id: string;
1190
+ provider: ProviderCategory | string;
1191
+ fields_allowed: number;
1192
+ fields_redacted: number;
1193
+ fields_hashed: number;
1194
+ fields_summarized: number;
1195
+ fields_denied: number;
1196
+ decisions: FieldFilterResult[];
1197
+ /** SHA-256 hash of the original context (for audit trail) */
1198
+ original_context_hash: string;
1199
+ /** SHA-256 hash of the filtered output (for audit trail) */
1200
+ filtered_context_hash: string;
1201
+ filtered_at: string;
1202
+ }
1203
+ /**
1204
+ * Evaluate a context field against a policy for a given provider.
1205
+ *
1206
+ * Priority order (same as L3 disclosure):
1207
+ * 1. Redact (blocks — highest priority)
1208
+ * 2. Deny (blocks entire request)
1209
+ * 3. Hash (transforms)
1210
+ * 4. Summarize (advisory transform)
1211
+ * 5. Allow (passes through)
1212
+ * 6. Default action
1213
+ */
1214
+ declare function evaluateField(policy: ContextGatePolicy, provider: ProviderCategory | string, field: string): FieldFilterResult;
1215
+ /**
1216
+ * Filter a full context object against a policy for a given provider.
1217
+ * Returns per-field decisions and content hashes for the audit trail.
1218
+ */
1219
+ declare function filterContext(policy: ContextGatePolicy, provider: ProviderCategory | string, context: Record<string, unknown>): ContextFilterResult;
1220
+ /**
1221
+ * Context gate policy store — encrypted under L1 sovereignty.
1222
+ */
1223
+ declare class ContextGatePolicyStore {
1224
+ private storage;
1225
+ private encryptionKey;
1226
+ private policies;
1227
+ constructor(storage: StorageBackend, masterKey: Uint8Array);
1228
+ /**
1229
+ * Create and store a new context-gating policy.
1230
+ */
1231
+ create(policyName: string, rules: ContextGateRule[], defaultAction: "redact" | "deny", identityId?: string): Promise<ContextGatePolicy>;
1232
+ /**
1233
+ * Get a policy by ID.
1234
+ */
1235
+ get(policyId: string): Promise<ContextGatePolicy | null>;
1236
+ /**
1237
+ * List all context-gating policies.
1238
+ */
1239
+ list(): Promise<ContextGatePolicy[]>;
1240
+ /**
1241
+ * Load all persisted policies into memory.
1242
+ */
1243
+ private loadAll;
1244
+ private persist;
1245
+ }
1246
+
1247
+ /**
1248
+ * Sanctuary MCP Server — L2 Context Gating: Starter Policy Templates
1249
+ *
1250
+ * Pre-built policies for common use cases. These are starting points —
1251
+ * users should customize them for their specific context structure.
1252
+ *
1253
+ * Templates:
1254
+ *
1255
+ * inference-minimal
1256
+ * Only the current task and query reach the LLM. Everything else
1257
+ * is redacted. Secrets, PII, memory, reasoning, and history are
1258
+ * all blocked. IDs are hashed. Maximum privacy, minimum context.
1259
+ *
1260
+ * inference-standard
1261
+ * Task, query, and tool results pass through. Conversation history
1262
+ * is flagged for summarization (compress before sending). Secrets,
1263
+ * PII, and internal reasoning are redacted. IDs are hashed.
1264
+ * Balanced: the LLM has enough context to be useful without seeing
1265
+ * everything the agent knows.
1266
+ *
1267
+ * logging-strict
1268
+ * Redacts everything for logging/analytics providers. Only
1269
+ * operation names and timestamps pass through. Use this for
1270
+ * telemetry services where you want usage metrics without
1271
+ * content exposure.
1272
+ *
1273
+ * tool-api-scoped
1274
+ * Allows tool-specific parameters and the current task, redacts
1275
+ * memory, history, secrets, and PII. Hashes IDs. For outbound
1276
+ * calls to external APIs (search, database, etc.) where you need
1277
+ * to send query parameters but not your agent's full state.
1278
+ */
1279
+
1280
+ /** A template definition ready to be applied via the policy store */
1281
+ interface ContextGateTemplate {
1282
+ /** Machine-readable template ID */
1283
+ id: string;
1284
+ /** Human-readable name */
1285
+ name: string;
1286
+ /** One-line description */
1287
+ description: string;
1288
+ /** When to use this template */
1289
+ use_when: string;
1290
+ /** The rules that make up this template */
1291
+ rules: ContextGateRule[];
1292
+ /** Default action for unmatched fields */
1293
+ default_action: "redact" | "deny";
1294
+ }
1295
+ /** All available templates, keyed by ID */
1296
+ declare const TEMPLATES: Record<string, ContextGateTemplate>;
1297
+ /** List all available template IDs */
1298
+ declare function listTemplateIds(): string[];
1299
+ /** Get a template by ID (returns undefined if not found) */
1300
+ declare function getTemplate(id: string): ContextGateTemplate | undefined;
1301
+
1302
+ /**
1303
+ * Sanctuary MCP Server — L2 Context Gating: Policy Recommendation Engine
1304
+ *
1305
+ * Analyzes a sample context object and recommends a context-gating policy
1306
+ * based on field name heuristics. The agent (or human) can then review,
1307
+ * adjust, and apply the recommendation.
1308
+ *
1309
+ * This is deliberately conservative: when in doubt, it recommends redact.
1310
+ * A false redaction is a usability issue; a false allow is a privacy leak.
1311
+ *
1312
+ * Classification heuristics:
1313
+ * - Known secret patterns → redact (highest confidence)
1314
+ * - Known PII patterns → redact (high confidence)
1315
+ * - Known internal state patterns → redact (high confidence)
1316
+ * - Known ID patterns → hash (medium confidence)
1317
+ * - Known history patterns → summarize (medium confidence)
1318
+ * - Known task/query patterns → allow (medium confidence)
1319
+ * - Everything else → redact (conservative default)
1320
+ *
1321
+ * WARNING: Fields like 'tool_results' and 'tool_output' are classified as
1322
+ * "allow" (medium confidence) but may contain sensitive data from external
1323
+ * API responses, including auth tokens, user data, or PII. Always review
1324
+ * recommendations before applying — the heuristic classifies by field NAME,
1325
+ * not field CONTENT.
1326
+ */
1327
+ /** Classification result for a single field */
1328
+ interface FieldClassification {
1329
+ field: string;
1330
+ recommended_action: "allow" | "redact" | "hash" | "summarize";
1331
+ reason: string;
1332
+ confidence: "high" | "medium" | "low";
1333
+ /** Pattern that matched, if any */
1334
+ matched_pattern: string | null;
1335
+ }
1336
+ /** Full recommendation result */
1337
+ interface PolicyRecommendation {
1338
+ provider: string;
1339
+ classifications: FieldClassification[];
1340
+ recommended_rules: {
1341
+ allow: string[];
1342
+ redact: string[];
1343
+ hash: string[];
1344
+ summarize: string[];
1345
+ };
1346
+ default_action: "redact";
1347
+ summary: {
1348
+ total_fields: number;
1349
+ allow: number;
1350
+ redact: number;
1351
+ hash: number;
1352
+ summarize: number;
1353
+ };
1354
+ warnings: string[];
1355
+ }
1356
+ /**
1357
+ * Classify a single field name and return a recommendation.
1358
+ */
1359
+ declare function classifyField(fieldName: string): FieldClassification;
1360
+ /**
1361
+ * Analyze a full context object and recommend a policy.
1362
+ */
1363
+ declare function recommendPolicy(context: Record<string, unknown>, provider?: string): PolicyRecommendation;
1364
+
1117
1365
  /**
1118
1366
  * Sanctuary MCP Server — In-Memory Storage Backend
1119
1367
  *
@@ -1187,7 +1435,13 @@ interface Tier2Config {
1187
1435
  interface ApprovalChannelConfig {
1188
1436
  type: "stderr" | "webhook" | "callback";
1189
1437
  timeout_seconds: number;
1190
- auto_deny: boolean;
1438
+ /**
1439
+ * SEC-002: auto_deny is hardcoded to true and not configurable.
1440
+ * Timeout on any approval channel ALWAYS results in denial.
1441
+ * This field is retained for backward compatibility with existing
1442
+ * policy files but is ignored — timeout always denies.
1443
+ */
1444
+ auto_deny?: boolean;
1191
1445
  webhook_url?: string;
1192
1446
  webhook_secret?: string;
1193
1447
  }
@@ -1215,7 +1469,7 @@ interface ApprovalRequest {
1215
1469
  interface ApprovalResponse {
1216
1470
  decision: "approve" | "deny";
1217
1471
  decided_at: string;
1218
- decided_by: "human" | "timeout" | "auto";
1472
+ decided_by: "human" | "timeout" | "auto" | "stderr:non-interactive";
1219
1473
  }
1220
1474
  /** Result of the approval gate evaluation */
1221
1475
  interface GateResult {
@@ -1258,22 +1512,25 @@ interface ApprovalChannel {
1258
1512
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1259
1513
  }
1260
1514
  /**
1261
- * Stderr approval channel — writes prompts to stderr, waits for response.
1515
+ * Stderr approval channel — non-interactive informational channel.
1262
1516
  *
1263
1517
  * In the MCP stdio model:
1264
1518
  * - stdin/stdout carry the MCP protocol (JSON-RPC)
1265
1519
  * - stderr is available for out-of-band human communication
1266
1520
  *
1267
- * Since many harnesses do not support interactive stdin during tool calls,
1268
- * this channel uses a timeout-based model: the prompt is displayed, and
1269
- * if no response is received within the timeout, the default action applies.
1521
+ * Because stdin is consumed by the MCP JSON-RPC transport, this channel
1522
+ * CANNOT read interactive human input. It is strictly informational:
1523
+ * the prompt is displayed so the human sees what is happening, and the
1524
+ * operation is denied immediately.
1270
1525
  *
1271
- * For MVS, the channel auto-resolves based on the auto_deny setting.
1272
- * Interactive stdin reading is deferred to a future version with harness support.
1526
+ * SEC-002 + SEC-016 invariants:
1527
+ * - This channel ALWAYS denies. No configuration can change this.
1528
+ * - There is no timeout or async delay — denial is synchronous.
1529
+ * - The `auto_deny` config field is ignored (SEC-002).
1530
+ * - For interactive approval, use the dashboard or webhook channel.
1273
1531
  */
1274
1532
  declare class StderrApprovalChannel implements ApprovalChannel {
1275
- private config;
1276
- constructor(config: ApprovalChannelConfig);
1533
+ constructor(_config: ApprovalChannelConfig);
1277
1534
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1278
1535
  private formatPrompt;
1279
1536
  }
@@ -1458,7 +1715,8 @@ interface DashboardConfig {
1458
1715
  port: number;
1459
1716
  host: string;
1460
1717
  timeout_seconds: number;
1461
- auto_deny: boolean;
1718
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1719
+ auto_deny?: boolean;
1462
1720
  /** Bearer token for API authentication. If omitted, auth is disabled. */
1463
1721
  auth_token?: string;
1464
1722
  /** TLS configuration for HTTPS. If omitted, plain HTTP is used. */
@@ -1478,6 +1736,11 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1478
1736
  private dashboardHTML;
1479
1737
  private authToken;
1480
1738
  private useTLS;
1739
+ /** SEC-012: Short-lived session store. Sessions replace URL query tokens. */
1740
+ private sessions;
1741
+ private sessionCleanupTimer;
1742
+ /** Rate limiting: per-IP request tracking */
1743
+ private rateLimits;
1481
1744
  constructor(config: DashboardConfig);
1482
1745
  /**
1483
1746
  * Inject dependencies after construction.
@@ -1503,11 +1766,52 @@ declare class DashboardApprovalChannel implements ApprovalChannel {
1503
1766
  requestApproval(request: ApprovalRequest): Promise<ApprovalResponse>;
1504
1767
  /**
1505
1768
  * Verify bearer token authentication.
1506
- * Checks Authorization header first, falls back to ?token= query param.
1769
+ *
1770
+ * SEC-012: The long-lived auth token is ONLY accepted via the Authorization
1771
+ * header — never in URL query strings. For SSE and page loads that cannot
1772
+ * set headers, a short-lived session token (obtained via POST /auth/session)
1773
+ * is accepted via ?session= query parameter.
1774
+ *
1507
1775
  * Returns true if auth passes, false if blocked (response already sent).
1508
1776
  */
1509
1777
  private checkAuth;
1778
+ /**
1779
+ * Create a short-lived session by exchanging the long-lived auth token
1780
+ * (provided in the Authorization header) for a session ID.
1781
+ */
1782
+ private createSession;
1783
+ /**
1784
+ * Validate a session ID — must exist and not be expired.
1785
+ */
1786
+ private validateSession;
1787
+ /**
1788
+ * Remove all expired sessions.
1789
+ */
1790
+ private cleanupSessions;
1791
+ /**
1792
+ * Get the remote address from a request, normalizing IPv6-mapped IPv4.
1793
+ */
1794
+ private getRemoteAddr;
1795
+ /**
1796
+ * Check rate limit for a request. Returns true if allowed, false if rate-limited.
1797
+ * When rate-limited, sends a 429 response.
1798
+ */
1799
+ private checkRateLimit;
1800
+ /**
1801
+ * Remove stale entries from the rate limit map.
1802
+ */
1803
+ private pruneRateLimits;
1510
1804
  private handleRequest;
1805
+ /**
1806
+ * SEC-012: Exchange a long-lived auth token (in Authorization header)
1807
+ * for a short-lived session ID. The session ID can be used in URL
1808
+ * query parameters without exposing the long-lived credential.
1809
+ *
1810
+ * This endpoint performs its OWN auth check (header-only) because it
1811
+ * must reject query-parameter tokens and is called before the
1812
+ * normal checkAuth flow.
1813
+ */
1814
+ private handleSessionExchange;
1511
1815
  private serveDashboard;
1512
1816
  private handleSSE;
1513
1817
  private handleStatus;
@@ -1568,8 +1872,8 @@ interface WebhookConfig {
1568
1872
  callback_host: string;
1569
1873
  /** Seconds to wait for a callback before timeout */
1570
1874
  timeout_seconds: number;
1571
- /** Whether to deny (true) or approve (false) on timeout */
1572
- auto_deny: boolean;
1875
+ /** SEC-002: auto_deny is always true. Field retained for interface compat but ignored. */
1876
+ auto_deny?: boolean;
1573
1877
  }
1574
1878
  /** Outbound webhook payload */
1575
1879
  interface WebhookPayload {
@@ -1937,4 +2241,4 @@ declare function createSanctuaryServer(options?: {
1937
2241
  storage?: StorageBackend;
1938
2242
  }): Promise<SanctuaryServer>;
1939
2243
 
1940
- export { ApprovalGate, AuditLog, AutoApproveChannel, BaselineTracker, type BridgeAttestationRequest, type BridgeAttestationResult, type BridgeCommitment, type BridgeVerificationResult, CallbackApprovalChannel, CommitmentStore, type ConcordiaOutcome, DashboardApprovalChannel, type DashboardConfig, type FederationCapabilities, type FederationPeer, FederationRegistry, FilesystemStorage, type GateResult, type HandshakeChallenge, type HandshakeCompletion, type HandshakeResponse, type HandshakeResult, MemoryStorage, type PedersenCommitment, type PeerTrustEvaluation, PolicyStore, type PrincipalPolicy, ReputationStore, type SHRBody, type SHRVerificationResult, type SanctuaryConfig, type SanctuaryServer, type SignedSHR, type SovereigntyTier, StateStore, StderrApprovalChannel, TIER_WEIGHTS, type TierMetadata, type TieredAttestation, WebhookApprovalChannel, type WebhookCallbackPayload, type WebhookConfig, type WebhookPayload, type ZKProofOfKnowledge, type ZKRangeProof, canonicalize, completeHandshake, computeWeightedScore, createBridgeCommitment, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, generateSHR, initiateHandshake, loadConfig, loadPrincipalPolicy, resolveTier, respondToHandshake, signPayload, tierDistribution, verifyBridgeCommitment, verifyCompletion, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };
2244
+ export { ApprovalGate, AuditLog, AutoApproveChannel, BaselineTracker, type BridgeAttestationRequest, type BridgeAttestationResult, type BridgeCommitment, type BridgeVerificationResult, TEMPLATES as CONTEXT_GATE_TEMPLATES, CallbackApprovalChannel, CommitmentStore, type ConcordiaOutcome, type ContextAction, type ContextFilterResult, type ContextGatePolicy, ContextGatePolicyStore, type ContextGateRule, type ContextGateTemplate, DashboardApprovalChannel, type DashboardConfig, type FederationCapabilities, type FederationPeer, FederationRegistry, type FieldClassification, type FieldFilterResult, FilesystemStorage, type GateResult, type HandshakeChallenge, type HandshakeCompletion, type HandshakeResponse, type HandshakeResult, MemoryStorage, type PedersenCommitment, type PeerTrustEvaluation, type PolicyRecommendation, PolicyStore, type PrincipalPolicy, type ProviderCategory, ReputationStore, type SHRBody, type SHRVerificationResult, type SanctuaryConfig, type SanctuaryServer, type SignedSHR, type SovereigntyTier, StateStore, StderrApprovalChannel, TIER_WEIGHTS, type TierMetadata, type TieredAttestation, WebhookApprovalChannel, type WebhookCallbackPayload, type WebhookConfig, type WebhookPayload, type ZKProofOfKnowledge, type ZKRangeProof, canonicalize, classifyField, completeHandshake, computeWeightedScore, createBridgeCommitment, createPedersenCommitment, createProofOfKnowledge, createRangeProof, createSanctuaryServer, evaluateField, filterContext, generateSHR, getTemplate, initiateHandshake, listTemplateIds, loadConfig, loadPrincipalPolicy, recommendPolicy, resolveTier, respondToHandshake, signPayload, tierDistribution, verifyBridgeCommitment, verifyCompletion, verifyPedersenCommitment, verifyProofOfKnowledge, verifyRangeProof, verifySHR, verifySignature };