@sanctuary-framework/mcp-server 0.6.1 → 0.7.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/cli.cjs +1733 -117
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1734 -118
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1278 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -7
- package/dist/index.d.ts +42 -7
- package/dist/index.js +1278 -117
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1408,7 +1408,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1408
1408
|
const tools = [
|
|
1409
1409
|
// ── Identity Tools ──────────────────────────────────────────────────
|
|
1410
1410
|
{
|
|
1411
|
-
name: "
|
|
1411
|
+
name: "identity_create",
|
|
1412
1412
|
description: "Create a new sovereign identity (Ed25519 keypair). The private key is encrypted and never exposed.",
|
|
1413
1413
|
inputSchema: {
|
|
1414
1414
|
type: "object",
|
|
@@ -1443,7 +1443,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1443
1443
|
}
|
|
1444
1444
|
},
|
|
1445
1445
|
{
|
|
1446
|
-
name: "
|
|
1446
|
+
name: "identity_list",
|
|
1447
1447
|
description: "List all managed sovereign identities.",
|
|
1448
1448
|
inputSchema: {
|
|
1449
1449
|
type: "object",
|
|
@@ -1468,7 +1468,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1468
1468
|
}
|
|
1469
1469
|
},
|
|
1470
1470
|
{
|
|
1471
|
-
name: "
|
|
1471
|
+
name: "identity_sign",
|
|
1472
1472
|
description: "Sign data with a managed identity. The private key is decrypted in memory only during signing.",
|
|
1473
1473
|
inputSchema: {
|
|
1474
1474
|
type: "object",
|
|
@@ -1506,7 +1506,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1506
1506
|
}
|
|
1507
1507
|
},
|
|
1508
1508
|
{
|
|
1509
|
-
name: "
|
|
1509
|
+
name: "identity_verify",
|
|
1510
1510
|
description: "Verify an Ed25519 signature. Provide either identity_id or public_key.",
|
|
1511
1511
|
inputSchema: {
|
|
1512
1512
|
type: "object",
|
|
@@ -1555,7 +1555,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1555
1555
|
}
|
|
1556
1556
|
},
|
|
1557
1557
|
{
|
|
1558
|
-
name: "
|
|
1558
|
+
name: "identity_rotate",
|
|
1559
1559
|
description: "Rotate keys for an identity. Generates a new keypair and signs a rotation event with the old key for verifiable chain.",
|
|
1560
1560
|
inputSchema: {
|
|
1561
1561
|
type: "object",
|
|
@@ -1588,7 +1588,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1588
1588
|
},
|
|
1589
1589
|
// ── State Tools ─────────────────────────────────────────────────────
|
|
1590
1590
|
{
|
|
1591
|
-
name: "
|
|
1591
|
+
name: "state_write",
|
|
1592
1592
|
description: "Write encrypted state to the sovereign store. Value is encrypted with a namespace-specific key. The write is signed by the active identity.",
|
|
1593
1593
|
inputSchema: {
|
|
1594
1594
|
type: "object",
|
|
@@ -1645,7 +1645,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1645
1645
|
}
|
|
1646
1646
|
},
|
|
1647
1647
|
{
|
|
1648
|
-
name: "
|
|
1648
|
+
name: "state_read",
|
|
1649
1649
|
description: "Read and decrypt state from the sovereign store. Verifies integrity via Merkle proof and signature.",
|
|
1650
1650
|
inputSchema: {
|
|
1651
1651
|
type: "object",
|
|
@@ -1686,7 +1686,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1686
1686
|
}
|
|
1687
1687
|
},
|
|
1688
1688
|
{
|
|
1689
|
-
name: "
|
|
1689
|
+
name: "state_list",
|
|
1690
1690
|
description: "List keys in a namespace (metadata only \u2014 no decryption).",
|
|
1691
1691
|
inputSchema: {
|
|
1692
1692
|
type: "object",
|
|
@@ -1718,7 +1718,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1718
1718
|
}
|
|
1719
1719
|
},
|
|
1720
1720
|
{
|
|
1721
|
-
name: "
|
|
1721
|
+
name: "state_delete",
|
|
1722
1722
|
description: "Securely delete state. Overwrites file with random bytes before removal (right to deletion, S1.6).",
|
|
1723
1723
|
inputSchema: {
|
|
1724
1724
|
type: "object",
|
|
@@ -1750,7 +1750,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1750
1750
|
}
|
|
1751
1751
|
},
|
|
1752
1752
|
{
|
|
1753
|
-
name: "
|
|
1753
|
+
name: "state_export",
|
|
1754
1754
|
description: "Export state as an encrypted, portable bundle for migration.",
|
|
1755
1755
|
inputSchema: {
|
|
1756
1756
|
type: "object",
|
|
@@ -1770,7 +1770,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1770
1770
|
}
|
|
1771
1771
|
},
|
|
1772
1772
|
{
|
|
1773
|
-
name: "
|
|
1773
|
+
name: "state_import",
|
|
1774
1774
|
description: "Import a previously exported state bundle.",
|
|
1775
1775
|
inputSchema: {
|
|
1776
1776
|
type: "object",
|
|
@@ -2391,7 +2391,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2391
2391
|
const tools = [
|
|
2392
2392
|
// ─── Commitment Schemes ───────────────────────────────────────────────
|
|
2393
2393
|
{
|
|
2394
|
-
name: "
|
|
2394
|
+
name: "proof_commitment",
|
|
2395
2395
|
description: "Create a cryptographic commitment to a value. The commitment hides the value until you choose to reveal it. Returns the commitment hash and a blinding factor (store securely).",
|
|
2396
2396
|
inputSchema: {
|
|
2397
2397
|
type: "object",
|
|
@@ -2426,7 +2426,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2426
2426
|
}
|
|
2427
2427
|
},
|
|
2428
2428
|
{
|
|
2429
|
-
name: "
|
|
2429
|
+
name: "proof_reveal",
|
|
2430
2430
|
description: "Verify a previously committed value by revealing it with the blinding factor. Returns whether the revealed value matches the commitment.",
|
|
2431
2431
|
inputSchema: {
|
|
2432
2432
|
type: "object",
|
|
@@ -2464,7 +2464,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2464
2464
|
},
|
|
2465
2465
|
// ─── Disclosure Policies ──────────────────────────────────────────────
|
|
2466
2466
|
{
|
|
2467
|
-
name: "
|
|
2467
|
+
name: "disclosure_set_policy",
|
|
2468
2468
|
description: "Define a disclosure policy that controls what an agent will and will not disclose in different interaction contexts. Rules specify which fields may be disclosed, which must be withheld, and which require cryptographic proof.",
|
|
2469
2469
|
inputSchema: {
|
|
2470
2470
|
type: "object",
|
|
@@ -2539,7 +2539,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2539
2539
|
}
|
|
2540
2540
|
},
|
|
2541
2541
|
{
|
|
2542
|
-
name: "
|
|
2542
|
+
name: "disclosure_evaluate",
|
|
2543
2543
|
description: "Evaluate a disclosure request against an active policy. Returns per-field decisions: disclose, withhold, proof, or ask-principal.",
|
|
2544
2544
|
inputSchema: {
|
|
2545
2545
|
type: "object",
|
|
@@ -2615,7 +2615,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2615
2615
|
},
|
|
2616
2616
|
// ─── ZK Proof Tools ───────────────────────────────────────────────────
|
|
2617
2617
|
{
|
|
2618
|
-
name: "
|
|
2618
|
+
name: "zk_commit",
|
|
2619
2619
|
description: "Create a Pedersen commitment to a numeric value on Ristretto255. Unlike SHA-256 commitments, Pedersen commitments support zero-knowledge proofs: you can prove properties about the committed value without revealing it.",
|
|
2620
2620
|
inputSchema: {
|
|
2621
2621
|
type: "object",
|
|
@@ -2646,7 +2646,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2646
2646
|
}
|
|
2647
2647
|
},
|
|
2648
2648
|
{
|
|
2649
|
-
name: "
|
|
2649
|
+
name: "zk_prove",
|
|
2650
2650
|
description: "Create a zero-knowledge proof of knowledge for a Pedersen commitment. Proves you know the value and blinding factor without revealing either. Uses a Schnorr sigma protocol with Fiat-Shamir transform.",
|
|
2651
2651
|
inputSchema: {
|
|
2652
2652
|
type: "object",
|
|
@@ -2687,7 +2687,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2687
2687
|
}
|
|
2688
2688
|
},
|
|
2689
2689
|
{
|
|
2690
|
-
name: "
|
|
2690
|
+
name: "zk_verify",
|
|
2691
2691
|
description: "Verify a zero-knowledge proof of knowledge for a Pedersen commitment. Checks that the prover knows the commitment's opening without learning anything.",
|
|
2692
2692
|
inputSchema: {
|
|
2693
2693
|
type: "object",
|
|
@@ -2715,7 +2715,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2715
2715
|
}
|
|
2716
2716
|
},
|
|
2717
2717
|
{
|
|
2718
|
-
name: "
|
|
2718
|
+
name: "zk_range_prove",
|
|
2719
2719
|
description: "Create a zero-knowledge range proof: prove that a committed value is within [min, max] without revealing the exact value. Uses bit-decomposition with OR-proofs on Ristretto255.",
|
|
2720
2720
|
inputSchema: {
|
|
2721
2721
|
type: "object",
|
|
@@ -2765,7 +2765,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2765
2765
|
}
|
|
2766
2766
|
},
|
|
2767
2767
|
{
|
|
2768
|
-
name: "
|
|
2768
|
+
name: "zk_range_verify",
|
|
2769
2769
|
description: "Verify a zero-knowledge range proof \u2014 confirms a committed value is within the claimed range without learning the value.",
|
|
2770
2770
|
inputSchema: {
|
|
2771
2771
|
type: "object",
|
|
@@ -3213,7 +3213,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3213
3213
|
const tools = [
|
|
3214
3214
|
// ─── Reputation Recording ─────────────────────────────────────────
|
|
3215
3215
|
{
|
|
3216
|
-
name: "
|
|
3216
|
+
name: "reputation_record",
|
|
3217
3217
|
description: "Record an interaction outcome as a signed attestation. Creates an EAS-compatible attestation signed by the specified identity.",
|
|
3218
3218
|
inputSchema: {
|
|
3219
3219
|
type: "object",
|
|
@@ -3306,7 +3306,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3306
3306
|
},
|
|
3307
3307
|
// ─── Reputation Query ─────────────────────────────────────────────
|
|
3308
3308
|
{
|
|
3309
|
-
name: "
|
|
3309
|
+
name: "reputation_query",
|
|
3310
3310
|
description: "Query aggregated reputation data with filtering. Returns summary statistics, never raw interaction details.",
|
|
3311
3311
|
inputSchema: {
|
|
3312
3312
|
type: "object",
|
|
@@ -3354,7 +3354,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3354
3354
|
},
|
|
3355
3355
|
// ─── Reputation Export ─────────────────────────────────────────────
|
|
3356
3356
|
{
|
|
3357
|
-
name: "
|
|
3357
|
+
name: "reputation_export",
|
|
3358
3358
|
description: "Export a portable reputation bundle (SANCTUARY_REP_V1). Includes all signed attestations for independent verification.",
|
|
3359
3359
|
inputSchema: {
|
|
3360
3360
|
type: "object",
|
|
@@ -3413,7 +3413,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3413
3413
|
},
|
|
3414
3414
|
// ─── Reputation Import ────────────────────────────────────────────
|
|
3415
3415
|
{
|
|
3416
|
-
name: "
|
|
3416
|
+
name: "reputation_import",
|
|
3417
3417
|
description: "Import a reputation bundle from another Sanctuary instance. Verifies all attestation signatures by default.",
|
|
3418
3418
|
inputSchema: {
|
|
3419
3419
|
type: "object",
|
|
@@ -3465,7 +3465,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3465
3465
|
},
|
|
3466
3466
|
// ─── Sovereignty-Weighted Query ──────────────────────────────────
|
|
3467
3467
|
{
|
|
3468
|
-
name: "
|
|
3468
|
+
name: "reputation_query_weighted",
|
|
3469
3469
|
description: "Query reputation with sovereignty-weighted scoring. Attestations from verified-sovereign agents carry full weight (1.0); unverified attestations carry reduced weight (0.2). Returns both the weighted score and tier distribution.",
|
|
3470
3470
|
inputSchema: {
|
|
3471
3471
|
type: "object",
|
|
@@ -3521,7 +3521,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3521
3521
|
},
|
|
3522
3522
|
// ─── Trust Bootstrap: Escrow ──────────────────────────────────────
|
|
3523
3523
|
{
|
|
3524
|
-
name: "
|
|
3524
|
+
name: "bootstrap_create_escrow",
|
|
3525
3525
|
description: "Create an escrow record for trust bootstrapping. Allows new participants with no reputation to transact safely.",
|
|
3526
3526
|
inputSchema: {
|
|
3527
3527
|
type: "object",
|
|
@@ -3580,7 +3580,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3580
3580
|
},
|
|
3581
3581
|
// ─── Trust Bootstrap: Guarantee ───────────────────────────────────
|
|
3582
3582
|
{
|
|
3583
|
-
name: "
|
|
3583
|
+
name: "bootstrap_provide_guarantee",
|
|
3584
3584
|
description: "A principal provides a signed reputation guarantee for a new agent. The guarantee certificate can be presented to counterparties.",
|
|
3585
3585
|
inputSchema: {
|
|
3586
3586
|
type: "object",
|
|
@@ -3658,7 +3658,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3658
3658
|
},
|
|
3659
3659
|
// ─── Verascore Reputation Publish ────────────────────────────────
|
|
3660
3660
|
{
|
|
3661
|
-
name: "
|
|
3661
|
+
name: "reputation_publish",
|
|
3662
3662
|
description: "Publish sovereignty data to Verascore (verascore.ai) \u2014 the agent reputation platform. Sends SHR data, handshake attestations, or sovereignty updates. The data is signed with the agent's Ed25519 key for verification. Requires a Verascore agent profile (claimed or stub) to exist.",
|
|
3663
3663
|
inputSchema: {
|
|
3664
3664
|
type: "object",
|
|
@@ -4099,8 +4099,6 @@ tier3_always_allow:
|
|
|
4099
4099
|
- governor_status
|
|
4100
4100
|
- reputation_publish
|
|
4101
4101
|
- sanctuary_policy_status
|
|
4102
|
-
- sanctuary_link_to_human
|
|
4103
|
-
- sanctuary_sign_challenge
|
|
4104
4102
|
|
|
4105
4103
|
# \u2500\u2500\u2500 Approval Channel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
4106
4104
|
# How Sanctuary reaches you when approval is needed.
|
|
@@ -7366,13 +7364,810 @@ function generateDashboardHTML(options) {
|
|
|
7366
7364
|
</html>`;
|
|
7367
7365
|
}
|
|
7368
7366
|
|
|
7367
|
+
// src/cocoon/fortress-view.ts
|
|
7368
|
+
function generateFortressViewHTML(options) {
|
|
7369
|
+
return `<!DOCTYPE html>
|
|
7370
|
+
<html lang="en">
|
|
7371
|
+
<head>
|
|
7372
|
+
<meta charset="UTF-8">
|
|
7373
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7374
|
+
<title>Sanctuary \u2014 Fortress View</title>
|
|
7375
|
+
<style>
|
|
7376
|
+
:root {
|
|
7377
|
+
--bg: #0d1117;
|
|
7378
|
+
--surface: #161b22;
|
|
7379
|
+
--surface-raised: #1c2128;
|
|
7380
|
+
--border: #30363d;
|
|
7381
|
+
--text-primary: #e6edf3;
|
|
7382
|
+
--text-secondary: #8b949e;
|
|
7383
|
+
--text-muted: #484f58;
|
|
7384
|
+
--green: #3fb950;
|
|
7385
|
+
--green-dim: #238636;
|
|
7386
|
+
--amber: #d29922;
|
|
7387
|
+
--amber-dim: #9e6a03;
|
|
7388
|
+
--red: #f85149;
|
|
7389
|
+
--red-dim: #da3633;
|
|
7390
|
+
--blue: #58a6ff;
|
|
7391
|
+
--blue-dim: #1f6feb;
|
|
7392
|
+
}
|
|
7393
|
+
|
|
7394
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7395
|
+
|
|
7396
|
+
body {
|
|
7397
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
7398
|
+
background-color: var(--bg);
|
|
7399
|
+
color: var(--text-primary);
|
|
7400
|
+
min-height: 100vh;
|
|
7401
|
+
}
|
|
7402
|
+
|
|
7403
|
+
/* \u2500\u2500 Header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7404
|
+
.fortress-header {
|
|
7405
|
+
display: flex;
|
|
7406
|
+
align-items: center;
|
|
7407
|
+
justify-content: space-between;
|
|
7408
|
+
padding: 16px 24px;
|
|
7409
|
+
border-bottom: 1px solid var(--border);
|
|
7410
|
+
background: var(--surface);
|
|
7411
|
+
}
|
|
7412
|
+
|
|
7413
|
+
.fortress-brand {
|
|
7414
|
+
display: flex;
|
|
7415
|
+
align-items: center;
|
|
7416
|
+
gap: 12px;
|
|
7417
|
+
}
|
|
7418
|
+
|
|
7419
|
+
.fortress-brand .shield {
|
|
7420
|
+
font-size: 28px;
|
|
7421
|
+
color: var(--blue);
|
|
7422
|
+
}
|
|
7423
|
+
|
|
7424
|
+
.fortress-brand h1 {
|
|
7425
|
+
font-size: 18px;
|
|
7426
|
+
font-weight: 600;
|
|
7427
|
+
letter-spacing: -0.5px;
|
|
7428
|
+
}
|
|
7429
|
+
|
|
7430
|
+
.fortress-brand .version {
|
|
7431
|
+
font-size: 12px;
|
|
7432
|
+
color: var(--text-secondary);
|
|
7433
|
+
}
|
|
7434
|
+
|
|
7435
|
+
.header-actions {
|
|
7436
|
+
display: flex;
|
|
7437
|
+
gap: 8px;
|
|
7438
|
+
}
|
|
7439
|
+
|
|
7440
|
+
.header-actions button {
|
|
7441
|
+
padding: 6px 16px;
|
|
7442
|
+
border-radius: 6px;
|
|
7443
|
+
border: 1px solid var(--border);
|
|
7444
|
+
background: var(--surface);
|
|
7445
|
+
color: var(--text-primary);
|
|
7446
|
+
font-size: 13px;
|
|
7447
|
+
cursor: pointer;
|
|
7448
|
+
transition: background 0.15s;
|
|
7449
|
+
}
|
|
7450
|
+
|
|
7451
|
+
.header-actions button:hover {
|
|
7452
|
+
background: var(--surface-raised);
|
|
7453
|
+
}
|
|
7454
|
+
|
|
7455
|
+
.header-actions .pause-btn {
|
|
7456
|
+
border-color: var(--red-dim);
|
|
7457
|
+
color: var(--red);
|
|
7458
|
+
}
|
|
7459
|
+
|
|
7460
|
+
.header-actions .pause-btn:hover {
|
|
7461
|
+
background: rgba(248, 81, 73, 0.1);
|
|
7462
|
+
}
|
|
7463
|
+
|
|
7464
|
+
.header-actions .pause-btn.paused {
|
|
7465
|
+
background: var(--red-dim);
|
|
7466
|
+
color: white;
|
|
7467
|
+
}
|
|
7468
|
+
|
|
7469
|
+
/* \u2500\u2500 Tab bar \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7470
|
+
.tab-bar {
|
|
7471
|
+
display: flex;
|
|
7472
|
+
border-bottom: 1px solid var(--border);
|
|
7473
|
+
background: var(--surface);
|
|
7474
|
+
padding: 0 24px;
|
|
7475
|
+
}
|
|
7476
|
+
|
|
7477
|
+
.tab-bar button {
|
|
7478
|
+
padding: 10px 16px;
|
|
7479
|
+
border: none;
|
|
7480
|
+
background: none;
|
|
7481
|
+
color: var(--text-secondary);
|
|
7482
|
+
font-size: 14px;
|
|
7483
|
+
cursor: pointer;
|
|
7484
|
+
border-bottom: 2px solid transparent;
|
|
7485
|
+
transition: all 0.15s;
|
|
7486
|
+
}
|
|
7487
|
+
|
|
7488
|
+
.tab-bar button:hover {
|
|
7489
|
+
color: var(--text-primary);
|
|
7490
|
+
}
|
|
7491
|
+
|
|
7492
|
+
.tab-bar button.active {
|
|
7493
|
+
color: var(--text-primary);
|
|
7494
|
+
border-bottom-color: var(--blue);
|
|
7495
|
+
}
|
|
7496
|
+
|
|
7497
|
+
/* \u2500\u2500 Content \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7498
|
+
.fortress-content { padding: 24px; }
|
|
7499
|
+
|
|
7500
|
+
/* \u2500\u2500 Status Banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7501
|
+
.status-banner {
|
|
7502
|
+
display: flex;
|
|
7503
|
+
align-items: center;
|
|
7504
|
+
gap: 16px;
|
|
7505
|
+
padding: 20px 24px;
|
|
7506
|
+
border-radius: 8px;
|
|
7507
|
+
border: 1px solid var(--border);
|
|
7508
|
+
background: var(--surface);
|
|
7509
|
+
margin-bottom: 24px;
|
|
7510
|
+
}
|
|
7511
|
+
|
|
7512
|
+
.status-indicator {
|
|
7513
|
+
width: 48px;
|
|
7514
|
+
height: 48px;
|
|
7515
|
+
border-radius: 50%;
|
|
7516
|
+
display: flex;
|
|
7517
|
+
align-items: center;
|
|
7518
|
+
justify-content: center;
|
|
7519
|
+
font-size: 24px;
|
|
7520
|
+
flex-shrink: 0;
|
|
7521
|
+
}
|
|
7522
|
+
|
|
7523
|
+
.status-indicator.green { background: rgba(63, 185, 80, 0.15); color: var(--green); }
|
|
7524
|
+
.status-indicator.amber { background: rgba(210, 153, 34, 0.15); color: var(--amber); }
|
|
7525
|
+
.status-indicator.red { background: rgba(248, 81, 73, 0.15); color: var(--red); }
|
|
7526
|
+
|
|
7527
|
+
.status-info h2 {
|
|
7528
|
+
font-size: 18px;
|
|
7529
|
+
font-weight: 600;
|
|
7530
|
+
margin-bottom: 4px;
|
|
7531
|
+
}
|
|
7532
|
+
|
|
7533
|
+
.status-info p {
|
|
7534
|
+
font-size: 14px;
|
|
7535
|
+
color: var(--text-secondary);
|
|
7536
|
+
}
|
|
7537
|
+
|
|
7538
|
+
.status-stats {
|
|
7539
|
+
display: flex;
|
|
7540
|
+
gap: 24px;
|
|
7541
|
+
margin-left: auto;
|
|
7542
|
+
}
|
|
7543
|
+
|
|
7544
|
+
.stat {
|
|
7545
|
+
text-align: center;
|
|
7546
|
+
}
|
|
7547
|
+
|
|
7548
|
+
.stat .value {
|
|
7549
|
+
font-size: 24px;
|
|
7550
|
+
font-weight: 600;
|
|
7551
|
+
font-variant-numeric: tabular-nums;
|
|
7552
|
+
}
|
|
7553
|
+
|
|
7554
|
+
.stat .label {
|
|
7555
|
+
font-size: 11px;
|
|
7556
|
+
color: var(--text-secondary);
|
|
7557
|
+
text-transform: uppercase;
|
|
7558
|
+
letter-spacing: 0.5px;
|
|
7559
|
+
}
|
|
7560
|
+
|
|
7561
|
+
/* \u2500\u2500 Two-column layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7562
|
+
.fortress-grid {
|
|
7563
|
+
display: grid;
|
|
7564
|
+
grid-template-columns: 1fr 360px;
|
|
7565
|
+
gap: 24px;
|
|
7566
|
+
}
|
|
7567
|
+
|
|
7568
|
+
@media (max-width: 900px) {
|
|
7569
|
+
.fortress-grid { grid-template-columns: 1fr; }
|
|
7570
|
+
}
|
|
7571
|
+
|
|
7572
|
+
/* \u2500\u2500 Feed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7573
|
+
.feed-panel {
|
|
7574
|
+
background: var(--surface);
|
|
7575
|
+
border: 1px solid var(--border);
|
|
7576
|
+
border-radius: 8px;
|
|
7577
|
+
overflow: hidden;
|
|
7578
|
+
}
|
|
7579
|
+
|
|
7580
|
+
.panel-header {
|
|
7581
|
+
display: flex;
|
|
7582
|
+
align-items: center;
|
|
7583
|
+
justify-content: space-between;
|
|
7584
|
+
padding: 12px 16px;
|
|
7585
|
+
border-bottom: 1px solid var(--border);
|
|
7586
|
+
}
|
|
7587
|
+
|
|
7588
|
+
.panel-header h3 {
|
|
7589
|
+
font-size: 14px;
|
|
7590
|
+
font-weight: 600;
|
|
7591
|
+
}
|
|
7592
|
+
|
|
7593
|
+
.feed-list {
|
|
7594
|
+
max-height: 600px;
|
|
7595
|
+
overflow-y: auto;
|
|
7596
|
+
scroll-behavior: smooth;
|
|
7597
|
+
}
|
|
7598
|
+
|
|
7599
|
+
.feed-item {
|
|
7600
|
+
display: flex;
|
|
7601
|
+
align-items: flex-start;
|
|
7602
|
+
gap: 10px;
|
|
7603
|
+
padding: 10px 16px;
|
|
7604
|
+
border-bottom: 1px solid var(--border);
|
|
7605
|
+
font-size: 13px;
|
|
7606
|
+
transition: background 0.1s;
|
|
7607
|
+
}
|
|
7608
|
+
|
|
7609
|
+
.feed-item:hover {
|
|
7610
|
+
background: var(--surface-raised);
|
|
7611
|
+
}
|
|
7612
|
+
|
|
7613
|
+
.feed-dot {
|
|
7614
|
+
width: 8px;
|
|
7615
|
+
height: 8px;
|
|
7616
|
+
border-radius: 50%;
|
|
7617
|
+
margin-top: 5px;
|
|
7618
|
+
flex-shrink: 0;
|
|
7619
|
+
}
|
|
7620
|
+
|
|
7621
|
+
.feed-dot.green { background: var(--green); }
|
|
7622
|
+
.feed-dot.amber { background: var(--amber); }
|
|
7623
|
+
.feed-dot.red { background: var(--red); }
|
|
7624
|
+
|
|
7625
|
+
.feed-detail {
|
|
7626
|
+
flex: 1;
|
|
7627
|
+
min-width: 0;
|
|
7628
|
+
}
|
|
7629
|
+
|
|
7630
|
+
.feed-tool {
|
|
7631
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
7632
|
+
font-size: 12px;
|
|
7633
|
+
color: var(--blue);
|
|
7634
|
+
word-break: break-all;
|
|
7635
|
+
}
|
|
7636
|
+
|
|
7637
|
+
.feed-decision {
|
|
7638
|
+
font-size: 12px;
|
|
7639
|
+
color: var(--text-secondary);
|
|
7640
|
+
margin-top: 2px;
|
|
7641
|
+
}
|
|
7642
|
+
|
|
7643
|
+
.feed-time {
|
|
7644
|
+
font-size: 11px;
|
|
7645
|
+
color: var(--text-muted);
|
|
7646
|
+
flex-shrink: 0;
|
|
7647
|
+
white-space: nowrap;
|
|
7648
|
+
}
|
|
7649
|
+
|
|
7650
|
+
.feed-empty {
|
|
7651
|
+
padding: 40px 16px;
|
|
7652
|
+
text-align: center;
|
|
7653
|
+
color: var(--text-muted);
|
|
7654
|
+
font-size: 14px;
|
|
7655
|
+
}
|
|
7656
|
+
|
|
7657
|
+
/* \u2500\u2500 Alerts Panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7658
|
+
.alerts-panel {
|
|
7659
|
+
background: var(--surface);
|
|
7660
|
+
border: 1px solid var(--border);
|
|
7661
|
+
border-radius: 8px;
|
|
7662
|
+
overflow: hidden;
|
|
7663
|
+
}
|
|
7664
|
+
|
|
7665
|
+
.alert-item {
|
|
7666
|
+
padding: 12px 16px;
|
|
7667
|
+
border-bottom: 1px solid var(--border);
|
|
7668
|
+
}
|
|
7669
|
+
|
|
7670
|
+
.alert-item .alert-title {
|
|
7671
|
+
font-size: 13px;
|
|
7672
|
+
font-weight: 500;
|
|
7673
|
+
margin-bottom: 4px;
|
|
7674
|
+
}
|
|
7675
|
+
|
|
7676
|
+
.alert-item .alert-desc {
|
|
7677
|
+
font-size: 12px;
|
|
7678
|
+
color: var(--text-secondary);
|
|
7679
|
+
margin-bottom: 8px;
|
|
7680
|
+
}
|
|
7681
|
+
|
|
7682
|
+
.alert-actions {
|
|
7683
|
+
display: flex;
|
|
7684
|
+
gap: 8px;
|
|
7685
|
+
}
|
|
7686
|
+
|
|
7687
|
+
.alert-actions button {
|
|
7688
|
+
padding: 4px 12px;
|
|
7689
|
+
border-radius: 4px;
|
|
7690
|
+
border: 1px solid var(--border);
|
|
7691
|
+
font-size: 12px;
|
|
7692
|
+
cursor: pointer;
|
|
7693
|
+
transition: all 0.15s;
|
|
7694
|
+
}
|
|
7695
|
+
|
|
7696
|
+
.approve-btn {
|
|
7697
|
+
background: var(--green-dim);
|
|
7698
|
+
color: white;
|
|
7699
|
+
border-color: var(--green-dim) !important;
|
|
7700
|
+
}
|
|
7701
|
+
|
|
7702
|
+
.approve-btn:hover { opacity: 0.9; }
|
|
7703
|
+
|
|
7704
|
+
.deny-btn {
|
|
7705
|
+
background: none;
|
|
7706
|
+
color: var(--red);
|
|
7707
|
+
border-color: var(--red-dim) !important;
|
|
7708
|
+
}
|
|
7709
|
+
|
|
7710
|
+
.deny-btn:hover {
|
|
7711
|
+
background: rgba(248, 81, 73, 0.1);
|
|
7712
|
+
}
|
|
7713
|
+
|
|
7714
|
+
.alerts-empty {
|
|
7715
|
+
padding: 40px 16px;
|
|
7716
|
+
text-align: center;
|
|
7717
|
+
color: var(--text-muted);
|
|
7718
|
+
font-size: 14px;
|
|
7719
|
+
}
|
|
7720
|
+
|
|
7721
|
+
/* \u2500\u2500 Servers panel \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
7722
|
+
.servers-panel {
|
|
7723
|
+
margin-top: 16px;
|
|
7724
|
+
}
|
|
7725
|
+
|
|
7726
|
+
.server-row {
|
|
7727
|
+
display: flex;
|
|
7728
|
+
align-items: center;
|
|
7729
|
+
gap: 8px;
|
|
7730
|
+
padding: 8px 16px;
|
|
7731
|
+
border-bottom: 1px solid var(--border);
|
|
7732
|
+
font-size: 13px;
|
|
7733
|
+
}
|
|
7734
|
+
|
|
7735
|
+
.server-status-dot {
|
|
7736
|
+
width: 8px;
|
|
7737
|
+
height: 8px;
|
|
7738
|
+
border-radius: 50%;
|
|
7739
|
+
}
|
|
7740
|
+
|
|
7741
|
+
.server-status-dot.connected { background: var(--green); }
|
|
7742
|
+
.server-status-dot.connecting { background: var(--amber); }
|
|
7743
|
+
.server-status-dot.disconnected, .server-status-dot.error { background: var(--red); }
|
|
7744
|
+
|
|
7745
|
+
.server-name {
|
|
7746
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
7747
|
+
font-size: 12px;
|
|
7748
|
+
}
|
|
7749
|
+
|
|
7750
|
+
.server-tier {
|
|
7751
|
+
margin-left: auto;
|
|
7752
|
+
font-size: 11px;
|
|
7753
|
+
color: var(--text-secondary);
|
|
7754
|
+
}
|
|
7755
|
+
</style>
|
|
7756
|
+
</head>
|
|
7757
|
+
<body>
|
|
7758
|
+
<!-- Header -->
|
|
7759
|
+
<div class="fortress-header">
|
|
7760
|
+
<div class="fortress-brand">
|
|
7761
|
+
<div class="shield">🛡</div>
|
|
7762
|
+
<div>
|
|
7763
|
+
<h1>Sanctuary Cocoon</h1>
|
|
7764
|
+
<div class="version">v${esc(options.serverVersion)}</div>
|
|
7765
|
+
</div>
|
|
7766
|
+
</div>
|
|
7767
|
+
<div class="header-actions">
|
|
7768
|
+
<button class="pause-btn" id="pause-btn" title="Pause agent \u2014 requires approval for all operations">Pause Agent</button>
|
|
7769
|
+
<button id="advanced-btn">Advanced</button>
|
|
7770
|
+
</div>
|
|
7771
|
+
</div>
|
|
7772
|
+
|
|
7773
|
+
<!-- Tab bar -->
|
|
7774
|
+
<div class="tab-bar">
|
|
7775
|
+
<button class="active" data-tab="fortress">Fortress</button>
|
|
7776
|
+
<button data-tab="advanced">Advanced</button>
|
|
7777
|
+
</div>
|
|
7778
|
+
|
|
7779
|
+
<!-- Fortress View -->
|
|
7780
|
+
<div class="fortress-content" id="fortress-tab">
|
|
7781
|
+
<!-- Status Banner -->
|
|
7782
|
+
<div class="status-banner" id="status-banner">
|
|
7783
|
+
<div class="status-indicator green" id="status-indicator">✓</div>
|
|
7784
|
+
<div class="status-info">
|
|
7785
|
+
<h2 id="status-title">Agent Protected</h2>
|
|
7786
|
+
<p id="status-subtitle">${options.upstreamServerCount} server${options.upstreamServerCount !== 1 ? "s" : ""} monitored. All systems nominal.</p>
|
|
7787
|
+
</div>
|
|
7788
|
+
<div class="status-stats">
|
|
7789
|
+
<div class="stat">
|
|
7790
|
+
<div class="value" id="stat-total">0</div>
|
|
7791
|
+
<div class="label">Calls</div>
|
|
7792
|
+
</div>
|
|
7793
|
+
<div class="stat">
|
|
7794
|
+
<div class="value" id="stat-blocked">0</div>
|
|
7795
|
+
<div class="label">Blocked</div>
|
|
7796
|
+
</div>
|
|
7797
|
+
<div class="stat">
|
|
7798
|
+
<div class="value" id="stat-pending">0</div>
|
|
7799
|
+
<div class="label">Pending</div>
|
|
7800
|
+
</div>
|
|
7801
|
+
</div>
|
|
7802
|
+
</div>
|
|
7803
|
+
|
|
7804
|
+
<!-- Two-column layout -->
|
|
7805
|
+
<div class="fortress-grid">
|
|
7806
|
+
<!-- Live Feed -->
|
|
7807
|
+
<div class="feed-panel">
|
|
7808
|
+
<div class="panel-header">
|
|
7809
|
+
<h3>Live Activity</h3>
|
|
7810
|
+
<span style="font-size: 12px; color: var(--text-muted);" id="feed-count">0 events</span>
|
|
7811
|
+
</div>
|
|
7812
|
+
<div class="feed-list" id="feed-list">
|
|
7813
|
+
<div class="feed-empty">Waiting for tool calls...</div>
|
|
7814
|
+
</div>
|
|
7815
|
+
</div>
|
|
7816
|
+
|
|
7817
|
+
<!-- Right column: Alerts + Servers -->
|
|
7818
|
+
<div>
|
|
7819
|
+
<!-- Alerts -->
|
|
7820
|
+
<div class="alerts-panel">
|
|
7821
|
+
<div class="panel-header">
|
|
7822
|
+
<h3>Needs Attention</h3>
|
|
7823
|
+
<span style="font-size: 12px; color: var(--text-muted);" id="alert-count">0</span>
|
|
7824
|
+
</div>
|
|
7825
|
+
<div id="alerts-list">
|
|
7826
|
+
<div class="alerts-empty">No pending actions</div>
|
|
7827
|
+
</div>
|
|
7828
|
+
</div>
|
|
7829
|
+
|
|
7830
|
+
<!-- Servers -->
|
|
7831
|
+
<div class="alerts-panel servers-panel">
|
|
7832
|
+
<div class="panel-header">
|
|
7833
|
+
<h3>Upstream Servers</h3>
|
|
7834
|
+
</div>
|
|
7835
|
+
<div id="servers-list">
|
|
7836
|
+
<div class="alerts-empty">No servers configured</div>
|
|
7837
|
+
</div>
|
|
7838
|
+
</div>
|
|
7839
|
+
</div>
|
|
7840
|
+
</div>
|
|
7841
|
+
</div>
|
|
7842
|
+
|
|
7843
|
+
<script>
|
|
7844
|
+
// \u2500\u2500 State \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7845
|
+
const API_BASE = window.location.origin;
|
|
7846
|
+
const SESSION_TOKEN = sessionStorage.getItem('sanctuary_session') || '';
|
|
7847
|
+
const MAX_FEED_ITEMS = 50;
|
|
7848
|
+
|
|
7849
|
+
let feedItems = [];
|
|
7850
|
+
let totalCalls = 0;
|
|
7851
|
+
let blockedCalls = 0;
|
|
7852
|
+
let pendingApprovals = [];
|
|
7853
|
+
let upstreamServers = [];
|
|
7854
|
+
let paused = false;
|
|
7855
|
+
|
|
7856
|
+
// \u2500\u2500 SSE Connection \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7857
|
+
function connectSSE() {
|
|
7858
|
+
const url = API_BASE + '/events' + (SESSION_TOKEN ? '?session=' + SESSION_TOKEN : '');
|
|
7859
|
+
const eventSource = new EventSource(url);
|
|
7860
|
+
|
|
7861
|
+
eventSource.addEventListener('proxy-call', (e) => {
|
|
7862
|
+
try {
|
|
7863
|
+
const data = JSON.parse(e.data);
|
|
7864
|
+
addFeedItem(data);
|
|
7865
|
+
} catch {}
|
|
7866
|
+
});
|
|
7867
|
+
|
|
7868
|
+
eventSource.addEventListener('proxy-server-status', (e) => {
|
|
7869
|
+
try {
|
|
7870
|
+
const data = JSON.parse(e.data);
|
|
7871
|
+
updateServerStatus(data.server, data.state, data.tool_count, data.error);
|
|
7872
|
+
} catch {}
|
|
7873
|
+
});
|
|
7874
|
+
|
|
7875
|
+
eventSource.addEventListener('injection-alert', (e) => {
|
|
7876
|
+
try {
|
|
7877
|
+
const data = JSON.parse(e.data);
|
|
7878
|
+
addFeedItem({
|
|
7879
|
+
tool: data.tool_name || 'unknown',
|
|
7880
|
+
server: 'detection',
|
|
7881
|
+
decision: 'blocked',
|
|
7882
|
+
reason: 'Injection detected: ' + (data.signals || []).join(', '),
|
|
7883
|
+
timestamp: new Date().toISOString(),
|
|
7884
|
+
});
|
|
7885
|
+
} catch {}
|
|
7886
|
+
});
|
|
7887
|
+
|
|
7888
|
+
eventSource.addEventListener('approval-request', (e) => {
|
|
7889
|
+
try {
|
|
7890
|
+
const data = JSON.parse(e.data);
|
|
7891
|
+
addPendingApproval(data);
|
|
7892
|
+
} catch {}
|
|
7893
|
+
});
|
|
7894
|
+
|
|
7895
|
+
eventSource.addEventListener('approval-resolved', (e) => {
|
|
7896
|
+
try {
|
|
7897
|
+
const data = JSON.parse(e.data);
|
|
7898
|
+
removePendingApproval(data.id);
|
|
7899
|
+
} catch {}
|
|
7900
|
+
});
|
|
7901
|
+
|
|
7902
|
+
eventSource.onerror = () => {
|
|
7903
|
+
eventSource.close();
|
|
7904
|
+
setTimeout(connectSSE, 3000);
|
|
7905
|
+
};
|
|
7906
|
+
}
|
|
7907
|
+
|
|
7908
|
+
// \u2500\u2500 Feed \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7909
|
+
function addFeedItem(data) {
|
|
7910
|
+
totalCalls++;
|
|
7911
|
+
if (data.decision === 'blocked' || data.decision === 'denied') {
|
|
7912
|
+
blockedCalls++;
|
|
7913
|
+
}
|
|
7914
|
+
|
|
7915
|
+
feedItems.unshift({
|
|
7916
|
+
tool: data.tool || 'unknown',
|
|
7917
|
+
server: data.server || '',
|
|
7918
|
+
decision: data.decision || 'allowed',
|
|
7919
|
+
reason: data.reason || '',
|
|
7920
|
+
time: data.timestamp || new Date().toISOString(),
|
|
7921
|
+
});
|
|
7922
|
+
|
|
7923
|
+
if (feedItems.length > MAX_FEED_ITEMS) {
|
|
7924
|
+
feedItems = feedItems.slice(0, MAX_FEED_ITEMS);
|
|
7925
|
+
}
|
|
7926
|
+
|
|
7927
|
+
renderFeed();
|
|
7928
|
+
updateStats();
|
|
7929
|
+
updateStatus();
|
|
7930
|
+
}
|
|
7931
|
+
|
|
7932
|
+
function renderFeed() {
|
|
7933
|
+
const container = document.getElementById('feed-list');
|
|
7934
|
+
if (feedItems.length === 0) {
|
|
7935
|
+
container.innerHTML = '<div class="feed-empty">Waiting for tool calls...</div>';
|
|
7936
|
+
return;
|
|
7937
|
+
}
|
|
7938
|
+
|
|
7939
|
+
container.innerHTML = feedItems.map(item => {
|
|
7940
|
+
const dotColor = item.decision === 'allowed' ? 'green'
|
|
7941
|
+
: item.decision === 'pending' ? 'amber' : 'red';
|
|
7942
|
+
const decisionText = item.decision === 'allowed' ? 'Auto-allowed'
|
|
7943
|
+
: item.decision === 'pending' ? 'Awaiting approval'
|
|
7944
|
+
: item.decision === 'blocked' ? 'Blocked' : item.decision;
|
|
7945
|
+
const timeStr = new Date(item.time).toLocaleTimeString();
|
|
7946
|
+
|
|
7947
|
+
return '<div class="feed-item">' +
|
|
7948
|
+
'<div class="feed-dot ' + dotColor + '"></div>' +
|
|
7949
|
+
'<div class="feed-detail">' +
|
|
7950
|
+
'<div class="feed-tool">' + esc(item.tool) + '</div>' +
|
|
7951
|
+
'<div class="feed-decision">' + esc(decisionText) +
|
|
7952
|
+
(item.reason ? ' \u2014 ' + esc(item.reason) : '') + '</div>' +
|
|
7953
|
+
'</div>' +
|
|
7954
|
+
'<div class="feed-time">' + esc(timeStr) + '</div>' +
|
|
7955
|
+
'</div>';
|
|
7956
|
+
}).join('');
|
|
7957
|
+
|
|
7958
|
+
document.getElementById('feed-count').textContent = feedItems.length + ' events';
|
|
7959
|
+
}
|
|
7960
|
+
|
|
7961
|
+
// \u2500\u2500 Alerts (Pending Approvals) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7962
|
+
function addPendingApproval(data) {
|
|
7963
|
+
pendingApprovals.push(data);
|
|
7964
|
+
renderAlerts();
|
|
7965
|
+
updateStats();
|
|
7966
|
+
updateStatus();
|
|
7967
|
+
}
|
|
7968
|
+
|
|
7969
|
+
function removePendingApproval(id) {
|
|
7970
|
+
pendingApprovals = pendingApprovals.filter(a => a.id !== id);
|
|
7971
|
+
renderAlerts();
|
|
7972
|
+
updateStats();
|
|
7973
|
+
updateStatus();
|
|
7974
|
+
}
|
|
7975
|
+
|
|
7976
|
+
function renderAlerts() {
|
|
7977
|
+
const container = document.getElementById('alerts-list');
|
|
7978
|
+
if (pendingApprovals.length === 0) {
|
|
7979
|
+
container.innerHTML = '<div class="alerts-empty">No pending actions</div>';
|
|
7980
|
+
document.getElementById('alert-count').textContent = '0';
|
|
7981
|
+
return;
|
|
7982
|
+
}
|
|
7983
|
+
|
|
7984
|
+
document.getElementById('alert-count').textContent = pendingApprovals.length.toString();
|
|
7985
|
+
|
|
7986
|
+
container.innerHTML = pendingApprovals.map(approval => {
|
|
7987
|
+
return '<div class="alert-item">' +
|
|
7988
|
+
'<div class="alert-title">Approval required: ' + esc(approval.operation || approval.tool_name || 'unknown') + '</div>' +
|
|
7989
|
+
'<div class="alert-desc">' + esc(approval.reason || 'This operation requires your approval before it can proceed.') + '</div>' +
|
|
7990
|
+
'<div class="alert-actions">' +
|
|
7991
|
+
'<button class="approve-btn" onclick="handleApproval(\\'' + esc(approval.id) + '\\', true)">Approve</button>' +
|
|
7992
|
+
'<button class="deny-btn" onclick="handleApproval(\\'' + esc(approval.id) + '\\', false)">Deny</button>' +
|
|
7993
|
+
'</div>' +
|
|
7994
|
+
'</div>';
|
|
7995
|
+
}).join('');
|
|
7996
|
+
}
|
|
7997
|
+
|
|
7998
|
+
async function handleApproval(id, approved) {
|
|
7999
|
+
const endpoint = approved ? '/api/approve/' : '/api/deny/';
|
|
8000
|
+
try {
|
|
8001
|
+
await fetch(API_BASE + endpoint + id, {
|
|
8002
|
+
method: 'POST',
|
|
8003
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8004
|
+
});
|
|
8005
|
+
removePendingApproval(id);
|
|
8006
|
+
} catch (err) {
|
|
8007
|
+
console.error('Approval action failed:', err);
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
8010
|
+
|
|
8011
|
+
// \u2500\u2500 Servers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8012
|
+
function updateServerStatus(serverName, state, toolCount, error) {
|
|
8013
|
+
const existing = upstreamServers.find(s => s.name === serverName);
|
|
8014
|
+
if (existing) {
|
|
8015
|
+
existing.state = state;
|
|
8016
|
+
existing.tool_count = toolCount;
|
|
8017
|
+
existing.error = error;
|
|
8018
|
+
} else {
|
|
8019
|
+
upstreamServers.push({ name: serverName, state, tool_count: toolCount, error });
|
|
8020
|
+
}
|
|
8021
|
+
renderServers();
|
|
8022
|
+
updateStatus();
|
|
8023
|
+
}
|
|
8024
|
+
|
|
8025
|
+
function renderServers() {
|
|
8026
|
+
const container = document.getElementById('servers-list');
|
|
8027
|
+
if (upstreamServers.length === 0) {
|
|
8028
|
+
container.innerHTML = '<div class="alerts-empty">No servers configured</div>';
|
|
8029
|
+
return;
|
|
8030
|
+
}
|
|
8031
|
+
|
|
8032
|
+
container.innerHTML = upstreamServers.map(server => {
|
|
8033
|
+
const stateClass = server.state || 'disconnected';
|
|
8034
|
+
const stateLabel = server.state === 'connected' ? 'Connected'
|
|
8035
|
+
: server.state === 'connecting' ? 'Connecting...'
|
|
8036
|
+
: server.state === 'error' ? 'Error' : 'Disconnected';
|
|
8037
|
+
|
|
8038
|
+
return '<div class="server-row">' +
|
|
8039
|
+
'<div class="server-status-dot ' + stateClass + '"></div>' +
|
|
8040
|
+
'<span class="server-name">' + esc(server.name) + '</span>' +
|
|
8041
|
+
'<span class="server-tier">' + esc(stateLabel) +
|
|
8042
|
+
(server.tool_count ? ' (' + server.tool_count + ' tools)' : '') + '</span>' +
|
|
8043
|
+
'</div>';
|
|
8044
|
+
}).join('');
|
|
8045
|
+
}
|
|
8046
|
+
|
|
8047
|
+
// \u2500\u2500 Status Banner \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8048
|
+
function updateStats() {
|
|
8049
|
+
document.getElementById('stat-total').textContent = totalCalls.toString();
|
|
8050
|
+
document.getElementById('stat-blocked').textContent = blockedCalls.toString();
|
|
8051
|
+
document.getElementById('stat-pending').textContent = pendingApprovals.length.toString();
|
|
8052
|
+
}
|
|
8053
|
+
|
|
8054
|
+
function updateStatus() {
|
|
8055
|
+
const indicator = document.getElementById('status-indicator');
|
|
8056
|
+
const title = document.getElementById('status-title');
|
|
8057
|
+
const subtitle = document.getElementById('status-subtitle');
|
|
8058
|
+
|
|
8059
|
+
const hasErrors = upstreamServers.some(s => s.state === 'error');
|
|
8060
|
+
const hasPending = pendingApprovals.length > 0;
|
|
8061
|
+
const hasBlocked = blockedCalls > 0;
|
|
8062
|
+
|
|
8063
|
+
if (paused) {
|
|
8064
|
+
indicator.className = 'status-indicator red';
|
|
8065
|
+
indicator.innerHTML = '⏸';
|
|
8066
|
+
title.textContent = 'Agent Paused';
|
|
8067
|
+
subtitle.textContent = 'All operations require approval. Click Resume to restore normal mode.';
|
|
8068
|
+
} else if (hasErrors) {
|
|
8069
|
+
indicator.className = 'status-indicator red';
|
|
8070
|
+
indicator.innerHTML = '⚠';
|
|
8071
|
+
title.textContent = 'Connection Issues';
|
|
8072
|
+
subtitle.textContent = 'One or more upstream servers have errors.';
|
|
8073
|
+
} else if (hasPending) {
|
|
8074
|
+
indicator.className = 'status-indicator amber';
|
|
8075
|
+
indicator.innerHTML = '⏳';
|
|
8076
|
+
title.textContent = 'Action Required';
|
|
8077
|
+
subtitle.textContent = pendingApprovals.length + ' operation' + (pendingApprovals.length > 1 ? 's' : '') + ' awaiting your approval.';
|
|
8078
|
+
} else {
|
|
8079
|
+
indicator.className = 'status-indicator green';
|
|
8080
|
+
indicator.innerHTML = '✓';
|
|
8081
|
+
title.textContent = 'Agent Protected';
|
|
8082
|
+
const serverCount = upstreamServers.filter(s => s.state === 'connected').length || ${options.upstreamServerCount};
|
|
8083
|
+
subtitle.textContent = serverCount + ' server' + (serverCount !== 1 ? 's' : '') + ' monitored. All systems nominal.';
|
|
8084
|
+
}
|
|
8085
|
+
}
|
|
8086
|
+
|
|
8087
|
+
// \u2500\u2500 Pause/Resume \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8088
|
+
document.getElementById('pause-btn').addEventListener('click', () => {
|
|
8089
|
+
paused = !paused;
|
|
8090
|
+
const btn = document.getElementById('pause-btn');
|
|
8091
|
+
if (paused) {
|
|
8092
|
+
btn.textContent = 'Resume Agent';
|
|
8093
|
+
btn.classList.add('paused');
|
|
8094
|
+
} else {
|
|
8095
|
+
btn.textContent = 'Pause Agent';
|
|
8096
|
+
btn.classList.remove('paused');
|
|
8097
|
+
}
|
|
8098
|
+
updateStatus();
|
|
8099
|
+
// TODO: POST to /api/cocoon/pause to set all tiers to 1
|
|
8100
|
+
});
|
|
8101
|
+
|
|
8102
|
+
// \u2500\u2500 Tab switching \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8103
|
+
document.getElementById('advanced-btn').addEventListener('click', () => {
|
|
8104
|
+
window.location.href = '/dashboard?session=' + SESSION_TOKEN;
|
|
8105
|
+
});
|
|
8106
|
+
|
|
8107
|
+
document.querySelectorAll('.tab-bar button').forEach(btn => {
|
|
8108
|
+
btn.addEventListener('click', () => {
|
|
8109
|
+
const tab = btn.dataset.tab;
|
|
8110
|
+
if (tab === 'advanced') {
|
|
8111
|
+
window.location.href = '/dashboard?session=' + SESSION_TOKEN;
|
|
8112
|
+
}
|
|
8113
|
+
});
|
|
8114
|
+
});
|
|
8115
|
+
|
|
8116
|
+
// \u2500\u2500 Escape helper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8117
|
+
function esc(str) {
|
|
8118
|
+
if (!str) return '';
|
|
8119
|
+
const d = document.createElement('div');
|
|
8120
|
+
d.textContent = String(str);
|
|
8121
|
+
return d.innerHTML;
|
|
8122
|
+
}
|
|
8123
|
+
|
|
8124
|
+
// \u2500\u2500 Init \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
8125
|
+
async function init() {
|
|
8126
|
+
// Load initial server state
|
|
8127
|
+
try {
|
|
8128
|
+
const resp = await fetch(API_BASE + '/api/proxy/servers', {
|
|
8129
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8130
|
+
});
|
|
8131
|
+
if (resp.ok) {
|
|
8132
|
+
const data = await resp.json();
|
|
8133
|
+
upstreamServers = data.servers || [];
|
|
8134
|
+
renderServers();
|
|
8135
|
+
}
|
|
8136
|
+
} catch {}
|
|
8137
|
+
|
|
8138
|
+
// Load pending approvals
|
|
8139
|
+
try {
|
|
8140
|
+
const resp = await fetch(API_BASE + '/api/pending', {
|
|
8141
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8142
|
+
});
|
|
8143
|
+
if (resp.ok) {
|
|
8144
|
+
const data = await resp.json();
|
|
8145
|
+
pendingApprovals = data.pending || [];
|
|
8146
|
+
renderAlerts();
|
|
8147
|
+
updateStats();
|
|
8148
|
+
}
|
|
8149
|
+
} catch {}
|
|
8150
|
+
|
|
8151
|
+
updateStatus();
|
|
8152
|
+
connectSSE();
|
|
8153
|
+
}
|
|
8154
|
+
|
|
8155
|
+
init();
|
|
8156
|
+
</script>
|
|
8157
|
+
</body>
|
|
8158
|
+
</html>`;
|
|
8159
|
+
}
|
|
8160
|
+
function esc(str) {
|
|
8161
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
8162
|
+
}
|
|
8163
|
+
|
|
7369
8164
|
// src/system-prompt-generator.ts
|
|
7370
8165
|
var FEATURE_INFO = {
|
|
7371
8166
|
audit_logging: {
|
|
7372
8167
|
name: "Audit Logging",
|
|
7373
|
-
activeDescription: "All your tool calls are logged to an encrypted audit trail. No action needed \u2014 this is automatic. You can query the log with
|
|
7374
|
-
toolNames: ["
|
|
7375
|
-
disabledDescription: "audit logging (
|
|
8168
|
+
activeDescription: "All your tool calls are logged to an encrypted audit trail. No action needed \u2014 this is automatic. You can query the log with monitor_audit_log if you need to review past activity.",
|
|
8169
|
+
toolNames: ["monitor_audit_log"],
|
|
8170
|
+
disabledDescription: "audit logging (monitor_audit_log)",
|
|
7376
8171
|
usageExample: "Automatic \u2014 every tool call you make is recorded. No explicit action required."
|
|
7377
8172
|
},
|
|
7378
8173
|
injection_detection: {
|
|
@@ -7383,16 +8178,16 @@ var FEATURE_INFO = {
|
|
|
7383
8178
|
},
|
|
7384
8179
|
context_gating: {
|
|
7385
8180
|
name: "Context Gating",
|
|
7386
|
-
activeDescription: "Before sending context to any external API (LLM inference, tool APIs, logging services), call
|
|
8181
|
+
activeDescription: "Before sending context to any external API (LLM inference, tool APIs, logging services), call context_gate_filter to strip sensitive fields. Use context_gate_set_policy to define filtering rules, or context_gate_apply_template for presets.",
|
|
7387
8182
|
toolNames: [
|
|
7388
|
-
"
|
|
7389
|
-
"
|
|
7390
|
-
"
|
|
7391
|
-
"
|
|
7392
|
-
"
|
|
8183
|
+
"context_gate_filter",
|
|
8184
|
+
"context_gate_set_policy",
|
|
8185
|
+
"context_gate_apply_template",
|
|
8186
|
+
"context_gate_recommend",
|
|
8187
|
+
"context_gate_list_policies"
|
|
7393
8188
|
],
|
|
7394
|
-
disabledDescription: "context gating (
|
|
7395
|
-
usageExample: "Before calling an external API, run:
|
|
8189
|
+
disabledDescription: "context gating (context_gate_filter)",
|
|
8190
|
+
usageExample: "Before calling an external API, run: context_gate_filter with your context object and policy_id to get a filtered version."
|
|
7396
8191
|
},
|
|
7397
8192
|
approval_gate: {
|
|
7398
8193
|
name: "Approval Gates",
|
|
@@ -7402,15 +8197,15 @@ var FEATURE_INFO = {
|
|
|
7402
8197
|
},
|
|
7403
8198
|
zk_proofs: {
|
|
7404
8199
|
name: "Zero-Knowledge Proofs",
|
|
7405
|
-
activeDescription: "You can prove claims about your data without revealing the underlying values. Use
|
|
8200
|
+
activeDescription: "You can prove claims about your data without revealing the underlying values. Use zk_commit to create a Pedersen commitment, zk_prove (Schnorr proof) to prove you know a committed value, and zk_range_prove to prove a value falls within a range \u2014 all without disclosing the actual data. For simpler SHA-256 commitments, use proof_commitment.",
|
|
7406
8201
|
toolNames: [
|
|
7407
|
-
"
|
|
7408
|
-
"
|
|
7409
|
-
"
|
|
7410
|
-
"
|
|
8202
|
+
"zk_commit",
|
|
8203
|
+
"zk_prove",
|
|
8204
|
+
"zk_range_prove",
|
|
8205
|
+
"proof_commitment"
|
|
7411
8206
|
],
|
|
7412
|
-
disabledDescription: "zero-knowledge proofs (
|
|
7413
|
-
usageExample: "To prove a claim without revealing data: first
|
|
8207
|
+
disabledDescription: "zero-knowledge proofs (zk_commit, zk_prove)",
|
|
8208
|
+
usageExample: "To prove a claim without revealing data: first zk_commit to commit, then zk_prove or zk_range_prove to generate a verifiable proof."
|
|
7414
8209
|
}
|
|
7415
8210
|
};
|
|
7416
8211
|
function generateSystemPrompt(profile) {
|
|
@@ -7473,12 +8268,12 @@ function buildQuickStart(activeKeys) {
|
|
|
7473
8268
|
const items = [];
|
|
7474
8269
|
if (activeKeys.includes("context_gating")) {
|
|
7475
8270
|
items.push(
|
|
7476
|
-
"1. ALWAYS call
|
|
8271
|
+
"1. ALWAYS call context_gate_filter before sending context to external APIs."
|
|
7477
8272
|
);
|
|
7478
8273
|
}
|
|
7479
8274
|
if (activeKeys.includes("zk_proofs")) {
|
|
7480
8275
|
items.push(
|
|
7481
|
-
`${items.length + 1}. Use
|
|
8276
|
+
`${items.length + 1}. Use zk_commit to prove claims without revealing underlying data.`
|
|
7482
8277
|
);
|
|
7483
8278
|
}
|
|
7484
8279
|
if (activeKeys.includes("approval_gate")) {
|
|
@@ -7522,6 +8317,7 @@ var DashboardApprovalChannel = class {
|
|
|
7522
8317
|
profileStore = null;
|
|
7523
8318
|
clientManager = null;
|
|
7524
8319
|
dashboardHTML;
|
|
8320
|
+
fortressHTML = null;
|
|
7525
8321
|
loginHTML;
|
|
7526
8322
|
authToken;
|
|
7527
8323
|
useTLS;
|
|
@@ -7907,8 +8703,14 @@ var DashboardApprovalChannel = class {
|
|
|
7907
8703
|
if (!this.checkAuth(req, url, res)) return;
|
|
7908
8704
|
if (!this.checkRateLimit(req, res, "general")) return;
|
|
7909
8705
|
try {
|
|
7910
|
-
if (method === "GET" &&
|
|
7911
|
-
this.
|
|
8706
|
+
if (method === "GET" && url.pathname === "/fortress") {
|
|
8707
|
+
this.serveFortressView(res);
|
|
8708
|
+
} else if (method === "GET" && (url.pathname === "/" || url.pathname === "/dashboard")) {
|
|
8709
|
+
if (this.fortressHTML) {
|
|
8710
|
+
this.serveFortressView(res);
|
|
8711
|
+
} else {
|
|
8712
|
+
this.serveDashboard(res);
|
|
8713
|
+
}
|
|
7912
8714
|
} else if (method === "GET" && url.pathname === "/events") {
|
|
7913
8715
|
this.handleSSE(req, res);
|
|
7914
8716
|
} else if (method === "GET" && url.pathname === "/api/status") {
|
|
@@ -8003,6 +8805,35 @@ var DashboardApprovalChannel = class {
|
|
|
8003
8805
|
});
|
|
8004
8806
|
res.end(this.dashboardHTML);
|
|
8005
8807
|
}
|
|
8808
|
+
serveFortressView(res) {
|
|
8809
|
+
if (!this.fortressHTML) {
|
|
8810
|
+
this.serveDashboard(res);
|
|
8811
|
+
return;
|
|
8812
|
+
}
|
|
8813
|
+
res.writeHead(200, {
|
|
8814
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
8815
|
+
"Cache-Control": "no-cache"
|
|
8816
|
+
});
|
|
8817
|
+
res.end(this.fortressHTML);
|
|
8818
|
+
}
|
|
8819
|
+
/**
|
|
8820
|
+
* Enable Fortress View (Cocoon mode) with the given upstream server count.
|
|
8821
|
+
* Once enabled, the root path `/` serves the Fortress View instead of the
|
|
8822
|
+
* standard dashboard. The standard dashboard remains available at `/dashboard`.
|
|
8823
|
+
*/
|
|
8824
|
+
enableFortressView(upstreamServerCount) {
|
|
8825
|
+
this.fortressHTML = generateFortressViewHTML({
|
|
8826
|
+
serverVersion: SANCTUARY_VERSION,
|
|
8827
|
+
authToken: this.authToken,
|
|
8828
|
+
upstreamServerCount
|
|
8829
|
+
});
|
|
8830
|
+
}
|
|
8831
|
+
/**
|
|
8832
|
+
* Broadcast a proxy call event to connected dashboards (Fortress View feed).
|
|
8833
|
+
*/
|
|
8834
|
+
broadcastProxyCall(data) {
|
|
8835
|
+
this.broadcastSSE("proxy-call", data);
|
|
8836
|
+
}
|
|
8006
8837
|
handleSSE(req, res) {
|
|
8007
8838
|
res.writeHead(200, {
|
|
8008
8839
|
"Content-Type": "text/event-stream",
|
|
@@ -8866,7 +9697,7 @@ var InjectionDetector = class {
|
|
|
8866
9697
|
}
|
|
8867
9698
|
/**
|
|
8868
9699
|
* Scan tool arguments for injection signals.
|
|
8869
|
-
* @param toolName Full tool name (e.g., "
|
|
9700
|
+
* @param toolName Full tool name (e.g., "state_read")
|
|
8870
9701
|
* @param args Tool arguments
|
|
8871
9702
|
* @returns DetectionResult with all detected signals
|
|
8872
9703
|
*/
|
|
@@ -9867,7 +10698,7 @@ var ApprovalGate = class {
|
|
|
9867
10698
|
/**
|
|
9868
10699
|
* Evaluate a tool call against the Principal Policy.
|
|
9869
10700
|
*
|
|
9870
|
-
* @param toolName - Full MCP tool name (e.g., "
|
|
10701
|
+
* @param toolName - Full MCP tool name (e.g., "state_export")
|
|
9871
10702
|
* @param args - Tool call arguments (for context extraction)
|
|
9872
10703
|
* @returns GateResult indicating whether the call is allowed
|
|
9873
10704
|
*/
|
|
@@ -10136,7 +10967,7 @@ var ApprovalGate = class {
|
|
|
10136
10967
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
10137
10968
|
return [
|
|
10138
10969
|
{
|
|
10139
|
-
name: "
|
|
10970
|
+
name: "principal_policy_view",
|
|
10140
10971
|
description: "View the current Principal Policy \u2014 the human-controlled rules governing what operations require approval. Read-only.",
|
|
10141
10972
|
inputSchema: {
|
|
10142
10973
|
type: "object",
|
|
@@ -10174,7 +11005,7 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
|
10174
11005
|
}
|
|
10175
11006
|
},
|
|
10176
11007
|
{
|
|
10177
|
-
name: "
|
|
11008
|
+
name: "principal_baseline_view",
|
|
10178
11009
|
description: "View the current behavioral baseline \u2014 the session profile used for anomaly detection. Shows known namespaces, counterparties, and tool call counts. Read-only.",
|
|
10179
11010
|
inputSchema: {
|
|
10180
11011
|
type: "object",
|
|
@@ -10514,7 +11345,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10514
11345
|
};
|
|
10515
11346
|
const tools = [
|
|
10516
11347
|
{
|
|
10517
|
-
name: "
|
|
11348
|
+
name: "shr_generate",
|
|
10518
11349
|
description: "Generate a signed Sovereignty Health Report (SHR) \u2014 a machine-readable, cryptographically signed advertisement of this instance's sovereignty posture. Present this to counterparties to prove your sovereignty capabilities.",
|
|
10519
11350
|
inputSchema: {
|
|
10520
11351
|
type: "object",
|
|
@@ -10543,7 +11374,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10543
11374
|
}
|
|
10544
11375
|
},
|
|
10545
11376
|
{
|
|
10546
|
-
name: "
|
|
11377
|
+
name: "shr_verify",
|
|
10547
11378
|
description: "Verify a counterparty's Sovereignty Health Report (SHR). Checks signature validity, temporal validity, and assesses sovereignty level.",
|
|
10548
11379
|
inputSchema: {
|
|
10549
11380
|
type: "object",
|
|
@@ -10569,7 +11400,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10569
11400
|
}
|
|
10570
11401
|
},
|
|
10571
11402
|
{
|
|
10572
|
-
name: "
|
|
11403
|
+
name: "shr_gateway_export",
|
|
10573
11404
|
description: "Export this instance's Sovereignty Health Report formatted for Ping Identity's Agent Gateway or other identity providers. Transforms the SHR into an authorization context with sovereignty scores, capability flags, and recommended access constraints.",
|
|
10574
11405
|
inputSchema: {
|
|
10575
11406
|
type: "object",
|
|
@@ -10956,7 +11787,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
10956
11787
|
};
|
|
10957
11788
|
const tools = [
|
|
10958
11789
|
{
|
|
10959
|
-
name: "
|
|
11790
|
+
name: "handshake_initiate",
|
|
10960
11791
|
description: "Initiate a sovereignty handshake with a counterparty. Generates a challenge containing this instance's signed SHR and a cryptographic nonce. Send the returned challenge to the counterparty.",
|
|
10961
11792
|
inputSchema: {
|
|
10962
11793
|
type: "object",
|
|
@@ -10983,7 +11814,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
10983
11814
|
}
|
|
10984
11815
|
},
|
|
10985
11816
|
{
|
|
10986
|
-
name: "
|
|
11817
|
+
name: "handshake_respond",
|
|
10987
11818
|
description: "Respond to an incoming sovereignty handshake challenge. Verifies the initiator's SHR, signs their nonce, and returns our SHR with a counter-nonce.",
|
|
10988
11819
|
inputSchema: {
|
|
10989
11820
|
type: "object",
|
|
@@ -11104,7 +11935,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11104
11935
|
}
|
|
11105
11936
|
},
|
|
11106
11937
|
{
|
|
11107
|
-
name: "
|
|
11938
|
+
name: "handshake_complete",
|
|
11108
11939
|
description: "Complete a sovereignty handshake (initiator side). Verifies the responder's SHR and nonce signature, signs their nonce, and produces the final result.",
|
|
11109
11940
|
inputSchema: {
|
|
11110
11941
|
type: "object",
|
|
@@ -11159,7 +11990,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11159
11990
|
}
|
|
11160
11991
|
},
|
|
11161
11992
|
{
|
|
11162
|
-
name: "
|
|
11993
|
+
name: "handshake_status",
|
|
11163
11994
|
description: "Check the status of a handshake session, or verify a completion message (responder side).",
|
|
11164
11995
|
inputSchema: {
|
|
11165
11996
|
type: "object",
|
|
@@ -11209,7 +12040,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11209
12040
|
},
|
|
11210
12041
|
// ─── Streamlined Exchange ─────────────────────────────────────────
|
|
11211
12042
|
{
|
|
11212
|
-
name: "
|
|
12043
|
+
name: "handshake_exchange",
|
|
11213
12044
|
description: "One-shot sovereignty exchange. Accepts a counterparty's signed SHR, verifies it, generates our SHR, and produces a signed attestation artifact \u2014 all in a single call. Returns a shareable attestation with human-readable summary. Use this instead of the 4-step handshake protocol when you want a quick, portable sovereignty verification (e.g., for social posting or async exchanges).",
|
|
11214
12045
|
inputSchema: {
|
|
11215
12046
|
type: "object",
|
|
@@ -11276,7 +12107,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11276
12107
|
}
|
|
11277
12108
|
},
|
|
11278
12109
|
{
|
|
11279
|
-
name: "
|
|
12110
|
+
name: "handshake_verify_attestation",
|
|
11280
12111
|
description: "Verify a signed attestation artifact from another agent. Checks the Ed25519 signature, temporal validity, and structural integrity.",
|
|
11281
12112
|
inputSchema: {
|
|
11282
12113
|
type: "object",
|
|
@@ -11485,7 +12316,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11485
12316
|
const tools = [
|
|
11486
12317
|
// ─── Peer Management ──────────────────────────────────────────────
|
|
11487
12318
|
{
|
|
11488
|
-
name: "
|
|
12319
|
+
name: "federation_peers",
|
|
11489
12320
|
description: "List known federation peers, register a peer from a completed handshake, or remove a peer. Every peer MUST enter through a verified handshake \u2014 no self-registration allowed.",
|
|
11490
12321
|
inputSchema: {
|
|
11491
12322
|
type: "object",
|
|
@@ -11588,7 +12419,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11588
12419
|
},
|
|
11589
12420
|
// ─── Trust Evaluation ─────────────────────────────────────────────
|
|
11590
12421
|
{
|
|
11591
|
-
name: "
|
|
12422
|
+
name: "federation_trust_evaluate",
|
|
11592
12423
|
description: "Evaluate the trust level of a federation peer. Considers handshake status, sovereignty tier, reputation score, and mutual attestation history. Returns a composite trust assessment.",
|
|
11593
12424
|
inputSchema: {
|
|
11594
12425
|
type: "object",
|
|
@@ -11623,7 +12454,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11623
12454
|
},
|
|
11624
12455
|
// ─── Federation Status ────────────────────────────────────────────
|
|
11625
12456
|
{
|
|
11626
|
-
name: "
|
|
12457
|
+
name: "federation_status",
|
|
11627
12458
|
description: "Overview of federation state: total peers, active connections, trust distribution, and readiness for cross-instance operations.",
|
|
11628
12459
|
inputSchema: {
|
|
11629
12460
|
type: "object",
|
|
@@ -11834,7 +12665,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11834
12665
|
const tools = [
|
|
11835
12666
|
// ─── bridge_commit ─────────────────────────────────────────────────
|
|
11836
12667
|
{
|
|
11837
|
-
name: "
|
|
12668
|
+
name: "bridge_commit",
|
|
11838
12669
|
description: "Create a cryptographic commitment binding a Concordia negotiation outcome to Sanctuary's L3 proof layer. The commitment includes a SHA-256 hash of the canonical outcome (hiding + binding), an Ed25519 signature by the committer's identity, and an optional Pedersen commitment on the round count for zero-knowledge range proofs. This is the Sanctuary side of the Concordia bridge \u2014 call this when a Concordia `accept` fires.",
|
|
11839
12670
|
inputSchema: {
|
|
11840
12671
|
type: "object",
|
|
@@ -11936,7 +12767,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11936
12767
|
},
|
|
11937
12768
|
// ─── bridge_verify ───────────────────────────────────────────────────
|
|
11938
12769
|
{
|
|
11939
|
-
name: "
|
|
12770
|
+
name: "bridge_verify",
|
|
11940
12771
|
description: "Verify a bridge commitment against a revealed Concordia negotiation outcome. Checks SHA-256 commitment validity, Ed25519 signature, session ID match, terms hash integrity, and Pedersen commitment (if present). Use this to confirm that a counterparty's claimed negotiation outcome matches what was cryptographically committed.",
|
|
11941
12772
|
inputSchema: {
|
|
11942
12773
|
type: "object",
|
|
@@ -11992,7 +12823,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11992
12823
|
},
|
|
11993
12824
|
// ─── bridge_attest ───────────────────────────────────────────────────
|
|
11994
12825
|
{
|
|
11995
|
-
name: "
|
|
12826
|
+
name: "bridge_attest",
|
|
11996
12827
|
description: "Record a Concordia negotiation as a Sanctuary L4 reputation attestation, linked to a bridge commitment. This completes the bridge: the commitment (L3) proves the terms were agreed, and the attestation (L4) feeds the sovereignty-weighted reputation score. The attestation is automatically tagged with the counterparty's sovereignty tier from any completed handshake.",
|
|
11997
12828
|
inputSchema: {
|
|
11998
12829
|
type: "object",
|
|
@@ -12556,7 +13387,7 @@ function generateGaps(env, l1, l2, l3, l4) {
|
|
|
12556
13387
|
title: "No context gating for outbound inference calls",
|
|
12557
13388
|
description: "Your agent sends its full context \u2014 conversation history, memory, preferences, internal reasoning \u2014 to remote LLM providers on every inference call. There is no mechanism to filter what leaves the sovereignty boundary. The provider sees everything the agent knows.",
|
|
12558
13389
|
openclaw_relevance: env.openclaw_detected ? "OpenClaw sends full agent context (including MEMORY.md, tool results, and conversation history) to the configured LLM provider with every API call. There is no built-in context filtering." : null,
|
|
12559
|
-
sanctuary_solution: "Sanctuary's context gating (sanctuary/context_gate_set_policy +
|
|
13390
|
+
sanctuary_solution: "Sanctuary's context gating (sanctuary/context_gate_set_policy + context_gate_filter) lets you define per-provider policies that control exactly what context flows outbound. Redact secrets, hash identifiers, and send only minimum-necessary context for each call.",
|
|
12560
13391
|
incident_class: INCIDENT_CONTEXT_LEAKAGE
|
|
12561
13392
|
});
|
|
12562
13393
|
}
|
|
@@ -12568,7 +13399,7 @@ function generateGaps(env, l1, l2, l3, l4) {
|
|
|
12568
13399
|
title: "No audit trail",
|
|
12569
13400
|
description: "No audit trail exists for tool call history. There is no record of what operations were executed, when, or by whom.",
|
|
12570
13401
|
openclaw_relevance: null,
|
|
12571
|
-
sanctuary_solution: "Sanctuary maintains an encrypted audit log of all operations, queryable via
|
|
13402
|
+
sanctuary_solution: "Sanctuary maintains an encrypted audit log of all operations, queryable via monitor_audit_log.",
|
|
12572
13403
|
incident_class: INCIDENT_CLAUDE_CODE_LEAK
|
|
12573
13404
|
});
|
|
12574
13405
|
}
|
|
@@ -12603,7 +13434,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12603
13434
|
recs.push({
|
|
12604
13435
|
priority: 1,
|
|
12605
13436
|
action: "Create a cryptographic identity \u2014 your agent's foundation for all sovereignty operations",
|
|
12606
|
-
tool: "
|
|
13437
|
+
tool: "identity_create",
|
|
12607
13438
|
effort: "immediate",
|
|
12608
13439
|
impact: "critical"
|
|
12609
13440
|
});
|
|
@@ -12612,7 +13443,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12612
13443
|
recs.push({
|
|
12613
13444
|
priority: 2,
|
|
12614
13445
|
action: "Migrate plaintext agent state to Sanctuary's encrypted store",
|
|
12615
|
-
tool: "
|
|
13446
|
+
tool: "state_write",
|
|
12616
13447
|
effort: "minutes",
|
|
12617
13448
|
impact: "critical"
|
|
12618
13449
|
});
|
|
@@ -12620,7 +13451,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12620
13451
|
recs.push({
|
|
12621
13452
|
priority: 3,
|
|
12622
13453
|
action: "Generate a Sovereignty Health Report to present to counterparties",
|
|
12623
|
-
tool: "
|
|
13454
|
+
tool: "shr_generate",
|
|
12624
13455
|
effort: "immediate",
|
|
12625
13456
|
impact: "high"
|
|
12626
13457
|
});
|
|
@@ -12628,7 +13459,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12628
13459
|
recs.push({
|
|
12629
13460
|
priority: 4,
|
|
12630
13461
|
action: "Enable the three-tier Principal Policy gate for graduated approval",
|
|
12631
|
-
tool: "
|
|
13462
|
+
tool: "principal_policy_view",
|
|
12632
13463
|
effort: "minutes",
|
|
12633
13464
|
impact: "high"
|
|
12634
13465
|
});
|
|
@@ -12637,7 +13468,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12637
13468
|
recs.push({
|
|
12638
13469
|
priority: 5,
|
|
12639
13470
|
action: "Configure context gating to control what flows to LLM providers",
|
|
12640
|
-
tool: "
|
|
13471
|
+
tool: "context_gate_set_policy",
|
|
12641
13472
|
effort: "minutes",
|
|
12642
13473
|
impact: "high"
|
|
12643
13474
|
});
|
|
@@ -12646,7 +13477,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12646
13477
|
recs.push({
|
|
12647
13478
|
priority: 6,
|
|
12648
13479
|
action: "Start recording reputation attestations from completed interactions",
|
|
12649
|
-
tool: "
|
|
13480
|
+
tool: "reputation_record",
|
|
12650
13481
|
effort: "minutes",
|
|
12651
13482
|
impact: "medium"
|
|
12652
13483
|
});
|
|
@@ -12655,7 +13486,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12655
13486
|
recs.push({
|
|
12656
13487
|
priority: 7,
|
|
12657
13488
|
action: "Configure selective disclosure policies for data sharing",
|
|
12658
|
-
tool: "
|
|
13489
|
+
tool: "disclosure_set_policy",
|
|
12659
13490
|
effort: "hours",
|
|
12660
13491
|
impact: "medium"
|
|
12661
13492
|
});
|
|
@@ -12795,7 +13626,7 @@ function wordWrap(text, maxWidth) {
|
|
|
12795
13626
|
function createAuditTools(config) {
|
|
12796
13627
|
const tools = [
|
|
12797
13628
|
{
|
|
12798
|
-
name: "
|
|
13629
|
+
name: "sovereignty_audit",
|
|
12799
13630
|
description: "Audit your agent's sovereignty posture. Inspects the local environment for encryption, identity, approval gates, selective disclosure, and reputation \u2014 including OpenClaw-specific configurations. Returns a scored gap analysis with prioritized recommendations.",
|
|
12800
13631
|
inputSchema: {
|
|
12801
13632
|
type: "object",
|
|
@@ -12823,6 +13654,302 @@ function createAuditTools(config) {
|
|
|
12823
13654
|
return { tools };
|
|
12824
13655
|
}
|
|
12825
13656
|
|
|
13657
|
+
// src/audit/siem-formatter.ts
|
|
13658
|
+
function parseGateDecision(details) {
|
|
13659
|
+
if (!details || typeof details.gate_decision !== "string") {
|
|
13660
|
+
return "auto-allow";
|
|
13661
|
+
}
|
|
13662
|
+
const decision = details.gate_decision.toLowerCase();
|
|
13663
|
+
if (decision === "approve" || decision === "deny") {
|
|
13664
|
+
return decision;
|
|
13665
|
+
}
|
|
13666
|
+
return "auto-allow";
|
|
13667
|
+
}
|
|
13668
|
+
function parseTier(details) {
|
|
13669
|
+
if (!details || typeof details.tier !== "number") {
|
|
13670
|
+
return 3;
|
|
13671
|
+
}
|
|
13672
|
+
return Math.max(1, Math.min(3, details.tier));
|
|
13673
|
+
}
|
|
13674
|
+
function parseSessionId(details) {
|
|
13675
|
+
if (!details || typeof details.session_id !== "string") {
|
|
13676
|
+
return "unknown";
|
|
13677
|
+
}
|
|
13678
|
+
return details.session_id;
|
|
13679
|
+
}
|
|
13680
|
+
function parseAgentDid(details) {
|
|
13681
|
+
if (!details || typeof details.agent_did !== "string") {
|
|
13682
|
+
return "unknown";
|
|
13683
|
+
}
|
|
13684
|
+
return details.agent_did;
|
|
13685
|
+
}
|
|
13686
|
+
function gateToCEFSeverity(decision, tier) {
|
|
13687
|
+
if (decision === "deny") {
|
|
13688
|
+
return 8;
|
|
13689
|
+
}
|
|
13690
|
+
if (decision === "approve") {
|
|
13691
|
+
if (tier === 1) return 5;
|
|
13692
|
+
if (tier === 2) return 3;
|
|
13693
|
+
}
|
|
13694
|
+
return 1;
|
|
13695
|
+
}
|
|
13696
|
+
function formatAsCEF(entry, options) {
|
|
13697
|
+
const version = "0";
|
|
13698
|
+
const vendor = "Sanctuary";
|
|
13699
|
+
const product = "MCP-Server";
|
|
13700
|
+
const productVersion = "0.7.0";
|
|
13701
|
+
const decision = parseGateDecision(entry.details);
|
|
13702
|
+
const tier = parseTier(entry.details);
|
|
13703
|
+
const sessionId = parseSessionId(entry.details);
|
|
13704
|
+
const agentDid = parseAgentDid(entry.details);
|
|
13705
|
+
const severity = gateToCEFSeverity(decision, tier);
|
|
13706
|
+
const signatureId = entry.operation.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
13707
|
+
const description = `Sanctuary ${entry.operation}`;
|
|
13708
|
+
const extensions = [
|
|
13709
|
+
`src=${agentDid}`,
|
|
13710
|
+
`act=${entry.operation}`,
|
|
13711
|
+
`outcome=${decision}`,
|
|
13712
|
+
`tier=${tier}`,
|
|
13713
|
+
`cs1=${sessionId}`,
|
|
13714
|
+
`cs1Label=SessionId`,
|
|
13715
|
+
`rt=${new Date(entry.timestamp).getTime()}`,
|
|
13716
|
+
`layer=${entry.layer}`,
|
|
13717
|
+
`result=${entry.result}`
|
|
13718
|
+
];
|
|
13719
|
+
return `CEF:${version}|${vendor}|${product}|${productVersion}|${signatureId}|${description}|${severity}|${extensions.join(" ")}`;
|
|
13720
|
+
}
|
|
13721
|
+
function gateToOCSFStatus(decision, result) {
|
|
13722
|
+
return decision === "deny" || result === "failure" ? 2 : 1;
|
|
13723
|
+
}
|
|
13724
|
+
function gateToCOCSFSeverity(decision, tier) {
|
|
13725
|
+
if (decision === "deny") {
|
|
13726
|
+
return 4;
|
|
13727
|
+
}
|
|
13728
|
+
if (decision === "approve") {
|
|
13729
|
+
if (tier === 1) return 3;
|
|
13730
|
+
if (tier === 2) return 2;
|
|
13731
|
+
}
|
|
13732
|
+
return 1;
|
|
13733
|
+
}
|
|
13734
|
+
function gateToOCSFDisposition(decision) {
|
|
13735
|
+
return decision === "deny" ? 2 : 1;
|
|
13736
|
+
}
|
|
13737
|
+
function formatAsOCSF(entry) {
|
|
13738
|
+
const decision = parseGateDecision(entry.details);
|
|
13739
|
+
const tier = parseTier(entry.details);
|
|
13740
|
+
const agentDid = parseAgentDid(entry.details);
|
|
13741
|
+
const timestamp = new Date(entry.timestamp).getTime();
|
|
13742
|
+
const statusId = gateToOCSFStatus(decision, entry.result);
|
|
13743
|
+
const severityId = gateToCOCSFSeverity(decision, tier);
|
|
13744
|
+
const dispositionId = gateToOCSFDisposition(decision);
|
|
13745
|
+
return {
|
|
13746
|
+
class_uid: 3001,
|
|
13747
|
+
class_name: "API Activity",
|
|
13748
|
+
category_uid: 3,
|
|
13749
|
+
category_name: "Application Activity",
|
|
13750
|
+
severity_id: severityId,
|
|
13751
|
+
time: timestamp,
|
|
13752
|
+
activity_id: 1,
|
|
13753
|
+
activity_name: "API Call",
|
|
13754
|
+
actor: {
|
|
13755
|
+
user: {
|
|
13756
|
+
uid: agentDid
|
|
13757
|
+
}
|
|
13758
|
+
},
|
|
13759
|
+
api: {
|
|
13760
|
+
operation: entry.operation,
|
|
13761
|
+
service: {
|
|
13762
|
+
name: "sanctuary-mcp"
|
|
13763
|
+
}
|
|
13764
|
+
},
|
|
13765
|
+
status_id: statusId,
|
|
13766
|
+
disposition_id: dispositionId,
|
|
13767
|
+
metadata: {
|
|
13768
|
+
version: "1.3.0",
|
|
13769
|
+
product: {
|
|
13770
|
+
name: "Sanctuary Framework",
|
|
13771
|
+
vendor_name: "Erik Newton",
|
|
13772
|
+
version: "0.7.0"
|
|
13773
|
+
}
|
|
13774
|
+
}
|
|
13775
|
+
};
|
|
13776
|
+
}
|
|
13777
|
+
|
|
13778
|
+
// src/audit/siem-tools.ts
|
|
13779
|
+
function createSIEMTools(auditLog) {
|
|
13780
|
+
const tools = [
|
|
13781
|
+
{
|
|
13782
|
+
name: "audit_export_siem",
|
|
13783
|
+
description: "Export audit log events in SIEM-standard formats (CEF or OCSF) for ingestion into Splunk, Datadog, QRadar, and other security information and event management (SIEM) platforms. Encrypted audit entries are decrypted and formatted according to your chosen standard. Tier 2 \u2014 may contain sensitive operation metadata.",
|
|
13784
|
+
inputSchema: {
|
|
13785
|
+
type: "object",
|
|
13786
|
+
properties: {
|
|
13787
|
+
format: {
|
|
13788
|
+
type: "string",
|
|
13789
|
+
enum: ["cef", "ocsf"],
|
|
13790
|
+
description: 'Output format: "cef" (Common Event Format, newline-delimited) or "ocsf" (Open Cybersecurity Schema Framework, JSON array)'
|
|
13791
|
+
},
|
|
13792
|
+
since: {
|
|
13793
|
+
type: "string",
|
|
13794
|
+
description: "Optional ISO 8601 timestamp. Export only events on or after this time. Defaults to 24 hours ago."
|
|
13795
|
+
},
|
|
13796
|
+
until: {
|
|
13797
|
+
type: "string",
|
|
13798
|
+
description: "Optional ISO 8601 timestamp. Export only events before this time. Defaults to now."
|
|
13799
|
+
},
|
|
13800
|
+
limit: {
|
|
13801
|
+
type: "number",
|
|
13802
|
+
description: "Maximum number of events to export (default 100, max 1000). Set to 1000 for bulk exports to SIEMs."
|
|
13803
|
+
},
|
|
13804
|
+
filter_tool: {
|
|
13805
|
+
type: "string",
|
|
13806
|
+
description: 'Optional. Export only events from this tool name (e.g., "sovereignty_audit", "state_set"). Case-insensitive substring matching.'
|
|
13807
|
+
},
|
|
13808
|
+
filter_decision: {
|
|
13809
|
+
type: "string",
|
|
13810
|
+
enum: ["approve", "deny", "auto-allow"],
|
|
13811
|
+
description: 'Optional. Export only events with this gate decision: "approve" (manual approval), "deny" (blocked), or "auto-allow" (Tier 3 auto-allowed).'
|
|
13812
|
+
},
|
|
13813
|
+
filter_layer: {
|
|
13814
|
+
type: "string",
|
|
13815
|
+
enum: ["l1", "l2", "l3", "l4"],
|
|
13816
|
+
description: "Optional. Export only events from this sovereignty layer (L1=Cognitive, L2=Operational, L3=Disclosure, L4=Reputation)."
|
|
13817
|
+
},
|
|
13818
|
+
filter_result: {
|
|
13819
|
+
type: "string",
|
|
13820
|
+
enum: ["success", "failure"],
|
|
13821
|
+
description: 'Optional. Export only events with this result: "success" or "failure".'
|
|
13822
|
+
}
|
|
13823
|
+
},
|
|
13824
|
+
required: ["format"]
|
|
13825
|
+
},
|
|
13826
|
+
handler: async (args) => {
|
|
13827
|
+
const format = String(args.format || "").toLowerCase();
|
|
13828
|
+
if (format !== "cef" && format !== "ocsf") {
|
|
13829
|
+
return {
|
|
13830
|
+
content: [
|
|
13831
|
+
{
|
|
13832
|
+
type: "text",
|
|
13833
|
+
text: JSON.stringify({
|
|
13834
|
+
error: "Invalid format. Must be 'cef' or 'ocsf'."
|
|
13835
|
+
})
|
|
13836
|
+
}
|
|
13837
|
+
]
|
|
13838
|
+
};
|
|
13839
|
+
}
|
|
13840
|
+
let since;
|
|
13841
|
+
if (args.since) {
|
|
13842
|
+
since = String(args.since);
|
|
13843
|
+
const sinceDate = new Date(since);
|
|
13844
|
+
if (isNaN(sinceDate.getTime())) {
|
|
13845
|
+
return {
|
|
13846
|
+
content: [
|
|
13847
|
+
{
|
|
13848
|
+
type: "text",
|
|
13849
|
+
text: JSON.stringify({
|
|
13850
|
+
error: `Invalid 'since' timestamp: ${since}. Must be ISO 8601.`
|
|
13851
|
+
})
|
|
13852
|
+
}
|
|
13853
|
+
]
|
|
13854
|
+
};
|
|
13855
|
+
}
|
|
13856
|
+
} else {
|
|
13857
|
+
const now = /* @__PURE__ */ new Date();
|
|
13858
|
+
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
|
|
13859
|
+
since = oneDayAgo.toISOString();
|
|
13860
|
+
}
|
|
13861
|
+
let until;
|
|
13862
|
+
if (args.until) {
|
|
13863
|
+
until = String(args.until);
|
|
13864
|
+
const untilDate = new Date(until);
|
|
13865
|
+
if (isNaN(untilDate.getTime())) {
|
|
13866
|
+
return {
|
|
13867
|
+
content: [
|
|
13868
|
+
{
|
|
13869
|
+
type: "text",
|
|
13870
|
+
text: JSON.stringify({
|
|
13871
|
+
error: `Invalid 'until' timestamp: ${until}. Must be ISO 8601.`
|
|
13872
|
+
})
|
|
13873
|
+
}
|
|
13874
|
+
]
|
|
13875
|
+
};
|
|
13876
|
+
}
|
|
13877
|
+
}
|
|
13878
|
+
let limit = 100;
|
|
13879
|
+
if (typeof args.limit === "number") {
|
|
13880
|
+
limit = Math.max(1, Math.min(1e3, args.limit));
|
|
13881
|
+
}
|
|
13882
|
+
const filterTool = args.filter_tool ? String(args.filter_tool).toLowerCase() : void 0;
|
|
13883
|
+
const filterDecision = args.filter_decision ? String(args.filter_decision).toLowerCase() : void 0;
|
|
13884
|
+
const filterLayer = args.filter_layer ? String(args.filter_layer).toLowerCase() : void 0;
|
|
13885
|
+
const filterResult = args.filter_result ? String(args.filter_result).toLowerCase() : void 0;
|
|
13886
|
+
const result = await auditLog.query({
|
|
13887
|
+
since,
|
|
13888
|
+
layer: filterLayer,
|
|
13889
|
+
operation_type: void 0,
|
|
13890
|
+
// Will filter after
|
|
13891
|
+
limit
|
|
13892
|
+
});
|
|
13893
|
+
let filtered = result.entries;
|
|
13894
|
+
if (filterTool) {
|
|
13895
|
+
filtered = filtered.filter(
|
|
13896
|
+
(e) => e.operation.toLowerCase().includes(filterTool)
|
|
13897
|
+
);
|
|
13898
|
+
}
|
|
13899
|
+
if (filterDecision) {
|
|
13900
|
+
filtered = filtered.filter((e) => {
|
|
13901
|
+
const decision = String(e.details?.gate_decision || "auto-allow").toLowerCase();
|
|
13902
|
+
return decision === filterDecision;
|
|
13903
|
+
});
|
|
13904
|
+
}
|
|
13905
|
+
if (filterResult) {
|
|
13906
|
+
filtered = filtered.filter((e) => e.result === filterResult);
|
|
13907
|
+
}
|
|
13908
|
+
if (until) {
|
|
13909
|
+
const untilDate = new Date(until);
|
|
13910
|
+
filtered = filtered.filter((e) => new Date(e.timestamp) < untilDate);
|
|
13911
|
+
}
|
|
13912
|
+
let output;
|
|
13913
|
+
if (format === "cef") {
|
|
13914
|
+
const cefLines = filtered.map((entry) => formatAsCEF(entry));
|
|
13915
|
+
output = cefLines.join("\n");
|
|
13916
|
+
} else {
|
|
13917
|
+
const ocsfObjects = filtered.map((entry) => formatAsOCSF(entry));
|
|
13918
|
+
output = JSON.stringify(ocsfObjects, null, 2);
|
|
13919
|
+
}
|
|
13920
|
+
return {
|
|
13921
|
+
content: [
|
|
13922
|
+
{
|
|
13923
|
+
type: "text",
|
|
13924
|
+
text: JSON.stringify({
|
|
13925
|
+
format,
|
|
13926
|
+
count: filtered.length,
|
|
13927
|
+
total_available: result.total,
|
|
13928
|
+
time_range: {
|
|
13929
|
+
since,
|
|
13930
|
+
until: until || (/* @__PURE__ */ new Date()).toISOString()
|
|
13931
|
+
},
|
|
13932
|
+
filters: {
|
|
13933
|
+
tool: filterTool,
|
|
13934
|
+
decision: filterDecision,
|
|
13935
|
+
layer: filterLayer,
|
|
13936
|
+
result: filterResult
|
|
13937
|
+
},
|
|
13938
|
+
note: format === "cef" ? `${filtered.length} CEF events (newline-delimited). Each line is a complete CEF event.` : `${filtered.length} OCSF objects in JSON array format.`
|
|
13939
|
+
})
|
|
13940
|
+
},
|
|
13941
|
+
{
|
|
13942
|
+
type: "text",
|
|
13943
|
+
text: output
|
|
13944
|
+
}
|
|
13945
|
+
]
|
|
13946
|
+
};
|
|
13947
|
+
}
|
|
13948
|
+
}
|
|
13949
|
+
];
|
|
13950
|
+
return { tools };
|
|
13951
|
+
}
|
|
13952
|
+
|
|
12826
13953
|
// src/l2-operational/context-gate.ts
|
|
12827
13954
|
init_encryption();
|
|
12828
13955
|
init_encoding();
|
|
@@ -13819,13 +14946,17 @@ var ContextGateEnforcer = class {
|
|
|
13819
14946
|
* Check if a tool should be filtered based on bypass prefixes.
|
|
13820
14947
|
*
|
|
13821
14948
|
* SEC-033: Uses exact namespace component matching, not bare startsWith().
|
|
13822
|
-
* A prefix of "
|
|
13823
|
-
*
|
|
13824
|
-
*
|
|
13825
|
-
*
|
|
14949
|
+
* A prefix of "proxy/" matches "proxy/server/tool" but NOT "proxyevil/steal".
|
|
14950
|
+
* The prefix must match exactly up to its length, and the prefix must end
|
|
14951
|
+
* with "/" to enforce namespace boundaries (if it doesn't, we add one).
|
|
14952
|
+
*
|
|
14953
|
+
* Special sentinel: "*" bypasses ALL tools (used when all Sanctuary-internal
|
|
14954
|
+
* tools should skip context gating — the default). Only proxy/external tools
|
|
14955
|
+
* should be filtered in production.
|
|
13826
14956
|
*/
|
|
13827
14957
|
shouldFilter(toolName) {
|
|
13828
14958
|
for (const prefix of this.config.bypass_prefixes) {
|
|
14959
|
+
if (prefix === "*") return false;
|
|
13829
14960
|
const safePrefix = prefix.endsWith("/") ? prefix : prefix + "/";
|
|
13830
14961
|
if (toolName === safePrefix.slice(0, -1) || toolName.startsWith(safePrefix)) {
|
|
13831
14962
|
return false;
|
|
@@ -13922,8 +15053,8 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
13922
15053
|
const enforcerConfig = {
|
|
13923
15054
|
enabled: false,
|
|
13924
15055
|
// Off by default; agents must explicitly enable it
|
|
13925
|
-
bypass_prefixes: ["
|
|
13926
|
-
// Skip internal tools
|
|
15056
|
+
bypass_prefixes: ["*"],
|
|
15057
|
+
// Skip all Sanctuary-internal tools; only proxy/ tools get filtered
|
|
13927
15058
|
log_only: false,
|
|
13928
15059
|
// Filter immediately
|
|
13929
15060
|
on_deny: "block"
|
|
@@ -13933,7 +15064,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
13933
15064
|
const tools = [
|
|
13934
15065
|
// ── Set Policy ──────────────────────────────────────────────────
|
|
13935
15066
|
{
|
|
13936
|
-
name: "
|
|
15067
|
+
name: "context_gate_set_policy",
|
|
13937
15068
|
description: "Create a context-gating policy that controls what information flows to remote providers (LLM APIs, tool APIs, logging services). Each rule specifies a provider category and which context fields to allow, redact, hash, or flag for summarization. Redact rules take absolute priority \u2014 if a field is in both 'allow' and 'redact', it is redacted. Default action applies to any field not mentioned in any rule. Use this to prevent your full agent context from being sent to remote LLM providers during inference calls.",
|
|
13938
15069
|
inputSchema: {
|
|
13939
15070
|
type: "object",
|
|
@@ -14042,13 +15173,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14042
15173
|
rules: policy.rules,
|
|
14043
15174
|
default_action: policy.default_action,
|
|
14044
15175
|
created_at: policy.created_at,
|
|
14045
|
-
message: "Context-gating policy created. Use
|
|
15176
|
+
message: "Context-gating policy created. Use context_gate_filter to apply this policy before making outbound calls."
|
|
14046
15177
|
});
|
|
14047
15178
|
}
|
|
14048
15179
|
},
|
|
14049
15180
|
// ── Apply Template ───────────────────────────────────────────────
|
|
14050
15181
|
{
|
|
14051
|
-
name: "
|
|
15182
|
+
name: "context_gate_apply_template",
|
|
14052
15183
|
description: "Apply a starter context-gating template. Available templates: inference-minimal (strictest \u2014 only task and query pass through), inference-standard (balanced \u2014 adds tool results, summarizes history), logging-strict (redacts all content for telemetry services), tool-api-scoped (allows tool parameters, redacts agent state). Templates are starting points \u2014 customize after applying.",
|
|
14053
15184
|
inputSchema: {
|
|
14054
15185
|
type: "object",
|
|
@@ -14097,13 +15228,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14097
15228
|
rules: policy.rules,
|
|
14098
15229
|
default_action: policy.default_action,
|
|
14099
15230
|
created_at: policy.created_at,
|
|
14100
|
-
message: "Template applied. Use
|
|
15231
|
+
message: "Template applied. Use context_gate_filter with this policy_id to filter context before outbound calls. Customize rules with context_gate_set_policy if needed."
|
|
14101
15232
|
});
|
|
14102
15233
|
}
|
|
14103
15234
|
},
|
|
14104
15235
|
// ── Recommend Policy ────────────────────────────────────────────
|
|
14105
15236
|
{
|
|
14106
|
-
name: "
|
|
15237
|
+
name: "context_gate_recommend",
|
|
14107
15238
|
description: "Analyze a sample context object and recommend a context-gating policy based on field name heuristics. Classifies each field as allow, redact, hash, or summarize with confidence levels. Returns a ready-to-apply rule set. When in doubt, recommends redact (conservative). Review the recommendations before applying.",
|
|
14108
15239
|
inputSchema: {
|
|
14109
15240
|
type: "object",
|
|
@@ -14140,7 +15271,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14140
15271
|
});
|
|
14141
15272
|
return toolResult({
|
|
14142
15273
|
...recommendation,
|
|
14143
|
-
next_steps: "Review the classifications above. If they look correct, you can apply them directly with
|
|
15274
|
+
next_steps: "Review the classifications above. If they look correct, you can apply them directly with context_gate_set_policy using the recommended_rules. Or start with a template via context_gate_apply_template and customize from there.",
|
|
14144
15275
|
available_templates: listTemplateIds().map((id) => {
|
|
14145
15276
|
const t = TEMPLATES[id];
|
|
14146
15277
|
return { id, name: t.name, description: t.description };
|
|
@@ -14150,7 +15281,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14150
15281
|
},
|
|
14151
15282
|
// ── Filter Context ──────────────────────────────────────────────
|
|
14152
15283
|
{
|
|
14153
|
-
name: "
|
|
15284
|
+
name: "context_gate_filter",
|
|
14154
15285
|
description: "Filter agent context through a gating policy before sending to a remote provider. Returns per-field decisions (allow, redact, hash, summarize) and content hashes for the audit trail. Call this BEFORE making any outbound API call to ensure you are only sending the minimum necessary context. The filtered output tells you exactly what can be sent safely.",
|
|
14155
15286
|
inputSchema: {
|
|
14156
15287
|
type: "object",
|
|
@@ -14256,7 +15387,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14256
15387
|
},
|
|
14257
15388
|
// ── List Policies ───────────────────────────────────────────────
|
|
14258
15389
|
{
|
|
14259
|
-
name: "
|
|
15390
|
+
name: "context_gate_list_policies",
|
|
14260
15391
|
description: "List all configured context-gating policies. Returns policy IDs, names, rule summaries, and default actions.",
|
|
14261
15392
|
inputSchema: {
|
|
14262
15393
|
type: "object",
|
|
@@ -14279,13 +15410,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14279
15410
|
updated_at: p.updated_at
|
|
14280
15411
|
})),
|
|
14281
15412
|
count: policies.length,
|
|
14282
|
-
message: policies.length === 0 ? "No context-gating policies configured. Use
|
|
15413
|
+
message: policies.length === 0 ? "No context-gating policies configured. Use context_gate_set_policy to create one." : `${policies.length} context-gating ${policies.length === 1 ? "policy" : "policies"} configured.`
|
|
14283
15414
|
});
|
|
14284
15415
|
}
|
|
14285
15416
|
},
|
|
14286
15417
|
// ── Enforcer Status ─────────────────────────────────────────────────
|
|
14287
15418
|
{
|
|
14288
|
-
name: "
|
|
15419
|
+
name: "context_gate_enforcer_status",
|
|
14289
15420
|
description: "Get the status of the automatic context gate enforcer, including enabled/disabled state, log_only mode, active policy, and statistics. The enforcer automatically filters tool arguments when enabled. Use this to monitor what the enforcer has been filtering.",
|
|
14290
15421
|
inputSchema: {
|
|
14291
15422
|
type: "object",
|
|
@@ -14306,13 +15437,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14306
15437
|
return toolResult({
|
|
14307
15438
|
enforcer_status: status,
|
|
14308
15439
|
description: "The enforcer is " + (status.enabled ? "enabled" : "disabled") + ". " + (status.log_only ? "Currently in log_only mode \u2014 filtering is logged but not applied." : "Filtering is actively applied to tool arguments."),
|
|
14309
|
-
guidance: status.stats.calls_inspected > 0 ? `Over ${status.stats.calls_inspected} tool calls, ${status.stats.fields_redacted} sensitive fields were redacted. Use
|
|
15440
|
+
guidance: status.stats.calls_inspected > 0 ? `Over ${status.stats.calls_inspected} tool calls, ${status.stats.fields_redacted} sensitive fields were redacted. Use context_gate_enforcer_configure to adjust settings.` : "No tool calls have been inspected yet."
|
|
14310
15441
|
});
|
|
14311
15442
|
}
|
|
14312
15443
|
},
|
|
14313
15444
|
// ── Enforcer Configuration ──────────────────────────────────────────
|
|
14314
15445
|
{
|
|
14315
|
-
name: "
|
|
15446
|
+
name: "context_gate_enforcer_configure",
|
|
14316
15447
|
description: "Configure the automatic context gate enforcer. Control whether it filters tool arguments, toggle log_only mode for gradual rollout, set the active policy, and choose what to do when denied fields are encountered (block the request or redact the field). Use this to enable automatic context protection.",
|
|
14317
15448
|
inputSchema: {
|
|
14318
15449
|
type: "object",
|
|
@@ -14635,7 +15766,7 @@ function assessL2Hardening(storagePath) {
|
|
|
14635
15766
|
function createL2HardeningTools(storagePath, auditLog) {
|
|
14636
15767
|
return [
|
|
14637
15768
|
{
|
|
14638
|
-
name: "
|
|
15769
|
+
name: "l2_hardening_status",
|
|
14639
15770
|
description: "L2 Process Hardening Status \u2014 Verify software-based operational isolation. Reports memory protection, process isolation level, filesystem permissions, and overall hardening assessment. Read-only. Tier 3 \u2014 always allowed.",
|
|
14640
15771
|
inputSchema: {
|
|
14641
15772
|
type: "object",
|
|
@@ -14703,7 +15834,7 @@ function createL2HardeningTools(storagePath, auditLog) {
|
|
|
14703
15834
|
}
|
|
14704
15835
|
},
|
|
14705
15836
|
{
|
|
14706
|
-
name: "
|
|
15837
|
+
name: "l2_verify_isolation",
|
|
14707
15838
|
description: "Verify L2 process isolation at runtime. Checks whether the Sanctuary server is running in an isolated environment (container, VM, sandbox) and validates filesystem and memory protections. Reports isolation level and any issues. Read-only. Tier 3 \u2014 always allowed.",
|
|
14708
15839
|
inputSchema: {
|
|
14709
15840
|
type: "object",
|
|
@@ -14982,7 +16113,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
14982
16113
|
const tools = [
|
|
14983
16114
|
// ── Get Profile ──────────────────────────────────────────────────
|
|
14984
16115
|
{
|
|
14985
|
-
name: "
|
|
16116
|
+
name: "sovereignty_profile_get",
|
|
14986
16117
|
description: "Get the current Sovereignty Profile \u2014 shows which Sanctuary features are active (audit logging, injection detection, context gating, approval gates, ZK proofs) and their configuration.",
|
|
14987
16118
|
inputSchema: {
|
|
14988
16119
|
type: "object",
|
|
@@ -15001,7 +16132,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
15001
16132
|
},
|
|
15002
16133
|
// ── Update Profile ───────────────────────────────────────────────
|
|
15003
16134
|
{
|
|
15004
|
-
name: "
|
|
16135
|
+
name: "sovereignty_profile_update",
|
|
15005
16136
|
description: "Update the Sovereignty Profile feature toggles. This changes which Sanctuary protections are active. Requires human approval (Tier 1) because it modifies enforcement behavior. Pass only the features you want to change \u2014 unspecified features remain unchanged.",
|
|
15006
16137
|
inputSchema: {
|
|
15007
16138
|
type: "object",
|
|
@@ -15082,7 +16213,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
15082
16213
|
},
|
|
15083
16214
|
// ── Generate System Prompt ───────────────────────────────────────
|
|
15084
16215
|
{
|
|
15085
|
-
name: "
|
|
16216
|
+
name: "sovereignty_profile_generate_prompt",
|
|
15086
16217
|
description: "Generate a system prompt snippet based on the active Sovereignty Profile. The snippet instructs an agent on which Sanctuary features are active and how to use them. Copy and paste this into your agent's system configuration.",
|
|
15087
16218
|
inputSchema: {
|
|
15088
16219
|
type: "object",
|
|
@@ -15476,6 +16607,7 @@ var ProxyRouter = class {
|
|
|
15476
16607
|
confidence: injectionResult.confidence,
|
|
15477
16608
|
latency_ms: Date.now() - start
|
|
15478
16609
|
}, "failure");
|
|
16610
|
+
this.notifyProxyCall(proxyName, serverName, "blocked", "injection_detected", tier);
|
|
15479
16611
|
return toolResult({
|
|
15480
16612
|
error: "Operation not permitted",
|
|
15481
16613
|
proxy: true
|
|
@@ -15506,6 +16638,7 @@ var ProxyRouter = class {
|
|
|
15506
16638
|
reason: govResult.reason,
|
|
15507
16639
|
latency_ms: Date.now() - start
|
|
15508
16640
|
}, "failure");
|
|
16641
|
+
this.notifyProxyCall(proxyName, serverName, "blocked", govResult.reason, tier);
|
|
15509
16642
|
return toolResult({
|
|
15510
16643
|
error: "Operation not permitted",
|
|
15511
16644
|
proxy: true,
|
|
@@ -15540,6 +16673,7 @@ var ProxyRouter = class {
|
|
|
15540
16673
|
decision: "allowed",
|
|
15541
16674
|
latency_ms: latencyMs
|
|
15542
16675
|
});
|
|
16676
|
+
this.notifyProxyCall(proxyName, serverName, "allowed", void 0, tier);
|
|
15543
16677
|
return this.normalizeResponse(result);
|
|
15544
16678
|
} catch (err) {
|
|
15545
16679
|
const latencyMs = Date.now() - start;
|
|
@@ -15559,6 +16693,7 @@ var ProxyRouter = class {
|
|
|
15559
16693
|
error: errorMessage,
|
|
15560
16694
|
latency_ms: latencyMs
|
|
15561
16695
|
}, "failure");
|
|
16696
|
+
this.notifyProxyCall(proxyName, serverName, "error", errorMessage, tier);
|
|
15562
16697
|
return {
|
|
15563
16698
|
content: [{
|
|
15564
16699
|
type: "text",
|
|
@@ -15573,6 +16708,24 @@ var ProxyRouter = class {
|
|
|
15573
16708
|
}
|
|
15574
16709
|
};
|
|
15575
16710
|
}
|
|
16711
|
+
/**
|
|
16712
|
+
* Notify the onProxyCall callback if configured.
|
|
16713
|
+
*/
|
|
16714
|
+
notifyProxyCall(tool, server, decision, reason, tier) {
|
|
16715
|
+
if (this.options.onProxyCall) {
|
|
16716
|
+
try {
|
|
16717
|
+
this.options.onProxyCall({
|
|
16718
|
+
tool,
|
|
16719
|
+
server,
|
|
16720
|
+
decision,
|
|
16721
|
+
reason,
|
|
16722
|
+
tier,
|
|
16723
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16724
|
+
});
|
|
16725
|
+
} catch {
|
|
16726
|
+
}
|
|
16727
|
+
}
|
|
16728
|
+
}
|
|
15576
16729
|
/**
|
|
15577
16730
|
* Call an upstream tool with a timeout.
|
|
15578
16731
|
*/
|
|
@@ -15845,7 +16998,7 @@ function createGovernorTools(governor, auditLog) {
|
|
|
15845
16998
|
const tools = [
|
|
15846
16999
|
// ── Governor Status ─────────────────────────────────────────────
|
|
15847
17000
|
{
|
|
15848
|
-
name: "
|
|
17001
|
+
name: "governor_status",
|
|
15849
17002
|
description: "View the current Call Governor status including volume counters, per-tool rate counts, duplicate cache size, and lifetime counter. Use this to monitor tool call consumption, detect potential loops, and check how close you are to governance limits. The governor protects against runaway tool calls by enforcing volume limits, rate limits, duplicate detection, and a session lifetime cap.",
|
|
15850
17003
|
inputSchema: {
|
|
15851
17004
|
type: "object",
|
|
@@ -15885,7 +17038,7 @@ function createGovernorTools(governor, auditLog) {
|
|
|
15885
17038
|
},
|
|
15886
17039
|
// ── Governor Reset ──────────────────────────────────────────────
|
|
15887
17040
|
{
|
|
15888
|
-
name: "
|
|
17041
|
+
name: "governor_reset",
|
|
15889
17042
|
description: "Reset all Call Governor counters: volume window, per-tool rate windows, duplicate cache, and lifetime counter. This clears the hard stop if the lifetime limit was reached. This is a Tier 1 operation \u2014 requires human approval because it removes all runtime governance state and could allow previously blocked behavior to resume.",
|
|
15890
17043
|
inputSchema: {
|
|
15891
17044
|
type: "object",
|
|
@@ -15975,7 +17128,7 @@ function createSanctuaryTools(opts) {
|
|
|
15975
17128
|
const tools = [
|
|
15976
17129
|
// ─── sanctuary_bootstrap ───────────────────────────────────────────
|
|
15977
17130
|
{
|
|
15978
|
-
name: "
|
|
17131
|
+
name: "sanctuary_bootstrap",
|
|
15979
17132
|
description: "One-shot bootstrap for a new sovereign agent identity. Generates an Ed25519 keypair, stores the encrypted identity, constructs a Sovereignty Health Report (SHR), and publishes it to Verascore. Returns { did, profileUrl, tier } for the newly-minted agent.",
|
|
15980
17133
|
inputSchema: {
|
|
15981
17134
|
type: "object",
|
|
@@ -16113,7 +17266,7 @@ function createSanctuaryTools(opts) {
|
|
|
16113
17266
|
},
|
|
16114
17267
|
// ─── sanctuary_policy_status ───────────────────────────────────────
|
|
16115
17268
|
{
|
|
16116
|
-
name: "
|
|
17269
|
+
name: "sanctuary_policy_status",
|
|
16117
17270
|
description: "Return a summary of the active Principal Policy: which operations require approval (Tier 1), which are subject to anomaly detection (Tier 2), and which auto-allow with audit (Tier 3).",
|
|
16118
17271
|
inputSchema: {
|
|
16119
17272
|
type: "object",
|
|
@@ -16143,7 +17296,7 @@ function createSanctuaryTools(opts) {
|
|
|
16143
17296
|
},
|
|
16144
17297
|
// ─── sanctuary_export_identity_bundle ──────────────────────────────
|
|
16145
17298
|
{
|
|
16146
|
-
name: "
|
|
17299
|
+
name: "sanctuary_export_identity_bundle",
|
|
16147
17300
|
description: "Export a signed, portable identity bundle: { publicKey, did, shr, attestations }. The bundle is signed with the identity's Ed25519 key so a recipient can verify authenticity against the public key. Private keys are never included.",
|
|
16148
17301
|
inputSchema: {
|
|
16149
17302
|
type: "object",
|
|
@@ -16212,7 +17365,7 @@ function createSanctuaryTools(opts) {
|
|
|
16212
17365
|
},
|
|
16213
17366
|
// ─── sanctuary_link_to_human ───────────────────────────────────────
|
|
16214
17367
|
{
|
|
16215
|
-
name: "
|
|
17368
|
+
name: "sanctuary_link_to_human",
|
|
16216
17369
|
description: "Trigger a Verascore magic-link login flow so a human principal can authenticate and subsequently claim this agent's DID. The email is sent by Verascore to the supplied address. This tool only initiates the flow \u2014 it does not directly bind the DID.",
|
|
16217
17370
|
inputSchema: {
|
|
16218
17371
|
type: "object",
|
|
@@ -16267,7 +17420,7 @@ function createSanctuaryTools(opts) {
|
|
|
16267
17420
|
},
|
|
16268
17421
|
// ─── sanctuary_sign_challenge ──────────────────────────────────────
|
|
16269
17422
|
{
|
|
16270
|
-
name: "
|
|
17423
|
+
name: "sanctuary_sign_challenge",
|
|
16271
17424
|
description: "Sign a domain-separated nonce with the agent's Ed25519 key. Used in DID-ownership proof flows. The signed message is constructed as: 'sanctuary-sign-challenge-v1\\x00' + purpose + '\\x00' + nonce. The verifier MUST reconstruct the same domain-prefixed message before calling Ed25519 verify \u2014 a raw-nonce signature is NOT valid for this tool. The `purpose` field binds the signature to a specific use case (e.g. 'verascore-claim') so a signature produced for one purpose cannot be replayed against a different verifier.",
|
|
16272
17425
|
inputSchema: {
|
|
16273
17426
|
type: "object",
|
|
@@ -16641,7 +17794,7 @@ async function createSanctuaryServer(options) {
|
|
|
16641
17794
|
}
|
|
16642
17795
|
const l2Tools = [
|
|
16643
17796
|
{
|
|
16644
|
-
name: "
|
|
17797
|
+
name: "exec_attest",
|
|
16645
17798
|
description: "Generate an attestation of the current execution environment, including sovereignty assessment and degradation report.",
|
|
16646
17799
|
inputSchema: {
|
|
16647
17800
|
type: "object",
|
|
@@ -16692,7 +17845,7 @@ async function createSanctuaryServer(options) {
|
|
|
16692
17845
|
}
|
|
16693
17846
|
},
|
|
16694
17847
|
{
|
|
16695
|
-
name: "
|
|
17848
|
+
name: "monitor_health",
|
|
16696
17849
|
description: "Sanctuary Health Report (SHR) \u2014 standardized sovereignty status.",
|
|
16697
17850
|
inputSchema: { type: "object", properties: {} },
|
|
16698
17851
|
handler: async () => {
|
|
@@ -16741,7 +17894,7 @@ async function createSanctuaryServer(options) {
|
|
|
16741
17894
|
}
|
|
16742
17895
|
},
|
|
16743
17896
|
{
|
|
16744
|
-
name: "
|
|
17897
|
+
name: "monitor_audit_log",
|
|
16745
17898
|
description: "Query the sovereignty audit log.",
|
|
16746
17899
|
inputSchema: {
|
|
16747
17900
|
type: "object",
|
|
@@ -16767,7 +17920,7 @@ async function createSanctuaryServer(options) {
|
|
|
16767
17920
|
}
|
|
16768
17921
|
];
|
|
16769
17922
|
const manifestTool = {
|
|
16770
|
-
name: "
|
|
17923
|
+
name: "manifest",
|
|
16771
17924
|
description: "Generate the Sanctuary Interface Manifest (SIM) \u2014 a machine-readable declaration of this server's capabilities.",
|
|
16772
17925
|
inputSchema: { type: "object", properties: {} },
|
|
16773
17926
|
handler: async () => {
|
|
@@ -16879,6 +18032,7 @@ async function createSanctuaryServer(options) {
|
|
|
16879
18032
|
handshakeResults
|
|
16880
18033
|
);
|
|
16881
18034
|
const { tools: auditTools } = createAuditTools(config);
|
|
18035
|
+
const { tools: siemTools } = createSIEMTools(auditLog);
|
|
16882
18036
|
const { tools: contextGateTools, enforcer: contextGateEnforcer } = createContextGateTools(storage, masterKey, auditLog);
|
|
16883
18037
|
const hardeningTools = createL2HardeningTools(config.storage_path, auditLog);
|
|
16884
18038
|
const profileStore = new SovereigntyProfileStore(storage, masterKey);
|
|
@@ -16961,7 +18115,7 @@ async function createSanctuaryServer(options) {
|
|
|
16961
18115
|
const dashboardTools = [];
|
|
16962
18116
|
if (dashboard) {
|
|
16963
18117
|
dashboardTools.push({
|
|
16964
|
-
name: "
|
|
18118
|
+
name: "dashboard_open",
|
|
16965
18119
|
description: "Generate a one-click URL to open the Principal Dashboard in a browser. Returns a pre-authenticated link \u2014 no manual token entry needed.",
|
|
16966
18120
|
inputSchema: {
|
|
16967
18121
|
type: "object",
|
|
@@ -16995,6 +18149,7 @@ async function createSanctuaryServer(options) {
|
|
|
16995
18149
|
...federationTools,
|
|
16996
18150
|
...bridgeTools,
|
|
16997
18151
|
...auditTools,
|
|
18152
|
+
...siemTools,
|
|
16998
18153
|
...contextGateTools,
|
|
16999
18154
|
...hardeningTools,
|
|
17000
18155
|
...profileTools,
|
|
@@ -17041,7 +18196,12 @@ async function createSanctuaryServer(options) {
|
|
|
17041
18196
|
}
|
|
17042
18197
|
return args;
|
|
17043
18198
|
},
|
|
17044
|
-
governor
|
|
18199
|
+
governor,
|
|
18200
|
+
onProxyCall: (data) => {
|
|
18201
|
+
if (dashboard) {
|
|
18202
|
+
dashboard.broadcastProxyCall(data);
|
|
18203
|
+
}
|
|
18204
|
+
}
|
|
17045
18205
|
}
|
|
17046
18206
|
);
|
|
17047
18207
|
clientManager.configure(enabledServers).catch((err) => {
|
|
@@ -17059,6 +18219,7 @@ async function createSanctuaryServer(options) {
|
|
|
17059
18219
|
auditLog,
|
|
17060
18220
|
clientManager
|
|
17061
18221
|
});
|
|
18222
|
+
dashboard.enableFortressView(enabledServers.length);
|
|
17062
18223
|
}
|
|
17063
18224
|
}
|
|
17064
18225
|
}
|