@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.cjs
CHANGED
|
@@ -1411,7 +1411,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1411
1411
|
const tools = [
|
|
1412
1412
|
// ── Identity Tools ──────────────────────────────────────────────────
|
|
1413
1413
|
{
|
|
1414
|
-
name: "
|
|
1414
|
+
name: "identity_create",
|
|
1415
1415
|
description: "Create a new sovereign identity (Ed25519 keypair). The private key is encrypted and never exposed.",
|
|
1416
1416
|
inputSchema: {
|
|
1417
1417
|
type: "object",
|
|
@@ -1446,7 +1446,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1446
1446
|
}
|
|
1447
1447
|
},
|
|
1448
1448
|
{
|
|
1449
|
-
name: "
|
|
1449
|
+
name: "identity_list",
|
|
1450
1450
|
description: "List all managed sovereign identities.",
|
|
1451
1451
|
inputSchema: {
|
|
1452
1452
|
type: "object",
|
|
@@ -1471,7 +1471,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1471
1471
|
}
|
|
1472
1472
|
},
|
|
1473
1473
|
{
|
|
1474
|
-
name: "
|
|
1474
|
+
name: "identity_sign",
|
|
1475
1475
|
description: "Sign data with a managed identity. The private key is decrypted in memory only during signing.",
|
|
1476
1476
|
inputSchema: {
|
|
1477
1477
|
type: "object",
|
|
@@ -1509,7 +1509,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1509
1509
|
}
|
|
1510
1510
|
},
|
|
1511
1511
|
{
|
|
1512
|
-
name: "
|
|
1512
|
+
name: "identity_verify",
|
|
1513
1513
|
description: "Verify an Ed25519 signature. Provide either identity_id or public_key.",
|
|
1514
1514
|
inputSchema: {
|
|
1515
1515
|
type: "object",
|
|
@@ -1558,7 +1558,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1558
1558
|
}
|
|
1559
1559
|
},
|
|
1560
1560
|
{
|
|
1561
|
-
name: "
|
|
1561
|
+
name: "identity_rotate",
|
|
1562
1562
|
description: "Rotate keys for an identity. Generates a new keypair and signs a rotation event with the old key for verifiable chain.",
|
|
1563
1563
|
inputSchema: {
|
|
1564
1564
|
type: "object",
|
|
@@ -1591,7 +1591,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1591
1591
|
},
|
|
1592
1592
|
// ── State Tools ─────────────────────────────────────────────────────
|
|
1593
1593
|
{
|
|
1594
|
-
name: "
|
|
1594
|
+
name: "state_write",
|
|
1595
1595
|
description: "Write encrypted state to the sovereign store. Value is encrypted with a namespace-specific key. The write is signed by the active identity.",
|
|
1596
1596
|
inputSchema: {
|
|
1597
1597
|
type: "object",
|
|
@@ -1648,7 +1648,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1648
1648
|
}
|
|
1649
1649
|
},
|
|
1650
1650
|
{
|
|
1651
|
-
name: "
|
|
1651
|
+
name: "state_read",
|
|
1652
1652
|
description: "Read and decrypt state from the sovereign store. Verifies integrity via Merkle proof and signature.",
|
|
1653
1653
|
inputSchema: {
|
|
1654
1654
|
type: "object",
|
|
@@ -1689,7 +1689,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1689
1689
|
}
|
|
1690
1690
|
},
|
|
1691
1691
|
{
|
|
1692
|
-
name: "
|
|
1692
|
+
name: "state_list",
|
|
1693
1693
|
description: "List keys in a namespace (metadata only \u2014 no decryption).",
|
|
1694
1694
|
inputSchema: {
|
|
1695
1695
|
type: "object",
|
|
@@ -1721,7 +1721,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1721
1721
|
}
|
|
1722
1722
|
},
|
|
1723
1723
|
{
|
|
1724
|
-
name: "
|
|
1724
|
+
name: "state_delete",
|
|
1725
1725
|
description: "Securely delete state. Overwrites file with random bytes before removal (right to deletion, S1.6).",
|
|
1726
1726
|
inputSchema: {
|
|
1727
1727
|
type: "object",
|
|
@@ -1753,7 +1753,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1753
1753
|
}
|
|
1754
1754
|
},
|
|
1755
1755
|
{
|
|
1756
|
-
name: "
|
|
1756
|
+
name: "state_export",
|
|
1757
1757
|
description: "Export state as an encrypted, portable bundle for migration.",
|
|
1758
1758
|
inputSchema: {
|
|
1759
1759
|
type: "object",
|
|
@@ -1773,7 +1773,7 @@ function createL1Tools(stateStore, storage, masterKey, keyProtection, auditLog)
|
|
|
1773
1773
|
}
|
|
1774
1774
|
},
|
|
1775
1775
|
{
|
|
1776
|
-
name: "
|
|
1776
|
+
name: "state_import",
|
|
1777
1777
|
description: "Import a previously exported state bundle.",
|
|
1778
1778
|
inputSchema: {
|
|
1779
1779
|
type: "object",
|
|
@@ -2394,7 +2394,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2394
2394
|
const tools = [
|
|
2395
2395
|
// ─── Commitment Schemes ───────────────────────────────────────────────
|
|
2396
2396
|
{
|
|
2397
|
-
name: "
|
|
2397
|
+
name: "proof_commitment",
|
|
2398
2398
|
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).",
|
|
2399
2399
|
inputSchema: {
|
|
2400
2400
|
type: "object",
|
|
@@ -2429,7 +2429,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2429
2429
|
}
|
|
2430
2430
|
},
|
|
2431
2431
|
{
|
|
2432
|
-
name: "
|
|
2432
|
+
name: "proof_reveal",
|
|
2433
2433
|
description: "Verify a previously committed value by revealing it with the blinding factor. Returns whether the revealed value matches the commitment.",
|
|
2434
2434
|
inputSchema: {
|
|
2435
2435
|
type: "object",
|
|
@@ -2467,7 +2467,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2467
2467
|
},
|
|
2468
2468
|
// ─── Disclosure Policies ──────────────────────────────────────────────
|
|
2469
2469
|
{
|
|
2470
|
-
name: "
|
|
2470
|
+
name: "disclosure_set_policy",
|
|
2471
2471
|
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.",
|
|
2472
2472
|
inputSchema: {
|
|
2473
2473
|
type: "object",
|
|
@@ -2542,7 +2542,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2542
2542
|
}
|
|
2543
2543
|
},
|
|
2544
2544
|
{
|
|
2545
|
-
name: "
|
|
2545
|
+
name: "disclosure_evaluate",
|
|
2546
2546
|
description: "Evaluate a disclosure request against an active policy. Returns per-field decisions: disclose, withhold, proof, or ask-principal.",
|
|
2547
2547
|
inputSchema: {
|
|
2548
2548
|
type: "object",
|
|
@@ -2618,7 +2618,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2618
2618
|
},
|
|
2619
2619
|
// ─── ZK Proof Tools ───────────────────────────────────────────────────
|
|
2620
2620
|
{
|
|
2621
|
-
name: "
|
|
2621
|
+
name: "zk_commit",
|
|
2622
2622
|
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.",
|
|
2623
2623
|
inputSchema: {
|
|
2624
2624
|
type: "object",
|
|
@@ -2649,7 +2649,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2649
2649
|
}
|
|
2650
2650
|
},
|
|
2651
2651
|
{
|
|
2652
|
-
name: "
|
|
2652
|
+
name: "zk_prove",
|
|
2653
2653
|
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.",
|
|
2654
2654
|
inputSchema: {
|
|
2655
2655
|
type: "object",
|
|
@@ -2690,7 +2690,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2690
2690
|
}
|
|
2691
2691
|
},
|
|
2692
2692
|
{
|
|
2693
|
-
name: "
|
|
2693
|
+
name: "zk_verify",
|
|
2694
2694
|
description: "Verify a zero-knowledge proof of knowledge for a Pedersen commitment. Checks that the prover knows the commitment's opening without learning anything.",
|
|
2695
2695
|
inputSchema: {
|
|
2696
2696
|
type: "object",
|
|
@@ -2718,7 +2718,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2718
2718
|
}
|
|
2719
2719
|
},
|
|
2720
2720
|
{
|
|
2721
|
-
name: "
|
|
2721
|
+
name: "zk_range_prove",
|
|
2722
2722
|
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.",
|
|
2723
2723
|
inputSchema: {
|
|
2724
2724
|
type: "object",
|
|
@@ -2768,7 +2768,7 @@ function createL3Tools(storage, masterKey, auditLog) {
|
|
|
2768
2768
|
}
|
|
2769
2769
|
},
|
|
2770
2770
|
{
|
|
2771
|
-
name: "
|
|
2771
|
+
name: "zk_range_verify",
|
|
2772
2772
|
description: "Verify a zero-knowledge range proof \u2014 confirms a committed value is within the claimed range without learning the value.",
|
|
2773
2773
|
inputSchema: {
|
|
2774
2774
|
type: "object",
|
|
@@ -3216,7 +3216,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3216
3216
|
const tools = [
|
|
3217
3217
|
// ─── Reputation Recording ─────────────────────────────────────────
|
|
3218
3218
|
{
|
|
3219
|
-
name: "
|
|
3219
|
+
name: "reputation_record",
|
|
3220
3220
|
description: "Record an interaction outcome as a signed attestation. Creates an EAS-compatible attestation signed by the specified identity.",
|
|
3221
3221
|
inputSchema: {
|
|
3222
3222
|
type: "object",
|
|
@@ -3309,7 +3309,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3309
3309
|
},
|
|
3310
3310
|
// ─── Reputation Query ─────────────────────────────────────────────
|
|
3311
3311
|
{
|
|
3312
|
-
name: "
|
|
3312
|
+
name: "reputation_query",
|
|
3313
3313
|
description: "Query aggregated reputation data with filtering. Returns summary statistics, never raw interaction details.",
|
|
3314
3314
|
inputSchema: {
|
|
3315
3315
|
type: "object",
|
|
@@ -3357,7 +3357,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3357
3357
|
},
|
|
3358
3358
|
// ─── Reputation Export ─────────────────────────────────────────────
|
|
3359
3359
|
{
|
|
3360
|
-
name: "
|
|
3360
|
+
name: "reputation_export",
|
|
3361
3361
|
description: "Export a portable reputation bundle (SANCTUARY_REP_V1). Includes all signed attestations for independent verification.",
|
|
3362
3362
|
inputSchema: {
|
|
3363
3363
|
type: "object",
|
|
@@ -3416,7 +3416,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3416
3416
|
},
|
|
3417
3417
|
// ─── Reputation Import ────────────────────────────────────────────
|
|
3418
3418
|
{
|
|
3419
|
-
name: "
|
|
3419
|
+
name: "reputation_import",
|
|
3420
3420
|
description: "Import a reputation bundle from another Sanctuary instance. Verifies all attestation signatures by default.",
|
|
3421
3421
|
inputSchema: {
|
|
3422
3422
|
type: "object",
|
|
@@ -3468,7 +3468,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3468
3468
|
},
|
|
3469
3469
|
// ─── Sovereignty-Weighted Query ──────────────────────────────────
|
|
3470
3470
|
{
|
|
3471
|
-
name: "
|
|
3471
|
+
name: "reputation_query_weighted",
|
|
3472
3472
|
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.",
|
|
3473
3473
|
inputSchema: {
|
|
3474
3474
|
type: "object",
|
|
@@ -3524,7 +3524,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3524
3524
|
},
|
|
3525
3525
|
// ─── Trust Bootstrap: Escrow ──────────────────────────────────────
|
|
3526
3526
|
{
|
|
3527
|
-
name: "
|
|
3527
|
+
name: "bootstrap_create_escrow",
|
|
3528
3528
|
description: "Create an escrow record for trust bootstrapping. Allows new participants with no reputation to transact safely.",
|
|
3529
3529
|
inputSchema: {
|
|
3530
3530
|
type: "object",
|
|
@@ -3583,7 +3583,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3583
3583
|
},
|
|
3584
3584
|
// ─── Trust Bootstrap: Guarantee ───────────────────────────────────
|
|
3585
3585
|
{
|
|
3586
|
-
name: "
|
|
3586
|
+
name: "bootstrap_provide_guarantee",
|
|
3587
3587
|
description: "A principal provides a signed reputation guarantee for a new agent. The guarantee certificate can be presented to counterparties.",
|
|
3588
3588
|
inputSchema: {
|
|
3589
3589
|
type: "object",
|
|
@@ -3661,7 +3661,7 @@ function createL4Tools(storage, masterKey, identityManager, auditLog, handshakeR
|
|
|
3661
3661
|
},
|
|
3662
3662
|
// ─── Verascore Reputation Publish ────────────────────────────────
|
|
3663
3663
|
{
|
|
3664
|
-
name: "
|
|
3664
|
+
name: "reputation_publish",
|
|
3665
3665
|
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.",
|
|
3666
3666
|
inputSchema: {
|
|
3667
3667
|
type: "object",
|
|
@@ -4102,8 +4102,6 @@ tier3_always_allow:
|
|
|
4102
4102
|
- governor_status
|
|
4103
4103
|
- reputation_publish
|
|
4104
4104
|
- sanctuary_policy_status
|
|
4105
|
-
- sanctuary_link_to_human
|
|
4106
|
-
- sanctuary_sign_challenge
|
|
4107
4105
|
|
|
4108
4106
|
# \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
|
|
4109
4107
|
# How Sanctuary reaches you when approval is needed.
|
|
@@ -7369,13 +7367,810 @@ function generateDashboardHTML(options) {
|
|
|
7369
7367
|
</html>`;
|
|
7370
7368
|
}
|
|
7371
7369
|
|
|
7370
|
+
// src/cocoon/fortress-view.ts
|
|
7371
|
+
function generateFortressViewHTML(options) {
|
|
7372
|
+
return `<!DOCTYPE html>
|
|
7373
|
+
<html lang="en">
|
|
7374
|
+
<head>
|
|
7375
|
+
<meta charset="UTF-8">
|
|
7376
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7377
|
+
<title>Sanctuary \u2014 Fortress View</title>
|
|
7378
|
+
<style>
|
|
7379
|
+
:root {
|
|
7380
|
+
--bg: #0d1117;
|
|
7381
|
+
--surface: #161b22;
|
|
7382
|
+
--surface-raised: #1c2128;
|
|
7383
|
+
--border: #30363d;
|
|
7384
|
+
--text-primary: #e6edf3;
|
|
7385
|
+
--text-secondary: #8b949e;
|
|
7386
|
+
--text-muted: #484f58;
|
|
7387
|
+
--green: #3fb950;
|
|
7388
|
+
--green-dim: #238636;
|
|
7389
|
+
--amber: #d29922;
|
|
7390
|
+
--amber-dim: #9e6a03;
|
|
7391
|
+
--red: #f85149;
|
|
7392
|
+
--red-dim: #da3633;
|
|
7393
|
+
--blue: #58a6ff;
|
|
7394
|
+
--blue-dim: #1f6feb;
|
|
7395
|
+
}
|
|
7396
|
+
|
|
7397
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
7398
|
+
|
|
7399
|
+
body {
|
|
7400
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
|
|
7401
|
+
background-color: var(--bg);
|
|
7402
|
+
color: var(--text-primary);
|
|
7403
|
+
min-height: 100vh;
|
|
7404
|
+
}
|
|
7405
|
+
|
|
7406
|
+
/* \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 */
|
|
7407
|
+
.fortress-header {
|
|
7408
|
+
display: flex;
|
|
7409
|
+
align-items: center;
|
|
7410
|
+
justify-content: space-between;
|
|
7411
|
+
padding: 16px 24px;
|
|
7412
|
+
border-bottom: 1px solid var(--border);
|
|
7413
|
+
background: var(--surface);
|
|
7414
|
+
}
|
|
7415
|
+
|
|
7416
|
+
.fortress-brand {
|
|
7417
|
+
display: flex;
|
|
7418
|
+
align-items: center;
|
|
7419
|
+
gap: 12px;
|
|
7420
|
+
}
|
|
7421
|
+
|
|
7422
|
+
.fortress-brand .shield {
|
|
7423
|
+
font-size: 28px;
|
|
7424
|
+
color: var(--blue);
|
|
7425
|
+
}
|
|
7426
|
+
|
|
7427
|
+
.fortress-brand h1 {
|
|
7428
|
+
font-size: 18px;
|
|
7429
|
+
font-weight: 600;
|
|
7430
|
+
letter-spacing: -0.5px;
|
|
7431
|
+
}
|
|
7432
|
+
|
|
7433
|
+
.fortress-brand .version {
|
|
7434
|
+
font-size: 12px;
|
|
7435
|
+
color: var(--text-secondary);
|
|
7436
|
+
}
|
|
7437
|
+
|
|
7438
|
+
.header-actions {
|
|
7439
|
+
display: flex;
|
|
7440
|
+
gap: 8px;
|
|
7441
|
+
}
|
|
7442
|
+
|
|
7443
|
+
.header-actions button {
|
|
7444
|
+
padding: 6px 16px;
|
|
7445
|
+
border-radius: 6px;
|
|
7446
|
+
border: 1px solid var(--border);
|
|
7447
|
+
background: var(--surface);
|
|
7448
|
+
color: var(--text-primary);
|
|
7449
|
+
font-size: 13px;
|
|
7450
|
+
cursor: pointer;
|
|
7451
|
+
transition: background 0.15s;
|
|
7452
|
+
}
|
|
7453
|
+
|
|
7454
|
+
.header-actions button:hover {
|
|
7455
|
+
background: var(--surface-raised);
|
|
7456
|
+
}
|
|
7457
|
+
|
|
7458
|
+
.header-actions .pause-btn {
|
|
7459
|
+
border-color: var(--red-dim);
|
|
7460
|
+
color: var(--red);
|
|
7461
|
+
}
|
|
7462
|
+
|
|
7463
|
+
.header-actions .pause-btn:hover {
|
|
7464
|
+
background: rgba(248, 81, 73, 0.1);
|
|
7465
|
+
}
|
|
7466
|
+
|
|
7467
|
+
.header-actions .pause-btn.paused {
|
|
7468
|
+
background: var(--red-dim);
|
|
7469
|
+
color: white;
|
|
7470
|
+
}
|
|
7471
|
+
|
|
7472
|
+
/* \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 */
|
|
7473
|
+
.tab-bar {
|
|
7474
|
+
display: flex;
|
|
7475
|
+
border-bottom: 1px solid var(--border);
|
|
7476
|
+
background: var(--surface);
|
|
7477
|
+
padding: 0 24px;
|
|
7478
|
+
}
|
|
7479
|
+
|
|
7480
|
+
.tab-bar button {
|
|
7481
|
+
padding: 10px 16px;
|
|
7482
|
+
border: none;
|
|
7483
|
+
background: none;
|
|
7484
|
+
color: var(--text-secondary);
|
|
7485
|
+
font-size: 14px;
|
|
7486
|
+
cursor: pointer;
|
|
7487
|
+
border-bottom: 2px solid transparent;
|
|
7488
|
+
transition: all 0.15s;
|
|
7489
|
+
}
|
|
7490
|
+
|
|
7491
|
+
.tab-bar button:hover {
|
|
7492
|
+
color: var(--text-primary);
|
|
7493
|
+
}
|
|
7494
|
+
|
|
7495
|
+
.tab-bar button.active {
|
|
7496
|
+
color: var(--text-primary);
|
|
7497
|
+
border-bottom-color: var(--blue);
|
|
7498
|
+
}
|
|
7499
|
+
|
|
7500
|
+
/* \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 */
|
|
7501
|
+
.fortress-content { padding: 24px; }
|
|
7502
|
+
|
|
7503
|
+
/* \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 */
|
|
7504
|
+
.status-banner {
|
|
7505
|
+
display: flex;
|
|
7506
|
+
align-items: center;
|
|
7507
|
+
gap: 16px;
|
|
7508
|
+
padding: 20px 24px;
|
|
7509
|
+
border-radius: 8px;
|
|
7510
|
+
border: 1px solid var(--border);
|
|
7511
|
+
background: var(--surface);
|
|
7512
|
+
margin-bottom: 24px;
|
|
7513
|
+
}
|
|
7514
|
+
|
|
7515
|
+
.status-indicator {
|
|
7516
|
+
width: 48px;
|
|
7517
|
+
height: 48px;
|
|
7518
|
+
border-radius: 50%;
|
|
7519
|
+
display: flex;
|
|
7520
|
+
align-items: center;
|
|
7521
|
+
justify-content: center;
|
|
7522
|
+
font-size: 24px;
|
|
7523
|
+
flex-shrink: 0;
|
|
7524
|
+
}
|
|
7525
|
+
|
|
7526
|
+
.status-indicator.green { background: rgba(63, 185, 80, 0.15); color: var(--green); }
|
|
7527
|
+
.status-indicator.amber { background: rgba(210, 153, 34, 0.15); color: var(--amber); }
|
|
7528
|
+
.status-indicator.red { background: rgba(248, 81, 73, 0.15); color: var(--red); }
|
|
7529
|
+
|
|
7530
|
+
.status-info h2 {
|
|
7531
|
+
font-size: 18px;
|
|
7532
|
+
font-weight: 600;
|
|
7533
|
+
margin-bottom: 4px;
|
|
7534
|
+
}
|
|
7535
|
+
|
|
7536
|
+
.status-info p {
|
|
7537
|
+
font-size: 14px;
|
|
7538
|
+
color: var(--text-secondary);
|
|
7539
|
+
}
|
|
7540
|
+
|
|
7541
|
+
.status-stats {
|
|
7542
|
+
display: flex;
|
|
7543
|
+
gap: 24px;
|
|
7544
|
+
margin-left: auto;
|
|
7545
|
+
}
|
|
7546
|
+
|
|
7547
|
+
.stat {
|
|
7548
|
+
text-align: center;
|
|
7549
|
+
}
|
|
7550
|
+
|
|
7551
|
+
.stat .value {
|
|
7552
|
+
font-size: 24px;
|
|
7553
|
+
font-weight: 600;
|
|
7554
|
+
font-variant-numeric: tabular-nums;
|
|
7555
|
+
}
|
|
7556
|
+
|
|
7557
|
+
.stat .label {
|
|
7558
|
+
font-size: 11px;
|
|
7559
|
+
color: var(--text-secondary);
|
|
7560
|
+
text-transform: uppercase;
|
|
7561
|
+
letter-spacing: 0.5px;
|
|
7562
|
+
}
|
|
7563
|
+
|
|
7564
|
+
/* \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 */
|
|
7565
|
+
.fortress-grid {
|
|
7566
|
+
display: grid;
|
|
7567
|
+
grid-template-columns: 1fr 360px;
|
|
7568
|
+
gap: 24px;
|
|
7569
|
+
}
|
|
7570
|
+
|
|
7571
|
+
@media (max-width: 900px) {
|
|
7572
|
+
.fortress-grid { grid-template-columns: 1fr; }
|
|
7573
|
+
}
|
|
7574
|
+
|
|
7575
|
+
/* \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 */
|
|
7576
|
+
.feed-panel {
|
|
7577
|
+
background: var(--surface);
|
|
7578
|
+
border: 1px solid var(--border);
|
|
7579
|
+
border-radius: 8px;
|
|
7580
|
+
overflow: hidden;
|
|
7581
|
+
}
|
|
7582
|
+
|
|
7583
|
+
.panel-header {
|
|
7584
|
+
display: flex;
|
|
7585
|
+
align-items: center;
|
|
7586
|
+
justify-content: space-between;
|
|
7587
|
+
padding: 12px 16px;
|
|
7588
|
+
border-bottom: 1px solid var(--border);
|
|
7589
|
+
}
|
|
7590
|
+
|
|
7591
|
+
.panel-header h3 {
|
|
7592
|
+
font-size: 14px;
|
|
7593
|
+
font-weight: 600;
|
|
7594
|
+
}
|
|
7595
|
+
|
|
7596
|
+
.feed-list {
|
|
7597
|
+
max-height: 600px;
|
|
7598
|
+
overflow-y: auto;
|
|
7599
|
+
scroll-behavior: smooth;
|
|
7600
|
+
}
|
|
7601
|
+
|
|
7602
|
+
.feed-item {
|
|
7603
|
+
display: flex;
|
|
7604
|
+
align-items: flex-start;
|
|
7605
|
+
gap: 10px;
|
|
7606
|
+
padding: 10px 16px;
|
|
7607
|
+
border-bottom: 1px solid var(--border);
|
|
7608
|
+
font-size: 13px;
|
|
7609
|
+
transition: background 0.1s;
|
|
7610
|
+
}
|
|
7611
|
+
|
|
7612
|
+
.feed-item:hover {
|
|
7613
|
+
background: var(--surface-raised);
|
|
7614
|
+
}
|
|
7615
|
+
|
|
7616
|
+
.feed-dot {
|
|
7617
|
+
width: 8px;
|
|
7618
|
+
height: 8px;
|
|
7619
|
+
border-radius: 50%;
|
|
7620
|
+
margin-top: 5px;
|
|
7621
|
+
flex-shrink: 0;
|
|
7622
|
+
}
|
|
7623
|
+
|
|
7624
|
+
.feed-dot.green { background: var(--green); }
|
|
7625
|
+
.feed-dot.amber { background: var(--amber); }
|
|
7626
|
+
.feed-dot.red { background: var(--red); }
|
|
7627
|
+
|
|
7628
|
+
.feed-detail {
|
|
7629
|
+
flex: 1;
|
|
7630
|
+
min-width: 0;
|
|
7631
|
+
}
|
|
7632
|
+
|
|
7633
|
+
.feed-tool {
|
|
7634
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
7635
|
+
font-size: 12px;
|
|
7636
|
+
color: var(--blue);
|
|
7637
|
+
word-break: break-all;
|
|
7638
|
+
}
|
|
7639
|
+
|
|
7640
|
+
.feed-decision {
|
|
7641
|
+
font-size: 12px;
|
|
7642
|
+
color: var(--text-secondary);
|
|
7643
|
+
margin-top: 2px;
|
|
7644
|
+
}
|
|
7645
|
+
|
|
7646
|
+
.feed-time {
|
|
7647
|
+
font-size: 11px;
|
|
7648
|
+
color: var(--text-muted);
|
|
7649
|
+
flex-shrink: 0;
|
|
7650
|
+
white-space: nowrap;
|
|
7651
|
+
}
|
|
7652
|
+
|
|
7653
|
+
.feed-empty {
|
|
7654
|
+
padding: 40px 16px;
|
|
7655
|
+
text-align: center;
|
|
7656
|
+
color: var(--text-muted);
|
|
7657
|
+
font-size: 14px;
|
|
7658
|
+
}
|
|
7659
|
+
|
|
7660
|
+
/* \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 */
|
|
7661
|
+
.alerts-panel {
|
|
7662
|
+
background: var(--surface);
|
|
7663
|
+
border: 1px solid var(--border);
|
|
7664
|
+
border-radius: 8px;
|
|
7665
|
+
overflow: hidden;
|
|
7666
|
+
}
|
|
7667
|
+
|
|
7668
|
+
.alert-item {
|
|
7669
|
+
padding: 12px 16px;
|
|
7670
|
+
border-bottom: 1px solid var(--border);
|
|
7671
|
+
}
|
|
7672
|
+
|
|
7673
|
+
.alert-item .alert-title {
|
|
7674
|
+
font-size: 13px;
|
|
7675
|
+
font-weight: 500;
|
|
7676
|
+
margin-bottom: 4px;
|
|
7677
|
+
}
|
|
7678
|
+
|
|
7679
|
+
.alert-item .alert-desc {
|
|
7680
|
+
font-size: 12px;
|
|
7681
|
+
color: var(--text-secondary);
|
|
7682
|
+
margin-bottom: 8px;
|
|
7683
|
+
}
|
|
7684
|
+
|
|
7685
|
+
.alert-actions {
|
|
7686
|
+
display: flex;
|
|
7687
|
+
gap: 8px;
|
|
7688
|
+
}
|
|
7689
|
+
|
|
7690
|
+
.alert-actions button {
|
|
7691
|
+
padding: 4px 12px;
|
|
7692
|
+
border-radius: 4px;
|
|
7693
|
+
border: 1px solid var(--border);
|
|
7694
|
+
font-size: 12px;
|
|
7695
|
+
cursor: pointer;
|
|
7696
|
+
transition: all 0.15s;
|
|
7697
|
+
}
|
|
7698
|
+
|
|
7699
|
+
.approve-btn {
|
|
7700
|
+
background: var(--green-dim);
|
|
7701
|
+
color: white;
|
|
7702
|
+
border-color: var(--green-dim) !important;
|
|
7703
|
+
}
|
|
7704
|
+
|
|
7705
|
+
.approve-btn:hover { opacity: 0.9; }
|
|
7706
|
+
|
|
7707
|
+
.deny-btn {
|
|
7708
|
+
background: none;
|
|
7709
|
+
color: var(--red);
|
|
7710
|
+
border-color: var(--red-dim) !important;
|
|
7711
|
+
}
|
|
7712
|
+
|
|
7713
|
+
.deny-btn:hover {
|
|
7714
|
+
background: rgba(248, 81, 73, 0.1);
|
|
7715
|
+
}
|
|
7716
|
+
|
|
7717
|
+
.alerts-empty {
|
|
7718
|
+
padding: 40px 16px;
|
|
7719
|
+
text-align: center;
|
|
7720
|
+
color: var(--text-muted);
|
|
7721
|
+
font-size: 14px;
|
|
7722
|
+
}
|
|
7723
|
+
|
|
7724
|
+
/* \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 */
|
|
7725
|
+
.servers-panel {
|
|
7726
|
+
margin-top: 16px;
|
|
7727
|
+
}
|
|
7728
|
+
|
|
7729
|
+
.server-row {
|
|
7730
|
+
display: flex;
|
|
7731
|
+
align-items: center;
|
|
7732
|
+
gap: 8px;
|
|
7733
|
+
padding: 8px 16px;
|
|
7734
|
+
border-bottom: 1px solid var(--border);
|
|
7735
|
+
font-size: 13px;
|
|
7736
|
+
}
|
|
7737
|
+
|
|
7738
|
+
.server-status-dot {
|
|
7739
|
+
width: 8px;
|
|
7740
|
+
height: 8px;
|
|
7741
|
+
border-radius: 50%;
|
|
7742
|
+
}
|
|
7743
|
+
|
|
7744
|
+
.server-status-dot.connected { background: var(--green); }
|
|
7745
|
+
.server-status-dot.connecting { background: var(--amber); }
|
|
7746
|
+
.server-status-dot.disconnected, .server-status-dot.error { background: var(--red); }
|
|
7747
|
+
|
|
7748
|
+
.server-name {
|
|
7749
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
7750
|
+
font-size: 12px;
|
|
7751
|
+
}
|
|
7752
|
+
|
|
7753
|
+
.server-tier {
|
|
7754
|
+
margin-left: auto;
|
|
7755
|
+
font-size: 11px;
|
|
7756
|
+
color: var(--text-secondary);
|
|
7757
|
+
}
|
|
7758
|
+
</style>
|
|
7759
|
+
</head>
|
|
7760
|
+
<body>
|
|
7761
|
+
<!-- Header -->
|
|
7762
|
+
<div class="fortress-header">
|
|
7763
|
+
<div class="fortress-brand">
|
|
7764
|
+
<div class="shield">🛡</div>
|
|
7765
|
+
<div>
|
|
7766
|
+
<h1>Sanctuary Cocoon</h1>
|
|
7767
|
+
<div class="version">v${esc(options.serverVersion)}</div>
|
|
7768
|
+
</div>
|
|
7769
|
+
</div>
|
|
7770
|
+
<div class="header-actions">
|
|
7771
|
+
<button class="pause-btn" id="pause-btn" title="Pause agent \u2014 requires approval for all operations">Pause Agent</button>
|
|
7772
|
+
<button id="advanced-btn">Advanced</button>
|
|
7773
|
+
</div>
|
|
7774
|
+
</div>
|
|
7775
|
+
|
|
7776
|
+
<!-- Tab bar -->
|
|
7777
|
+
<div class="tab-bar">
|
|
7778
|
+
<button class="active" data-tab="fortress">Fortress</button>
|
|
7779
|
+
<button data-tab="advanced">Advanced</button>
|
|
7780
|
+
</div>
|
|
7781
|
+
|
|
7782
|
+
<!-- Fortress View -->
|
|
7783
|
+
<div class="fortress-content" id="fortress-tab">
|
|
7784
|
+
<!-- Status Banner -->
|
|
7785
|
+
<div class="status-banner" id="status-banner">
|
|
7786
|
+
<div class="status-indicator green" id="status-indicator">✓</div>
|
|
7787
|
+
<div class="status-info">
|
|
7788
|
+
<h2 id="status-title">Agent Protected</h2>
|
|
7789
|
+
<p id="status-subtitle">${options.upstreamServerCount} server${options.upstreamServerCount !== 1 ? "s" : ""} monitored. All systems nominal.</p>
|
|
7790
|
+
</div>
|
|
7791
|
+
<div class="status-stats">
|
|
7792
|
+
<div class="stat">
|
|
7793
|
+
<div class="value" id="stat-total">0</div>
|
|
7794
|
+
<div class="label">Calls</div>
|
|
7795
|
+
</div>
|
|
7796
|
+
<div class="stat">
|
|
7797
|
+
<div class="value" id="stat-blocked">0</div>
|
|
7798
|
+
<div class="label">Blocked</div>
|
|
7799
|
+
</div>
|
|
7800
|
+
<div class="stat">
|
|
7801
|
+
<div class="value" id="stat-pending">0</div>
|
|
7802
|
+
<div class="label">Pending</div>
|
|
7803
|
+
</div>
|
|
7804
|
+
</div>
|
|
7805
|
+
</div>
|
|
7806
|
+
|
|
7807
|
+
<!-- Two-column layout -->
|
|
7808
|
+
<div class="fortress-grid">
|
|
7809
|
+
<!-- Live Feed -->
|
|
7810
|
+
<div class="feed-panel">
|
|
7811
|
+
<div class="panel-header">
|
|
7812
|
+
<h3>Live Activity</h3>
|
|
7813
|
+
<span style="font-size: 12px; color: var(--text-muted);" id="feed-count">0 events</span>
|
|
7814
|
+
</div>
|
|
7815
|
+
<div class="feed-list" id="feed-list">
|
|
7816
|
+
<div class="feed-empty">Waiting for tool calls...</div>
|
|
7817
|
+
</div>
|
|
7818
|
+
</div>
|
|
7819
|
+
|
|
7820
|
+
<!-- Right column: Alerts + Servers -->
|
|
7821
|
+
<div>
|
|
7822
|
+
<!-- Alerts -->
|
|
7823
|
+
<div class="alerts-panel">
|
|
7824
|
+
<div class="panel-header">
|
|
7825
|
+
<h3>Needs Attention</h3>
|
|
7826
|
+
<span style="font-size: 12px; color: var(--text-muted);" id="alert-count">0</span>
|
|
7827
|
+
</div>
|
|
7828
|
+
<div id="alerts-list">
|
|
7829
|
+
<div class="alerts-empty">No pending actions</div>
|
|
7830
|
+
</div>
|
|
7831
|
+
</div>
|
|
7832
|
+
|
|
7833
|
+
<!-- Servers -->
|
|
7834
|
+
<div class="alerts-panel servers-panel">
|
|
7835
|
+
<div class="panel-header">
|
|
7836
|
+
<h3>Upstream Servers</h3>
|
|
7837
|
+
</div>
|
|
7838
|
+
<div id="servers-list">
|
|
7839
|
+
<div class="alerts-empty">No servers configured</div>
|
|
7840
|
+
</div>
|
|
7841
|
+
</div>
|
|
7842
|
+
</div>
|
|
7843
|
+
</div>
|
|
7844
|
+
</div>
|
|
7845
|
+
|
|
7846
|
+
<script>
|
|
7847
|
+
// \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
|
|
7848
|
+
const API_BASE = window.location.origin;
|
|
7849
|
+
const SESSION_TOKEN = sessionStorage.getItem('sanctuary_session') || '';
|
|
7850
|
+
const MAX_FEED_ITEMS = 50;
|
|
7851
|
+
|
|
7852
|
+
let feedItems = [];
|
|
7853
|
+
let totalCalls = 0;
|
|
7854
|
+
let blockedCalls = 0;
|
|
7855
|
+
let pendingApprovals = [];
|
|
7856
|
+
let upstreamServers = [];
|
|
7857
|
+
let paused = false;
|
|
7858
|
+
|
|
7859
|
+
// \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
|
|
7860
|
+
function connectSSE() {
|
|
7861
|
+
const url = API_BASE + '/events' + (SESSION_TOKEN ? '?session=' + SESSION_TOKEN : '');
|
|
7862
|
+
const eventSource = new EventSource(url);
|
|
7863
|
+
|
|
7864
|
+
eventSource.addEventListener('proxy-call', (e) => {
|
|
7865
|
+
try {
|
|
7866
|
+
const data = JSON.parse(e.data);
|
|
7867
|
+
addFeedItem(data);
|
|
7868
|
+
} catch {}
|
|
7869
|
+
});
|
|
7870
|
+
|
|
7871
|
+
eventSource.addEventListener('proxy-server-status', (e) => {
|
|
7872
|
+
try {
|
|
7873
|
+
const data = JSON.parse(e.data);
|
|
7874
|
+
updateServerStatus(data.server, data.state, data.tool_count, data.error);
|
|
7875
|
+
} catch {}
|
|
7876
|
+
});
|
|
7877
|
+
|
|
7878
|
+
eventSource.addEventListener('injection-alert', (e) => {
|
|
7879
|
+
try {
|
|
7880
|
+
const data = JSON.parse(e.data);
|
|
7881
|
+
addFeedItem({
|
|
7882
|
+
tool: data.tool_name || 'unknown',
|
|
7883
|
+
server: 'detection',
|
|
7884
|
+
decision: 'blocked',
|
|
7885
|
+
reason: 'Injection detected: ' + (data.signals || []).join(', '),
|
|
7886
|
+
timestamp: new Date().toISOString(),
|
|
7887
|
+
});
|
|
7888
|
+
} catch {}
|
|
7889
|
+
});
|
|
7890
|
+
|
|
7891
|
+
eventSource.addEventListener('approval-request', (e) => {
|
|
7892
|
+
try {
|
|
7893
|
+
const data = JSON.parse(e.data);
|
|
7894
|
+
addPendingApproval(data);
|
|
7895
|
+
} catch {}
|
|
7896
|
+
});
|
|
7897
|
+
|
|
7898
|
+
eventSource.addEventListener('approval-resolved', (e) => {
|
|
7899
|
+
try {
|
|
7900
|
+
const data = JSON.parse(e.data);
|
|
7901
|
+
removePendingApproval(data.id);
|
|
7902
|
+
} catch {}
|
|
7903
|
+
});
|
|
7904
|
+
|
|
7905
|
+
eventSource.onerror = () => {
|
|
7906
|
+
eventSource.close();
|
|
7907
|
+
setTimeout(connectSSE, 3000);
|
|
7908
|
+
};
|
|
7909
|
+
}
|
|
7910
|
+
|
|
7911
|
+
// \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
|
|
7912
|
+
function addFeedItem(data) {
|
|
7913
|
+
totalCalls++;
|
|
7914
|
+
if (data.decision === 'blocked' || data.decision === 'denied') {
|
|
7915
|
+
blockedCalls++;
|
|
7916
|
+
}
|
|
7917
|
+
|
|
7918
|
+
feedItems.unshift({
|
|
7919
|
+
tool: data.tool || 'unknown',
|
|
7920
|
+
server: data.server || '',
|
|
7921
|
+
decision: data.decision || 'allowed',
|
|
7922
|
+
reason: data.reason || '',
|
|
7923
|
+
time: data.timestamp || new Date().toISOString(),
|
|
7924
|
+
});
|
|
7925
|
+
|
|
7926
|
+
if (feedItems.length > MAX_FEED_ITEMS) {
|
|
7927
|
+
feedItems = feedItems.slice(0, MAX_FEED_ITEMS);
|
|
7928
|
+
}
|
|
7929
|
+
|
|
7930
|
+
renderFeed();
|
|
7931
|
+
updateStats();
|
|
7932
|
+
updateStatus();
|
|
7933
|
+
}
|
|
7934
|
+
|
|
7935
|
+
function renderFeed() {
|
|
7936
|
+
const container = document.getElementById('feed-list');
|
|
7937
|
+
if (feedItems.length === 0) {
|
|
7938
|
+
container.innerHTML = '<div class="feed-empty">Waiting for tool calls...</div>';
|
|
7939
|
+
return;
|
|
7940
|
+
}
|
|
7941
|
+
|
|
7942
|
+
container.innerHTML = feedItems.map(item => {
|
|
7943
|
+
const dotColor = item.decision === 'allowed' ? 'green'
|
|
7944
|
+
: item.decision === 'pending' ? 'amber' : 'red';
|
|
7945
|
+
const decisionText = item.decision === 'allowed' ? 'Auto-allowed'
|
|
7946
|
+
: item.decision === 'pending' ? 'Awaiting approval'
|
|
7947
|
+
: item.decision === 'blocked' ? 'Blocked' : item.decision;
|
|
7948
|
+
const timeStr = new Date(item.time).toLocaleTimeString();
|
|
7949
|
+
|
|
7950
|
+
return '<div class="feed-item">' +
|
|
7951
|
+
'<div class="feed-dot ' + dotColor + '"></div>' +
|
|
7952
|
+
'<div class="feed-detail">' +
|
|
7953
|
+
'<div class="feed-tool">' + esc(item.tool) + '</div>' +
|
|
7954
|
+
'<div class="feed-decision">' + esc(decisionText) +
|
|
7955
|
+
(item.reason ? ' \u2014 ' + esc(item.reason) : '') + '</div>' +
|
|
7956
|
+
'</div>' +
|
|
7957
|
+
'<div class="feed-time">' + esc(timeStr) + '</div>' +
|
|
7958
|
+
'</div>';
|
|
7959
|
+
}).join('');
|
|
7960
|
+
|
|
7961
|
+
document.getElementById('feed-count').textContent = feedItems.length + ' events';
|
|
7962
|
+
}
|
|
7963
|
+
|
|
7964
|
+
// \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
|
|
7965
|
+
function addPendingApproval(data) {
|
|
7966
|
+
pendingApprovals.push(data);
|
|
7967
|
+
renderAlerts();
|
|
7968
|
+
updateStats();
|
|
7969
|
+
updateStatus();
|
|
7970
|
+
}
|
|
7971
|
+
|
|
7972
|
+
function removePendingApproval(id) {
|
|
7973
|
+
pendingApprovals = pendingApprovals.filter(a => a.id !== id);
|
|
7974
|
+
renderAlerts();
|
|
7975
|
+
updateStats();
|
|
7976
|
+
updateStatus();
|
|
7977
|
+
}
|
|
7978
|
+
|
|
7979
|
+
function renderAlerts() {
|
|
7980
|
+
const container = document.getElementById('alerts-list');
|
|
7981
|
+
if (pendingApprovals.length === 0) {
|
|
7982
|
+
container.innerHTML = '<div class="alerts-empty">No pending actions</div>';
|
|
7983
|
+
document.getElementById('alert-count').textContent = '0';
|
|
7984
|
+
return;
|
|
7985
|
+
}
|
|
7986
|
+
|
|
7987
|
+
document.getElementById('alert-count').textContent = pendingApprovals.length.toString();
|
|
7988
|
+
|
|
7989
|
+
container.innerHTML = pendingApprovals.map(approval => {
|
|
7990
|
+
return '<div class="alert-item">' +
|
|
7991
|
+
'<div class="alert-title">Approval required: ' + esc(approval.operation || approval.tool_name || 'unknown') + '</div>' +
|
|
7992
|
+
'<div class="alert-desc">' + esc(approval.reason || 'This operation requires your approval before it can proceed.') + '</div>' +
|
|
7993
|
+
'<div class="alert-actions">' +
|
|
7994
|
+
'<button class="approve-btn" onclick="handleApproval(\\'' + esc(approval.id) + '\\', true)">Approve</button>' +
|
|
7995
|
+
'<button class="deny-btn" onclick="handleApproval(\\'' + esc(approval.id) + '\\', false)">Deny</button>' +
|
|
7996
|
+
'</div>' +
|
|
7997
|
+
'</div>';
|
|
7998
|
+
}).join('');
|
|
7999
|
+
}
|
|
8000
|
+
|
|
8001
|
+
async function handleApproval(id, approved) {
|
|
8002
|
+
const endpoint = approved ? '/api/approve/' : '/api/deny/';
|
|
8003
|
+
try {
|
|
8004
|
+
await fetch(API_BASE + endpoint + id, {
|
|
8005
|
+
method: 'POST',
|
|
8006
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8007
|
+
});
|
|
8008
|
+
removePendingApproval(id);
|
|
8009
|
+
} catch (err) {
|
|
8010
|
+
console.error('Approval action failed:', err);
|
|
8011
|
+
}
|
|
8012
|
+
}
|
|
8013
|
+
|
|
8014
|
+
// \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
|
|
8015
|
+
function updateServerStatus(serverName, state, toolCount, error) {
|
|
8016
|
+
const existing = upstreamServers.find(s => s.name === serverName);
|
|
8017
|
+
if (existing) {
|
|
8018
|
+
existing.state = state;
|
|
8019
|
+
existing.tool_count = toolCount;
|
|
8020
|
+
existing.error = error;
|
|
8021
|
+
} else {
|
|
8022
|
+
upstreamServers.push({ name: serverName, state, tool_count: toolCount, error });
|
|
8023
|
+
}
|
|
8024
|
+
renderServers();
|
|
8025
|
+
updateStatus();
|
|
8026
|
+
}
|
|
8027
|
+
|
|
8028
|
+
function renderServers() {
|
|
8029
|
+
const container = document.getElementById('servers-list');
|
|
8030
|
+
if (upstreamServers.length === 0) {
|
|
8031
|
+
container.innerHTML = '<div class="alerts-empty">No servers configured</div>';
|
|
8032
|
+
return;
|
|
8033
|
+
}
|
|
8034
|
+
|
|
8035
|
+
container.innerHTML = upstreamServers.map(server => {
|
|
8036
|
+
const stateClass = server.state || 'disconnected';
|
|
8037
|
+
const stateLabel = server.state === 'connected' ? 'Connected'
|
|
8038
|
+
: server.state === 'connecting' ? 'Connecting...'
|
|
8039
|
+
: server.state === 'error' ? 'Error' : 'Disconnected';
|
|
8040
|
+
|
|
8041
|
+
return '<div class="server-row">' +
|
|
8042
|
+
'<div class="server-status-dot ' + stateClass + '"></div>' +
|
|
8043
|
+
'<span class="server-name">' + esc(server.name) + '</span>' +
|
|
8044
|
+
'<span class="server-tier">' + esc(stateLabel) +
|
|
8045
|
+
(server.tool_count ? ' (' + server.tool_count + ' tools)' : '') + '</span>' +
|
|
8046
|
+
'</div>';
|
|
8047
|
+
}).join('');
|
|
8048
|
+
}
|
|
8049
|
+
|
|
8050
|
+
// \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
|
|
8051
|
+
function updateStats() {
|
|
8052
|
+
document.getElementById('stat-total').textContent = totalCalls.toString();
|
|
8053
|
+
document.getElementById('stat-blocked').textContent = blockedCalls.toString();
|
|
8054
|
+
document.getElementById('stat-pending').textContent = pendingApprovals.length.toString();
|
|
8055
|
+
}
|
|
8056
|
+
|
|
8057
|
+
function updateStatus() {
|
|
8058
|
+
const indicator = document.getElementById('status-indicator');
|
|
8059
|
+
const title = document.getElementById('status-title');
|
|
8060
|
+
const subtitle = document.getElementById('status-subtitle');
|
|
8061
|
+
|
|
8062
|
+
const hasErrors = upstreamServers.some(s => s.state === 'error');
|
|
8063
|
+
const hasPending = pendingApprovals.length > 0;
|
|
8064
|
+
const hasBlocked = blockedCalls > 0;
|
|
8065
|
+
|
|
8066
|
+
if (paused) {
|
|
8067
|
+
indicator.className = 'status-indicator red';
|
|
8068
|
+
indicator.innerHTML = '⏸';
|
|
8069
|
+
title.textContent = 'Agent Paused';
|
|
8070
|
+
subtitle.textContent = 'All operations require approval. Click Resume to restore normal mode.';
|
|
8071
|
+
} else if (hasErrors) {
|
|
8072
|
+
indicator.className = 'status-indicator red';
|
|
8073
|
+
indicator.innerHTML = '⚠';
|
|
8074
|
+
title.textContent = 'Connection Issues';
|
|
8075
|
+
subtitle.textContent = 'One or more upstream servers have errors.';
|
|
8076
|
+
} else if (hasPending) {
|
|
8077
|
+
indicator.className = 'status-indicator amber';
|
|
8078
|
+
indicator.innerHTML = '⏳';
|
|
8079
|
+
title.textContent = 'Action Required';
|
|
8080
|
+
subtitle.textContent = pendingApprovals.length + ' operation' + (pendingApprovals.length > 1 ? 's' : '') + ' awaiting your approval.';
|
|
8081
|
+
} else {
|
|
8082
|
+
indicator.className = 'status-indicator green';
|
|
8083
|
+
indicator.innerHTML = '✓';
|
|
8084
|
+
title.textContent = 'Agent Protected';
|
|
8085
|
+
const serverCount = upstreamServers.filter(s => s.state === 'connected').length || ${options.upstreamServerCount};
|
|
8086
|
+
subtitle.textContent = serverCount + ' server' + (serverCount !== 1 ? 's' : '') + ' monitored. All systems nominal.';
|
|
8087
|
+
}
|
|
8088
|
+
}
|
|
8089
|
+
|
|
8090
|
+
// \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
|
|
8091
|
+
document.getElementById('pause-btn').addEventListener('click', () => {
|
|
8092
|
+
paused = !paused;
|
|
8093
|
+
const btn = document.getElementById('pause-btn');
|
|
8094
|
+
if (paused) {
|
|
8095
|
+
btn.textContent = 'Resume Agent';
|
|
8096
|
+
btn.classList.add('paused');
|
|
8097
|
+
} else {
|
|
8098
|
+
btn.textContent = 'Pause Agent';
|
|
8099
|
+
btn.classList.remove('paused');
|
|
8100
|
+
}
|
|
8101
|
+
updateStatus();
|
|
8102
|
+
// TODO: POST to /api/cocoon/pause to set all tiers to 1
|
|
8103
|
+
});
|
|
8104
|
+
|
|
8105
|
+
// \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
|
|
8106
|
+
document.getElementById('advanced-btn').addEventListener('click', () => {
|
|
8107
|
+
window.location.href = '/dashboard?session=' + SESSION_TOKEN;
|
|
8108
|
+
});
|
|
8109
|
+
|
|
8110
|
+
document.querySelectorAll('.tab-bar button').forEach(btn => {
|
|
8111
|
+
btn.addEventListener('click', () => {
|
|
8112
|
+
const tab = btn.dataset.tab;
|
|
8113
|
+
if (tab === 'advanced') {
|
|
8114
|
+
window.location.href = '/dashboard?session=' + SESSION_TOKEN;
|
|
8115
|
+
}
|
|
8116
|
+
});
|
|
8117
|
+
});
|
|
8118
|
+
|
|
8119
|
+
// \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
|
|
8120
|
+
function esc(str) {
|
|
8121
|
+
if (!str) return '';
|
|
8122
|
+
const d = document.createElement('div');
|
|
8123
|
+
d.textContent = String(str);
|
|
8124
|
+
return d.innerHTML;
|
|
8125
|
+
}
|
|
8126
|
+
|
|
8127
|
+
// \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
|
|
8128
|
+
async function init() {
|
|
8129
|
+
// Load initial server state
|
|
8130
|
+
try {
|
|
8131
|
+
const resp = await fetch(API_BASE + '/api/proxy/servers', {
|
|
8132
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8133
|
+
});
|
|
8134
|
+
if (resp.ok) {
|
|
8135
|
+
const data = await resp.json();
|
|
8136
|
+
upstreamServers = data.servers || [];
|
|
8137
|
+
renderServers();
|
|
8138
|
+
}
|
|
8139
|
+
} catch {}
|
|
8140
|
+
|
|
8141
|
+
// Load pending approvals
|
|
8142
|
+
try {
|
|
8143
|
+
const resp = await fetch(API_BASE + '/api/pending', {
|
|
8144
|
+
headers: SESSION_TOKEN ? { 'Authorization': 'Bearer ' + SESSION_TOKEN } : {},
|
|
8145
|
+
});
|
|
8146
|
+
if (resp.ok) {
|
|
8147
|
+
const data = await resp.json();
|
|
8148
|
+
pendingApprovals = data.pending || [];
|
|
8149
|
+
renderAlerts();
|
|
8150
|
+
updateStats();
|
|
8151
|
+
}
|
|
8152
|
+
} catch {}
|
|
8153
|
+
|
|
8154
|
+
updateStatus();
|
|
8155
|
+
connectSSE();
|
|
8156
|
+
}
|
|
8157
|
+
|
|
8158
|
+
init();
|
|
8159
|
+
</script>
|
|
8160
|
+
</body>
|
|
8161
|
+
</html>`;
|
|
8162
|
+
}
|
|
8163
|
+
function esc(str) {
|
|
8164
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
8165
|
+
}
|
|
8166
|
+
|
|
7372
8167
|
// src/system-prompt-generator.ts
|
|
7373
8168
|
var FEATURE_INFO = {
|
|
7374
8169
|
audit_logging: {
|
|
7375
8170
|
name: "Audit Logging",
|
|
7376
|
-
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
|
|
7377
|
-
toolNames: ["
|
|
7378
|
-
disabledDescription: "audit logging (
|
|
8171
|
+
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.",
|
|
8172
|
+
toolNames: ["monitor_audit_log"],
|
|
8173
|
+
disabledDescription: "audit logging (monitor_audit_log)",
|
|
7379
8174
|
usageExample: "Automatic \u2014 every tool call you make is recorded. No explicit action required."
|
|
7380
8175
|
},
|
|
7381
8176
|
injection_detection: {
|
|
@@ -7386,16 +8181,16 @@ var FEATURE_INFO = {
|
|
|
7386
8181
|
},
|
|
7387
8182
|
context_gating: {
|
|
7388
8183
|
name: "Context Gating",
|
|
7389
|
-
activeDescription: "Before sending context to any external API (LLM inference, tool APIs, logging services), call
|
|
8184
|
+
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.",
|
|
7390
8185
|
toolNames: [
|
|
7391
|
-
"
|
|
7392
|
-
"
|
|
7393
|
-
"
|
|
7394
|
-
"
|
|
7395
|
-
"
|
|
8186
|
+
"context_gate_filter",
|
|
8187
|
+
"context_gate_set_policy",
|
|
8188
|
+
"context_gate_apply_template",
|
|
8189
|
+
"context_gate_recommend",
|
|
8190
|
+
"context_gate_list_policies"
|
|
7396
8191
|
],
|
|
7397
|
-
disabledDescription: "context gating (
|
|
7398
|
-
usageExample: "Before calling an external API, run:
|
|
8192
|
+
disabledDescription: "context gating (context_gate_filter)",
|
|
8193
|
+
usageExample: "Before calling an external API, run: context_gate_filter with your context object and policy_id to get a filtered version."
|
|
7399
8194
|
},
|
|
7400
8195
|
approval_gate: {
|
|
7401
8196
|
name: "Approval Gates",
|
|
@@ -7405,15 +8200,15 @@ var FEATURE_INFO = {
|
|
|
7405
8200
|
},
|
|
7406
8201
|
zk_proofs: {
|
|
7407
8202
|
name: "Zero-Knowledge Proofs",
|
|
7408
|
-
activeDescription: "You can prove claims about your data without revealing the underlying values. Use
|
|
8203
|
+
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.",
|
|
7409
8204
|
toolNames: [
|
|
7410
|
-
"
|
|
7411
|
-
"
|
|
7412
|
-
"
|
|
7413
|
-
"
|
|
8205
|
+
"zk_commit",
|
|
8206
|
+
"zk_prove",
|
|
8207
|
+
"zk_range_prove",
|
|
8208
|
+
"proof_commitment"
|
|
7414
8209
|
],
|
|
7415
|
-
disabledDescription: "zero-knowledge proofs (
|
|
7416
|
-
usageExample: "To prove a claim without revealing data: first
|
|
8210
|
+
disabledDescription: "zero-knowledge proofs (zk_commit, zk_prove)",
|
|
8211
|
+
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."
|
|
7417
8212
|
}
|
|
7418
8213
|
};
|
|
7419
8214
|
function generateSystemPrompt(profile) {
|
|
@@ -7476,12 +8271,12 @@ function buildQuickStart(activeKeys) {
|
|
|
7476
8271
|
const items = [];
|
|
7477
8272
|
if (activeKeys.includes("context_gating")) {
|
|
7478
8273
|
items.push(
|
|
7479
|
-
"1. ALWAYS call
|
|
8274
|
+
"1. ALWAYS call context_gate_filter before sending context to external APIs."
|
|
7480
8275
|
);
|
|
7481
8276
|
}
|
|
7482
8277
|
if (activeKeys.includes("zk_proofs")) {
|
|
7483
8278
|
items.push(
|
|
7484
|
-
`${items.length + 1}. Use
|
|
8279
|
+
`${items.length + 1}. Use zk_commit to prove claims without revealing underlying data.`
|
|
7485
8280
|
);
|
|
7486
8281
|
}
|
|
7487
8282
|
if (activeKeys.includes("approval_gate")) {
|
|
@@ -7525,6 +8320,7 @@ var DashboardApprovalChannel = class {
|
|
|
7525
8320
|
profileStore = null;
|
|
7526
8321
|
clientManager = null;
|
|
7527
8322
|
dashboardHTML;
|
|
8323
|
+
fortressHTML = null;
|
|
7528
8324
|
loginHTML;
|
|
7529
8325
|
authToken;
|
|
7530
8326
|
useTLS;
|
|
@@ -7910,8 +8706,14 @@ var DashboardApprovalChannel = class {
|
|
|
7910
8706
|
if (!this.checkAuth(req, url, res)) return;
|
|
7911
8707
|
if (!this.checkRateLimit(req, res, "general")) return;
|
|
7912
8708
|
try {
|
|
7913
|
-
if (method === "GET" &&
|
|
7914
|
-
this.
|
|
8709
|
+
if (method === "GET" && url.pathname === "/fortress") {
|
|
8710
|
+
this.serveFortressView(res);
|
|
8711
|
+
} else if (method === "GET" && (url.pathname === "/" || url.pathname === "/dashboard")) {
|
|
8712
|
+
if (this.fortressHTML) {
|
|
8713
|
+
this.serveFortressView(res);
|
|
8714
|
+
} else {
|
|
8715
|
+
this.serveDashboard(res);
|
|
8716
|
+
}
|
|
7915
8717
|
} else if (method === "GET" && url.pathname === "/events") {
|
|
7916
8718
|
this.handleSSE(req, res);
|
|
7917
8719
|
} else if (method === "GET" && url.pathname === "/api/status") {
|
|
@@ -8006,6 +8808,35 @@ var DashboardApprovalChannel = class {
|
|
|
8006
8808
|
});
|
|
8007
8809
|
res.end(this.dashboardHTML);
|
|
8008
8810
|
}
|
|
8811
|
+
serveFortressView(res) {
|
|
8812
|
+
if (!this.fortressHTML) {
|
|
8813
|
+
this.serveDashboard(res);
|
|
8814
|
+
return;
|
|
8815
|
+
}
|
|
8816
|
+
res.writeHead(200, {
|
|
8817
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
8818
|
+
"Cache-Control": "no-cache"
|
|
8819
|
+
});
|
|
8820
|
+
res.end(this.fortressHTML);
|
|
8821
|
+
}
|
|
8822
|
+
/**
|
|
8823
|
+
* Enable Fortress View (Cocoon mode) with the given upstream server count.
|
|
8824
|
+
* Once enabled, the root path `/` serves the Fortress View instead of the
|
|
8825
|
+
* standard dashboard. The standard dashboard remains available at `/dashboard`.
|
|
8826
|
+
*/
|
|
8827
|
+
enableFortressView(upstreamServerCount) {
|
|
8828
|
+
this.fortressHTML = generateFortressViewHTML({
|
|
8829
|
+
serverVersion: SANCTUARY_VERSION,
|
|
8830
|
+
authToken: this.authToken,
|
|
8831
|
+
upstreamServerCount
|
|
8832
|
+
});
|
|
8833
|
+
}
|
|
8834
|
+
/**
|
|
8835
|
+
* Broadcast a proxy call event to connected dashboards (Fortress View feed).
|
|
8836
|
+
*/
|
|
8837
|
+
broadcastProxyCall(data) {
|
|
8838
|
+
this.broadcastSSE("proxy-call", data);
|
|
8839
|
+
}
|
|
8009
8840
|
handleSSE(req, res) {
|
|
8010
8841
|
res.writeHead(200, {
|
|
8011
8842
|
"Content-Type": "text/event-stream",
|
|
@@ -8869,7 +9700,7 @@ var InjectionDetector = class {
|
|
|
8869
9700
|
}
|
|
8870
9701
|
/**
|
|
8871
9702
|
* Scan tool arguments for injection signals.
|
|
8872
|
-
* @param toolName Full tool name (e.g., "
|
|
9703
|
+
* @param toolName Full tool name (e.g., "state_read")
|
|
8873
9704
|
* @param args Tool arguments
|
|
8874
9705
|
* @returns DetectionResult with all detected signals
|
|
8875
9706
|
*/
|
|
@@ -9870,7 +10701,7 @@ var ApprovalGate = class {
|
|
|
9870
10701
|
/**
|
|
9871
10702
|
* Evaluate a tool call against the Principal Policy.
|
|
9872
10703
|
*
|
|
9873
|
-
* @param toolName - Full MCP tool name (e.g., "
|
|
10704
|
+
* @param toolName - Full MCP tool name (e.g., "state_export")
|
|
9874
10705
|
* @param args - Tool call arguments (for context extraction)
|
|
9875
10706
|
* @returns GateResult indicating whether the call is allowed
|
|
9876
10707
|
*/
|
|
@@ -10139,7 +10970,7 @@ var ApprovalGate = class {
|
|
|
10139
10970
|
function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
10140
10971
|
return [
|
|
10141
10972
|
{
|
|
10142
|
-
name: "
|
|
10973
|
+
name: "principal_policy_view",
|
|
10143
10974
|
description: "View the current Principal Policy \u2014 the human-controlled rules governing what operations require approval. Read-only.",
|
|
10144
10975
|
inputSchema: {
|
|
10145
10976
|
type: "object",
|
|
@@ -10177,7 +11008,7 @@ function createPrincipalPolicyTools(policy, baseline, auditLog) {
|
|
|
10177
11008
|
}
|
|
10178
11009
|
},
|
|
10179
11010
|
{
|
|
10180
|
-
name: "
|
|
11011
|
+
name: "principal_baseline_view",
|
|
10181
11012
|
description: "View the current behavioral baseline \u2014 the session profile used for anomaly detection. Shows known namespaces, counterparties, and tool call counts. Read-only.",
|
|
10182
11013
|
inputSchema: {
|
|
10183
11014
|
type: "object",
|
|
@@ -10517,7 +11348,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10517
11348
|
};
|
|
10518
11349
|
const tools = [
|
|
10519
11350
|
{
|
|
10520
|
-
name: "
|
|
11351
|
+
name: "shr_generate",
|
|
10521
11352
|
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.",
|
|
10522
11353
|
inputSchema: {
|
|
10523
11354
|
type: "object",
|
|
@@ -10546,7 +11377,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10546
11377
|
}
|
|
10547
11378
|
},
|
|
10548
11379
|
{
|
|
10549
|
-
name: "
|
|
11380
|
+
name: "shr_verify",
|
|
10550
11381
|
description: "Verify a counterparty's Sovereignty Health Report (SHR). Checks signature validity, temporal validity, and assesses sovereignty level.",
|
|
10551
11382
|
inputSchema: {
|
|
10552
11383
|
type: "object",
|
|
@@ -10572,7 +11403,7 @@ function createSHRTools(config, identityManager, masterKey, auditLog) {
|
|
|
10572
11403
|
}
|
|
10573
11404
|
},
|
|
10574
11405
|
{
|
|
10575
|
-
name: "
|
|
11406
|
+
name: "shr_gateway_export",
|
|
10576
11407
|
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.",
|
|
10577
11408
|
inputSchema: {
|
|
10578
11409
|
type: "object",
|
|
@@ -10959,7 +11790,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
10959
11790
|
};
|
|
10960
11791
|
const tools = [
|
|
10961
11792
|
{
|
|
10962
|
-
name: "
|
|
11793
|
+
name: "handshake_initiate",
|
|
10963
11794
|
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.",
|
|
10964
11795
|
inputSchema: {
|
|
10965
11796
|
type: "object",
|
|
@@ -10986,7 +11817,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
10986
11817
|
}
|
|
10987
11818
|
},
|
|
10988
11819
|
{
|
|
10989
|
-
name: "
|
|
11820
|
+
name: "handshake_respond",
|
|
10990
11821
|
description: "Respond to an incoming sovereignty handshake challenge. Verifies the initiator's SHR, signs their nonce, and returns our SHR with a counter-nonce.",
|
|
10991
11822
|
inputSchema: {
|
|
10992
11823
|
type: "object",
|
|
@@ -11107,7 +11938,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11107
11938
|
}
|
|
11108
11939
|
},
|
|
11109
11940
|
{
|
|
11110
|
-
name: "
|
|
11941
|
+
name: "handshake_complete",
|
|
11111
11942
|
description: "Complete a sovereignty handshake (initiator side). Verifies the responder's SHR and nonce signature, signs their nonce, and produces the final result.",
|
|
11112
11943
|
inputSchema: {
|
|
11113
11944
|
type: "object",
|
|
@@ -11162,7 +11993,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11162
11993
|
}
|
|
11163
11994
|
},
|
|
11164
11995
|
{
|
|
11165
|
-
name: "
|
|
11996
|
+
name: "handshake_status",
|
|
11166
11997
|
description: "Check the status of a handshake session, or verify a completion message (responder side).",
|
|
11167
11998
|
inputSchema: {
|
|
11168
11999
|
type: "object",
|
|
@@ -11212,7 +12043,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11212
12043
|
},
|
|
11213
12044
|
// ─── Streamlined Exchange ─────────────────────────────────────────
|
|
11214
12045
|
{
|
|
11215
|
-
name: "
|
|
12046
|
+
name: "handshake_exchange",
|
|
11216
12047
|
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).",
|
|
11217
12048
|
inputSchema: {
|
|
11218
12049
|
type: "object",
|
|
@@ -11279,7 +12110,7 @@ function createHandshakeTools(config, identityManager, masterKey, auditLog, opti
|
|
|
11279
12110
|
}
|
|
11280
12111
|
},
|
|
11281
12112
|
{
|
|
11282
|
-
name: "
|
|
12113
|
+
name: "handshake_verify_attestation",
|
|
11283
12114
|
description: "Verify a signed attestation artifact from another agent. Checks the Ed25519 signature, temporal validity, and structural integrity.",
|
|
11284
12115
|
inputSchema: {
|
|
11285
12116
|
type: "object",
|
|
@@ -11488,7 +12319,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11488
12319
|
const tools = [
|
|
11489
12320
|
// ─── Peer Management ──────────────────────────────────────────────
|
|
11490
12321
|
{
|
|
11491
|
-
name: "
|
|
12322
|
+
name: "federation_peers",
|
|
11492
12323
|
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.",
|
|
11493
12324
|
inputSchema: {
|
|
11494
12325
|
type: "object",
|
|
@@ -11591,7 +12422,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11591
12422
|
},
|
|
11592
12423
|
// ─── Trust Evaluation ─────────────────────────────────────────────
|
|
11593
12424
|
{
|
|
11594
|
-
name: "
|
|
12425
|
+
name: "federation_trust_evaluate",
|
|
11595
12426
|
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.",
|
|
11596
12427
|
inputSchema: {
|
|
11597
12428
|
type: "object",
|
|
@@ -11626,7 +12457,7 @@ function createFederationTools(auditLog, handshakeResults) {
|
|
|
11626
12457
|
},
|
|
11627
12458
|
// ─── Federation Status ────────────────────────────────────────────
|
|
11628
12459
|
{
|
|
11629
|
-
name: "
|
|
12460
|
+
name: "federation_status",
|
|
11630
12461
|
description: "Overview of federation state: total peers, active connections, trust distribution, and readiness for cross-instance operations.",
|
|
11631
12462
|
inputSchema: {
|
|
11632
12463
|
type: "object",
|
|
@@ -11837,7 +12668,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11837
12668
|
const tools = [
|
|
11838
12669
|
// ─── bridge_commit ─────────────────────────────────────────────────
|
|
11839
12670
|
{
|
|
11840
|
-
name: "
|
|
12671
|
+
name: "bridge_commit",
|
|
11841
12672
|
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.",
|
|
11842
12673
|
inputSchema: {
|
|
11843
12674
|
type: "object",
|
|
@@ -11939,7 +12770,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11939
12770
|
},
|
|
11940
12771
|
// ─── bridge_verify ───────────────────────────────────────────────────
|
|
11941
12772
|
{
|
|
11942
|
-
name: "
|
|
12773
|
+
name: "bridge_verify",
|
|
11943
12774
|
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.",
|
|
11944
12775
|
inputSchema: {
|
|
11945
12776
|
type: "object",
|
|
@@ -11995,7 +12826,7 @@ function createBridgeTools(storage, masterKey, identityManager, auditLog, handsh
|
|
|
11995
12826
|
},
|
|
11996
12827
|
// ─── bridge_attest ───────────────────────────────────────────────────
|
|
11997
12828
|
{
|
|
11998
|
-
name: "
|
|
12829
|
+
name: "bridge_attest",
|
|
11999
12830
|
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.",
|
|
12000
12831
|
inputSchema: {
|
|
12001
12832
|
type: "object",
|
|
@@ -12559,7 +13390,7 @@ function generateGaps(env, l1, l2, l3, l4) {
|
|
|
12559
13390
|
title: "No context gating for outbound inference calls",
|
|
12560
13391
|
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.",
|
|
12561
13392
|
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,
|
|
12562
|
-
sanctuary_solution: "Sanctuary's context gating (sanctuary/context_gate_set_policy +
|
|
13393
|
+
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.",
|
|
12563
13394
|
incident_class: INCIDENT_CONTEXT_LEAKAGE
|
|
12564
13395
|
});
|
|
12565
13396
|
}
|
|
@@ -12571,7 +13402,7 @@ function generateGaps(env, l1, l2, l3, l4) {
|
|
|
12571
13402
|
title: "No audit trail",
|
|
12572
13403
|
description: "No audit trail exists for tool call history. There is no record of what operations were executed, when, or by whom.",
|
|
12573
13404
|
openclaw_relevance: null,
|
|
12574
|
-
sanctuary_solution: "Sanctuary maintains an encrypted audit log of all operations, queryable via
|
|
13405
|
+
sanctuary_solution: "Sanctuary maintains an encrypted audit log of all operations, queryable via monitor_audit_log.",
|
|
12575
13406
|
incident_class: INCIDENT_CLAUDE_CODE_LEAK
|
|
12576
13407
|
});
|
|
12577
13408
|
}
|
|
@@ -12606,7 +13437,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12606
13437
|
recs.push({
|
|
12607
13438
|
priority: 1,
|
|
12608
13439
|
action: "Create a cryptographic identity \u2014 your agent's foundation for all sovereignty operations",
|
|
12609
|
-
tool: "
|
|
13440
|
+
tool: "identity_create",
|
|
12610
13441
|
effort: "immediate",
|
|
12611
13442
|
impact: "critical"
|
|
12612
13443
|
});
|
|
@@ -12615,7 +13446,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12615
13446
|
recs.push({
|
|
12616
13447
|
priority: 2,
|
|
12617
13448
|
action: "Migrate plaintext agent state to Sanctuary's encrypted store",
|
|
12618
|
-
tool: "
|
|
13449
|
+
tool: "state_write",
|
|
12619
13450
|
effort: "minutes",
|
|
12620
13451
|
impact: "critical"
|
|
12621
13452
|
});
|
|
@@ -12623,7 +13454,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12623
13454
|
recs.push({
|
|
12624
13455
|
priority: 3,
|
|
12625
13456
|
action: "Generate a Sovereignty Health Report to present to counterparties",
|
|
12626
|
-
tool: "
|
|
13457
|
+
tool: "shr_generate",
|
|
12627
13458
|
effort: "immediate",
|
|
12628
13459
|
impact: "high"
|
|
12629
13460
|
});
|
|
@@ -12631,7 +13462,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12631
13462
|
recs.push({
|
|
12632
13463
|
priority: 4,
|
|
12633
13464
|
action: "Enable the three-tier Principal Policy gate for graduated approval",
|
|
12634
|
-
tool: "
|
|
13465
|
+
tool: "principal_policy_view",
|
|
12635
13466
|
effort: "minutes",
|
|
12636
13467
|
impact: "high"
|
|
12637
13468
|
});
|
|
@@ -12640,7 +13471,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12640
13471
|
recs.push({
|
|
12641
13472
|
priority: 5,
|
|
12642
13473
|
action: "Configure context gating to control what flows to LLM providers",
|
|
12643
|
-
tool: "
|
|
13474
|
+
tool: "context_gate_set_policy",
|
|
12644
13475
|
effort: "minutes",
|
|
12645
13476
|
impact: "high"
|
|
12646
13477
|
});
|
|
@@ -12649,7 +13480,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12649
13480
|
recs.push({
|
|
12650
13481
|
priority: 6,
|
|
12651
13482
|
action: "Start recording reputation attestations from completed interactions",
|
|
12652
|
-
tool: "
|
|
13483
|
+
tool: "reputation_record",
|
|
12653
13484
|
effort: "minutes",
|
|
12654
13485
|
impact: "medium"
|
|
12655
13486
|
});
|
|
@@ -12658,7 +13489,7 @@ function generateRecommendations(env, l1, l2, l3, l4) {
|
|
|
12658
13489
|
recs.push({
|
|
12659
13490
|
priority: 7,
|
|
12660
13491
|
action: "Configure selective disclosure policies for data sharing",
|
|
12661
|
-
tool: "
|
|
13492
|
+
tool: "disclosure_set_policy",
|
|
12662
13493
|
effort: "hours",
|
|
12663
13494
|
impact: "medium"
|
|
12664
13495
|
});
|
|
@@ -12798,7 +13629,7 @@ function wordWrap(text, maxWidth) {
|
|
|
12798
13629
|
function createAuditTools(config) {
|
|
12799
13630
|
const tools = [
|
|
12800
13631
|
{
|
|
12801
|
-
name: "
|
|
13632
|
+
name: "sovereignty_audit",
|
|
12802
13633
|
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.",
|
|
12803
13634
|
inputSchema: {
|
|
12804
13635
|
type: "object",
|
|
@@ -12826,6 +13657,302 @@ function createAuditTools(config) {
|
|
|
12826
13657
|
return { tools };
|
|
12827
13658
|
}
|
|
12828
13659
|
|
|
13660
|
+
// src/audit/siem-formatter.ts
|
|
13661
|
+
function parseGateDecision(details) {
|
|
13662
|
+
if (!details || typeof details.gate_decision !== "string") {
|
|
13663
|
+
return "auto-allow";
|
|
13664
|
+
}
|
|
13665
|
+
const decision = details.gate_decision.toLowerCase();
|
|
13666
|
+
if (decision === "approve" || decision === "deny") {
|
|
13667
|
+
return decision;
|
|
13668
|
+
}
|
|
13669
|
+
return "auto-allow";
|
|
13670
|
+
}
|
|
13671
|
+
function parseTier(details) {
|
|
13672
|
+
if (!details || typeof details.tier !== "number") {
|
|
13673
|
+
return 3;
|
|
13674
|
+
}
|
|
13675
|
+
return Math.max(1, Math.min(3, details.tier));
|
|
13676
|
+
}
|
|
13677
|
+
function parseSessionId(details) {
|
|
13678
|
+
if (!details || typeof details.session_id !== "string") {
|
|
13679
|
+
return "unknown";
|
|
13680
|
+
}
|
|
13681
|
+
return details.session_id;
|
|
13682
|
+
}
|
|
13683
|
+
function parseAgentDid(details) {
|
|
13684
|
+
if (!details || typeof details.agent_did !== "string") {
|
|
13685
|
+
return "unknown";
|
|
13686
|
+
}
|
|
13687
|
+
return details.agent_did;
|
|
13688
|
+
}
|
|
13689
|
+
function gateToCEFSeverity(decision, tier) {
|
|
13690
|
+
if (decision === "deny") {
|
|
13691
|
+
return 8;
|
|
13692
|
+
}
|
|
13693
|
+
if (decision === "approve") {
|
|
13694
|
+
if (tier === 1) return 5;
|
|
13695
|
+
if (tier === 2) return 3;
|
|
13696
|
+
}
|
|
13697
|
+
return 1;
|
|
13698
|
+
}
|
|
13699
|
+
function formatAsCEF(entry, options) {
|
|
13700
|
+
const version = "0";
|
|
13701
|
+
const vendor = "Sanctuary";
|
|
13702
|
+
const product = "MCP-Server";
|
|
13703
|
+
const productVersion = "0.7.0";
|
|
13704
|
+
const decision = parseGateDecision(entry.details);
|
|
13705
|
+
const tier = parseTier(entry.details);
|
|
13706
|
+
const sessionId = parseSessionId(entry.details);
|
|
13707
|
+
const agentDid = parseAgentDid(entry.details);
|
|
13708
|
+
const severity = gateToCEFSeverity(decision, tier);
|
|
13709
|
+
const signatureId = entry.operation.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
13710
|
+
const description = `Sanctuary ${entry.operation}`;
|
|
13711
|
+
const extensions = [
|
|
13712
|
+
`src=${agentDid}`,
|
|
13713
|
+
`act=${entry.operation}`,
|
|
13714
|
+
`outcome=${decision}`,
|
|
13715
|
+
`tier=${tier}`,
|
|
13716
|
+
`cs1=${sessionId}`,
|
|
13717
|
+
`cs1Label=SessionId`,
|
|
13718
|
+
`rt=${new Date(entry.timestamp).getTime()}`,
|
|
13719
|
+
`layer=${entry.layer}`,
|
|
13720
|
+
`result=${entry.result}`
|
|
13721
|
+
];
|
|
13722
|
+
return `CEF:${version}|${vendor}|${product}|${productVersion}|${signatureId}|${description}|${severity}|${extensions.join(" ")}`;
|
|
13723
|
+
}
|
|
13724
|
+
function gateToOCSFStatus(decision, result) {
|
|
13725
|
+
return decision === "deny" || result === "failure" ? 2 : 1;
|
|
13726
|
+
}
|
|
13727
|
+
function gateToCOCSFSeverity(decision, tier) {
|
|
13728
|
+
if (decision === "deny") {
|
|
13729
|
+
return 4;
|
|
13730
|
+
}
|
|
13731
|
+
if (decision === "approve") {
|
|
13732
|
+
if (tier === 1) return 3;
|
|
13733
|
+
if (tier === 2) return 2;
|
|
13734
|
+
}
|
|
13735
|
+
return 1;
|
|
13736
|
+
}
|
|
13737
|
+
function gateToOCSFDisposition(decision) {
|
|
13738
|
+
return decision === "deny" ? 2 : 1;
|
|
13739
|
+
}
|
|
13740
|
+
function formatAsOCSF(entry) {
|
|
13741
|
+
const decision = parseGateDecision(entry.details);
|
|
13742
|
+
const tier = parseTier(entry.details);
|
|
13743
|
+
const agentDid = parseAgentDid(entry.details);
|
|
13744
|
+
const timestamp = new Date(entry.timestamp).getTime();
|
|
13745
|
+
const statusId = gateToOCSFStatus(decision, entry.result);
|
|
13746
|
+
const severityId = gateToCOCSFSeverity(decision, tier);
|
|
13747
|
+
const dispositionId = gateToOCSFDisposition(decision);
|
|
13748
|
+
return {
|
|
13749
|
+
class_uid: 3001,
|
|
13750
|
+
class_name: "API Activity",
|
|
13751
|
+
category_uid: 3,
|
|
13752
|
+
category_name: "Application Activity",
|
|
13753
|
+
severity_id: severityId,
|
|
13754
|
+
time: timestamp,
|
|
13755
|
+
activity_id: 1,
|
|
13756
|
+
activity_name: "API Call",
|
|
13757
|
+
actor: {
|
|
13758
|
+
user: {
|
|
13759
|
+
uid: agentDid
|
|
13760
|
+
}
|
|
13761
|
+
},
|
|
13762
|
+
api: {
|
|
13763
|
+
operation: entry.operation,
|
|
13764
|
+
service: {
|
|
13765
|
+
name: "sanctuary-mcp"
|
|
13766
|
+
}
|
|
13767
|
+
},
|
|
13768
|
+
status_id: statusId,
|
|
13769
|
+
disposition_id: dispositionId,
|
|
13770
|
+
metadata: {
|
|
13771
|
+
version: "1.3.0",
|
|
13772
|
+
product: {
|
|
13773
|
+
name: "Sanctuary Framework",
|
|
13774
|
+
vendor_name: "Erik Newton",
|
|
13775
|
+
version: "0.7.0"
|
|
13776
|
+
}
|
|
13777
|
+
}
|
|
13778
|
+
};
|
|
13779
|
+
}
|
|
13780
|
+
|
|
13781
|
+
// src/audit/siem-tools.ts
|
|
13782
|
+
function createSIEMTools(auditLog) {
|
|
13783
|
+
const tools = [
|
|
13784
|
+
{
|
|
13785
|
+
name: "audit_export_siem",
|
|
13786
|
+
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.",
|
|
13787
|
+
inputSchema: {
|
|
13788
|
+
type: "object",
|
|
13789
|
+
properties: {
|
|
13790
|
+
format: {
|
|
13791
|
+
type: "string",
|
|
13792
|
+
enum: ["cef", "ocsf"],
|
|
13793
|
+
description: 'Output format: "cef" (Common Event Format, newline-delimited) or "ocsf" (Open Cybersecurity Schema Framework, JSON array)'
|
|
13794
|
+
},
|
|
13795
|
+
since: {
|
|
13796
|
+
type: "string",
|
|
13797
|
+
description: "Optional ISO 8601 timestamp. Export only events on or after this time. Defaults to 24 hours ago."
|
|
13798
|
+
},
|
|
13799
|
+
until: {
|
|
13800
|
+
type: "string",
|
|
13801
|
+
description: "Optional ISO 8601 timestamp. Export only events before this time. Defaults to now."
|
|
13802
|
+
},
|
|
13803
|
+
limit: {
|
|
13804
|
+
type: "number",
|
|
13805
|
+
description: "Maximum number of events to export (default 100, max 1000). Set to 1000 for bulk exports to SIEMs."
|
|
13806
|
+
},
|
|
13807
|
+
filter_tool: {
|
|
13808
|
+
type: "string",
|
|
13809
|
+
description: 'Optional. Export only events from this tool name (e.g., "sovereignty_audit", "state_set"). Case-insensitive substring matching.'
|
|
13810
|
+
},
|
|
13811
|
+
filter_decision: {
|
|
13812
|
+
type: "string",
|
|
13813
|
+
enum: ["approve", "deny", "auto-allow"],
|
|
13814
|
+
description: 'Optional. Export only events with this gate decision: "approve" (manual approval), "deny" (blocked), or "auto-allow" (Tier 3 auto-allowed).'
|
|
13815
|
+
},
|
|
13816
|
+
filter_layer: {
|
|
13817
|
+
type: "string",
|
|
13818
|
+
enum: ["l1", "l2", "l3", "l4"],
|
|
13819
|
+
description: "Optional. Export only events from this sovereignty layer (L1=Cognitive, L2=Operational, L3=Disclosure, L4=Reputation)."
|
|
13820
|
+
},
|
|
13821
|
+
filter_result: {
|
|
13822
|
+
type: "string",
|
|
13823
|
+
enum: ["success", "failure"],
|
|
13824
|
+
description: 'Optional. Export only events with this result: "success" or "failure".'
|
|
13825
|
+
}
|
|
13826
|
+
},
|
|
13827
|
+
required: ["format"]
|
|
13828
|
+
},
|
|
13829
|
+
handler: async (args) => {
|
|
13830
|
+
const format = String(args.format || "").toLowerCase();
|
|
13831
|
+
if (format !== "cef" && format !== "ocsf") {
|
|
13832
|
+
return {
|
|
13833
|
+
content: [
|
|
13834
|
+
{
|
|
13835
|
+
type: "text",
|
|
13836
|
+
text: JSON.stringify({
|
|
13837
|
+
error: "Invalid format. Must be 'cef' or 'ocsf'."
|
|
13838
|
+
})
|
|
13839
|
+
}
|
|
13840
|
+
]
|
|
13841
|
+
};
|
|
13842
|
+
}
|
|
13843
|
+
let since;
|
|
13844
|
+
if (args.since) {
|
|
13845
|
+
since = String(args.since);
|
|
13846
|
+
const sinceDate = new Date(since);
|
|
13847
|
+
if (isNaN(sinceDate.getTime())) {
|
|
13848
|
+
return {
|
|
13849
|
+
content: [
|
|
13850
|
+
{
|
|
13851
|
+
type: "text",
|
|
13852
|
+
text: JSON.stringify({
|
|
13853
|
+
error: `Invalid 'since' timestamp: ${since}. Must be ISO 8601.`
|
|
13854
|
+
})
|
|
13855
|
+
}
|
|
13856
|
+
]
|
|
13857
|
+
};
|
|
13858
|
+
}
|
|
13859
|
+
} else {
|
|
13860
|
+
const now = /* @__PURE__ */ new Date();
|
|
13861
|
+
const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1e3);
|
|
13862
|
+
since = oneDayAgo.toISOString();
|
|
13863
|
+
}
|
|
13864
|
+
let until;
|
|
13865
|
+
if (args.until) {
|
|
13866
|
+
until = String(args.until);
|
|
13867
|
+
const untilDate = new Date(until);
|
|
13868
|
+
if (isNaN(untilDate.getTime())) {
|
|
13869
|
+
return {
|
|
13870
|
+
content: [
|
|
13871
|
+
{
|
|
13872
|
+
type: "text",
|
|
13873
|
+
text: JSON.stringify({
|
|
13874
|
+
error: `Invalid 'until' timestamp: ${until}. Must be ISO 8601.`
|
|
13875
|
+
})
|
|
13876
|
+
}
|
|
13877
|
+
]
|
|
13878
|
+
};
|
|
13879
|
+
}
|
|
13880
|
+
}
|
|
13881
|
+
let limit = 100;
|
|
13882
|
+
if (typeof args.limit === "number") {
|
|
13883
|
+
limit = Math.max(1, Math.min(1e3, args.limit));
|
|
13884
|
+
}
|
|
13885
|
+
const filterTool = args.filter_tool ? String(args.filter_tool).toLowerCase() : void 0;
|
|
13886
|
+
const filterDecision = args.filter_decision ? String(args.filter_decision).toLowerCase() : void 0;
|
|
13887
|
+
const filterLayer = args.filter_layer ? String(args.filter_layer).toLowerCase() : void 0;
|
|
13888
|
+
const filterResult = args.filter_result ? String(args.filter_result).toLowerCase() : void 0;
|
|
13889
|
+
const result = await auditLog.query({
|
|
13890
|
+
since,
|
|
13891
|
+
layer: filterLayer,
|
|
13892
|
+
operation_type: void 0,
|
|
13893
|
+
// Will filter after
|
|
13894
|
+
limit
|
|
13895
|
+
});
|
|
13896
|
+
let filtered = result.entries;
|
|
13897
|
+
if (filterTool) {
|
|
13898
|
+
filtered = filtered.filter(
|
|
13899
|
+
(e) => e.operation.toLowerCase().includes(filterTool)
|
|
13900
|
+
);
|
|
13901
|
+
}
|
|
13902
|
+
if (filterDecision) {
|
|
13903
|
+
filtered = filtered.filter((e) => {
|
|
13904
|
+
const decision = String(e.details?.gate_decision || "auto-allow").toLowerCase();
|
|
13905
|
+
return decision === filterDecision;
|
|
13906
|
+
});
|
|
13907
|
+
}
|
|
13908
|
+
if (filterResult) {
|
|
13909
|
+
filtered = filtered.filter((e) => e.result === filterResult);
|
|
13910
|
+
}
|
|
13911
|
+
if (until) {
|
|
13912
|
+
const untilDate = new Date(until);
|
|
13913
|
+
filtered = filtered.filter((e) => new Date(e.timestamp) < untilDate);
|
|
13914
|
+
}
|
|
13915
|
+
let output;
|
|
13916
|
+
if (format === "cef") {
|
|
13917
|
+
const cefLines = filtered.map((entry) => formatAsCEF(entry));
|
|
13918
|
+
output = cefLines.join("\n");
|
|
13919
|
+
} else {
|
|
13920
|
+
const ocsfObjects = filtered.map((entry) => formatAsOCSF(entry));
|
|
13921
|
+
output = JSON.stringify(ocsfObjects, null, 2);
|
|
13922
|
+
}
|
|
13923
|
+
return {
|
|
13924
|
+
content: [
|
|
13925
|
+
{
|
|
13926
|
+
type: "text",
|
|
13927
|
+
text: JSON.stringify({
|
|
13928
|
+
format,
|
|
13929
|
+
count: filtered.length,
|
|
13930
|
+
total_available: result.total,
|
|
13931
|
+
time_range: {
|
|
13932
|
+
since,
|
|
13933
|
+
until: until || (/* @__PURE__ */ new Date()).toISOString()
|
|
13934
|
+
},
|
|
13935
|
+
filters: {
|
|
13936
|
+
tool: filterTool,
|
|
13937
|
+
decision: filterDecision,
|
|
13938
|
+
layer: filterLayer,
|
|
13939
|
+
result: filterResult
|
|
13940
|
+
},
|
|
13941
|
+
note: format === "cef" ? `${filtered.length} CEF events (newline-delimited). Each line is a complete CEF event.` : `${filtered.length} OCSF objects in JSON array format.`
|
|
13942
|
+
})
|
|
13943
|
+
},
|
|
13944
|
+
{
|
|
13945
|
+
type: "text",
|
|
13946
|
+
text: output
|
|
13947
|
+
}
|
|
13948
|
+
]
|
|
13949
|
+
};
|
|
13950
|
+
}
|
|
13951
|
+
}
|
|
13952
|
+
];
|
|
13953
|
+
return { tools };
|
|
13954
|
+
}
|
|
13955
|
+
|
|
12829
13956
|
// src/l2-operational/context-gate.ts
|
|
12830
13957
|
init_encryption();
|
|
12831
13958
|
init_encoding();
|
|
@@ -13822,13 +14949,17 @@ var ContextGateEnforcer = class {
|
|
|
13822
14949
|
* Check if a tool should be filtered based on bypass prefixes.
|
|
13823
14950
|
*
|
|
13824
14951
|
* SEC-033: Uses exact namespace component matching, not bare startsWith().
|
|
13825
|
-
* A prefix of "
|
|
13826
|
-
*
|
|
13827
|
-
*
|
|
13828
|
-
*
|
|
14952
|
+
* A prefix of "proxy/" matches "proxy/server/tool" but NOT "proxyevil/steal".
|
|
14953
|
+
* The prefix must match exactly up to its length, and the prefix must end
|
|
14954
|
+
* with "/" to enforce namespace boundaries (if it doesn't, we add one).
|
|
14955
|
+
*
|
|
14956
|
+
* Special sentinel: "*" bypasses ALL tools (used when all Sanctuary-internal
|
|
14957
|
+
* tools should skip context gating — the default). Only proxy/external tools
|
|
14958
|
+
* should be filtered in production.
|
|
13829
14959
|
*/
|
|
13830
14960
|
shouldFilter(toolName) {
|
|
13831
14961
|
for (const prefix of this.config.bypass_prefixes) {
|
|
14962
|
+
if (prefix === "*") return false;
|
|
13832
14963
|
const safePrefix = prefix.endsWith("/") ? prefix : prefix + "/";
|
|
13833
14964
|
if (toolName === safePrefix.slice(0, -1) || toolName.startsWith(safePrefix)) {
|
|
13834
14965
|
return false;
|
|
@@ -13925,8 +15056,8 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
13925
15056
|
const enforcerConfig = {
|
|
13926
15057
|
enabled: false,
|
|
13927
15058
|
// Off by default; agents must explicitly enable it
|
|
13928
|
-
bypass_prefixes: ["
|
|
13929
|
-
// Skip internal tools
|
|
15059
|
+
bypass_prefixes: ["*"],
|
|
15060
|
+
// Skip all Sanctuary-internal tools; only proxy/ tools get filtered
|
|
13930
15061
|
log_only: false,
|
|
13931
15062
|
// Filter immediately
|
|
13932
15063
|
on_deny: "block"
|
|
@@ -13936,7 +15067,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
13936
15067
|
const tools = [
|
|
13937
15068
|
// ── Set Policy ──────────────────────────────────────────────────
|
|
13938
15069
|
{
|
|
13939
|
-
name: "
|
|
15070
|
+
name: "context_gate_set_policy",
|
|
13940
15071
|
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.",
|
|
13941
15072
|
inputSchema: {
|
|
13942
15073
|
type: "object",
|
|
@@ -14045,13 +15176,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14045
15176
|
rules: policy.rules,
|
|
14046
15177
|
default_action: policy.default_action,
|
|
14047
15178
|
created_at: policy.created_at,
|
|
14048
|
-
message: "Context-gating policy created. Use
|
|
15179
|
+
message: "Context-gating policy created. Use context_gate_filter to apply this policy before making outbound calls."
|
|
14049
15180
|
});
|
|
14050
15181
|
}
|
|
14051
15182
|
},
|
|
14052
15183
|
// ── Apply Template ───────────────────────────────────────────────
|
|
14053
15184
|
{
|
|
14054
|
-
name: "
|
|
15185
|
+
name: "context_gate_apply_template",
|
|
14055
15186
|
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.",
|
|
14056
15187
|
inputSchema: {
|
|
14057
15188
|
type: "object",
|
|
@@ -14100,13 +15231,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14100
15231
|
rules: policy.rules,
|
|
14101
15232
|
default_action: policy.default_action,
|
|
14102
15233
|
created_at: policy.created_at,
|
|
14103
|
-
message: "Template applied. Use
|
|
15234
|
+
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."
|
|
14104
15235
|
});
|
|
14105
15236
|
}
|
|
14106
15237
|
},
|
|
14107
15238
|
// ── Recommend Policy ────────────────────────────────────────────
|
|
14108
15239
|
{
|
|
14109
|
-
name: "
|
|
15240
|
+
name: "context_gate_recommend",
|
|
14110
15241
|
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.",
|
|
14111
15242
|
inputSchema: {
|
|
14112
15243
|
type: "object",
|
|
@@ -14143,7 +15274,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14143
15274
|
});
|
|
14144
15275
|
return toolResult({
|
|
14145
15276
|
...recommendation,
|
|
14146
|
-
next_steps: "Review the classifications above. If they look correct, you can apply them directly with
|
|
15277
|
+
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.",
|
|
14147
15278
|
available_templates: listTemplateIds().map((id) => {
|
|
14148
15279
|
const t = TEMPLATES[id];
|
|
14149
15280
|
return { id, name: t.name, description: t.description };
|
|
@@ -14153,7 +15284,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14153
15284
|
},
|
|
14154
15285
|
// ── Filter Context ──────────────────────────────────────────────
|
|
14155
15286
|
{
|
|
14156
|
-
name: "
|
|
15287
|
+
name: "context_gate_filter",
|
|
14157
15288
|
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.",
|
|
14158
15289
|
inputSchema: {
|
|
14159
15290
|
type: "object",
|
|
@@ -14259,7 +15390,7 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14259
15390
|
},
|
|
14260
15391
|
// ── List Policies ───────────────────────────────────────────────
|
|
14261
15392
|
{
|
|
14262
|
-
name: "
|
|
15393
|
+
name: "context_gate_list_policies",
|
|
14263
15394
|
description: "List all configured context-gating policies. Returns policy IDs, names, rule summaries, and default actions.",
|
|
14264
15395
|
inputSchema: {
|
|
14265
15396
|
type: "object",
|
|
@@ -14282,13 +15413,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14282
15413
|
updated_at: p.updated_at
|
|
14283
15414
|
})),
|
|
14284
15415
|
count: policies.length,
|
|
14285
|
-
message: policies.length === 0 ? "No context-gating policies configured. Use
|
|
15416
|
+
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.`
|
|
14286
15417
|
});
|
|
14287
15418
|
}
|
|
14288
15419
|
},
|
|
14289
15420
|
// ── Enforcer Status ─────────────────────────────────────────────────
|
|
14290
15421
|
{
|
|
14291
|
-
name: "
|
|
15422
|
+
name: "context_gate_enforcer_status",
|
|
14292
15423
|
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.",
|
|
14293
15424
|
inputSchema: {
|
|
14294
15425
|
type: "object",
|
|
@@ -14309,13 +15440,13 @@ function createContextGateTools(storage, masterKey, auditLog) {
|
|
|
14309
15440
|
return toolResult({
|
|
14310
15441
|
enforcer_status: status,
|
|
14311
15442
|
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."),
|
|
14312
|
-
guidance: status.stats.calls_inspected > 0 ? `Over ${status.stats.calls_inspected} tool calls, ${status.stats.fields_redacted} sensitive fields were redacted. Use
|
|
15443
|
+
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."
|
|
14313
15444
|
});
|
|
14314
15445
|
}
|
|
14315
15446
|
},
|
|
14316
15447
|
// ── Enforcer Configuration ──────────────────────────────────────────
|
|
14317
15448
|
{
|
|
14318
|
-
name: "
|
|
15449
|
+
name: "context_gate_enforcer_configure",
|
|
14319
15450
|
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.",
|
|
14320
15451
|
inputSchema: {
|
|
14321
15452
|
type: "object",
|
|
@@ -14638,7 +15769,7 @@ function assessL2Hardening(storagePath) {
|
|
|
14638
15769
|
function createL2HardeningTools(storagePath, auditLog) {
|
|
14639
15770
|
return [
|
|
14640
15771
|
{
|
|
14641
|
-
name: "
|
|
15772
|
+
name: "l2_hardening_status",
|
|
14642
15773
|
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.",
|
|
14643
15774
|
inputSchema: {
|
|
14644
15775
|
type: "object",
|
|
@@ -14706,7 +15837,7 @@ function createL2HardeningTools(storagePath, auditLog) {
|
|
|
14706
15837
|
}
|
|
14707
15838
|
},
|
|
14708
15839
|
{
|
|
14709
|
-
name: "
|
|
15840
|
+
name: "l2_verify_isolation",
|
|
14710
15841
|
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.",
|
|
14711
15842
|
inputSchema: {
|
|
14712
15843
|
type: "object",
|
|
@@ -14985,7 +16116,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
14985
16116
|
const tools = [
|
|
14986
16117
|
// ── Get Profile ──────────────────────────────────────────────────
|
|
14987
16118
|
{
|
|
14988
|
-
name: "
|
|
16119
|
+
name: "sovereignty_profile_get",
|
|
14989
16120
|
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.",
|
|
14990
16121
|
inputSchema: {
|
|
14991
16122
|
type: "object",
|
|
@@ -15004,7 +16135,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
15004
16135
|
},
|
|
15005
16136
|
// ── Update Profile ───────────────────────────────────────────────
|
|
15006
16137
|
{
|
|
15007
|
-
name: "
|
|
16138
|
+
name: "sovereignty_profile_update",
|
|
15008
16139
|
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.",
|
|
15009
16140
|
inputSchema: {
|
|
15010
16141
|
type: "object",
|
|
@@ -15085,7 +16216,7 @@ function createSovereigntyProfileTools(profileStore, auditLog) {
|
|
|
15085
16216
|
},
|
|
15086
16217
|
// ── Generate System Prompt ───────────────────────────────────────
|
|
15087
16218
|
{
|
|
15088
|
-
name: "
|
|
16219
|
+
name: "sovereignty_profile_generate_prompt",
|
|
15089
16220
|
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.",
|
|
15090
16221
|
inputSchema: {
|
|
15091
16222
|
type: "object",
|
|
@@ -15479,6 +16610,7 @@ var ProxyRouter = class {
|
|
|
15479
16610
|
confidence: injectionResult.confidence,
|
|
15480
16611
|
latency_ms: Date.now() - start
|
|
15481
16612
|
}, "failure");
|
|
16613
|
+
this.notifyProxyCall(proxyName, serverName, "blocked", "injection_detected", tier);
|
|
15482
16614
|
return toolResult({
|
|
15483
16615
|
error: "Operation not permitted",
|
|
15484
16616
|
proxy: true
|
|
@@ -15509,6 +16641,7 @@ var ProxyRouter = class {
|
|
|
15509
16641
|
reason: govResult.reason,
|
|
15510
16642
|
latency_ms: Date.now() - start
|
|
15511
16643
|
}, "failure");
|
|
16644
|
+
this.notifyProxyCall(proxyName, serverName, "blocked", govResult.reason, tier);
|
|
15512
16645
|
return toolResult({
|
|
15513
16646
|
error: "Operation not permitted",
|
|
15514
16647
|
proxy: true,
|
|
@@ -15543,6 +16676,7 @@ var ProxyRouter = class {
|
|
|
15543
16676
|
decision: "allowed",
|
|
15544
16677
|
latency_ms: latencyMs
|
|
15545
16678
|
});
|
|
16679
|
+
this.notifyProxyCall(proxyName, serverName, "allowed", void 0, tier);
|
|
15546
16680
|
return this.normalizeResponse(result);
|
|
15547
16681
|
} catch (err) {
|
|
15548
16682
|
const latencyMs = Date.now() - start;
|
|
@@ -15562,6 +16696,7 @@ var ProxyRouter = class {
|
|
|
15562
16696
|
error: errorMessage,
|
|
15563
16697
|
latency_ms: latencyMs
|
|
15564
16698
|
}, "failure");
|
|
16699
|
+
this.notifyProxyCall(proxyName, serverName, "error", errorMessage, tier);
|
|
15565
16700
|
return {
|
|
15566
16701
|
content: [{
|
|
15567
16702
|
type: "text",
|
|
@@ -15576,6 +16711,24 @@ var ProxyRouter = class {
|
|
|
15576
16711
|
}
|
|
15577
16712
|
};
|
|
15578
16713
|
}
|
|
16714
|
+
/**
|
|
16715
|
+
* Notify the onProxyCall callback if configured.
|
|
16716
|
+
*/
|
|
16717
|
+
notifyProxyCall(tool, server, decision, reason, tier) {
|
|
16718
|
+
if (this.options.onProxyCall) {
|
|
16719
|
+
try {
|
|
16720
|
+
this.options.onProxyCall({
|
|
16721
|
+
tool,
|
|
16722
|
+
server,
|
|
16723
|
+
decision,
|
|
16724
|
+
reason,
|
|
16725
|
+
tier,
|
|
16726
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
16727
|
+
});
|
|
16728
|
+
} catch {
|
|
16729
|
+
}
|
|
16730
|
+
}
|
|
16731
|
+
}
|
|
15579
16732
|
/**
|
|
15580
16733
|
* Call an upstream tool with a timeout.
|
|
15581
16734
|
*/
|
|
@@ -15848,7 +17001,7 @@ function createGovernorTools(governor, auditLog) {
|
|
|
15848
17001
|
const tools = [
|
|
15849
17002
|
// ── Governor Status ─────────────────────────────────────────────
|
|
15850
17003
|
{
|
|
15851
|
-
name: "
|
|
17004
|
+
name: "governor_status",
|
|
15852
17005
|
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.",
|
|
15853
17006
|
inputSchema: {
|
|
15854
17007
|
type: "object",
|
|
@@ -15888,7 +17041,7 @@ function createGovernorTools(governor, auditLog) {
|
|
|
15888
17041
|
},
|
|
15889
17042
|
// ── Governor Reset ──────────────────────────────────────────────
|
|
15890
17043
|
{
|
|
15891
|
-
name: "
|
|
17044
|
+
name: "governor_reset",
|
|
15892
17045
|
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.",
|
|
15893
17046
|
inputSchema: {
|
|
15894
17047
|
type: "object",
|
|
@@ -15978,7 +17131,7 @@ function createSanctuaryTools(opts) {
|
|
|
15978
17131
|
const tools = [
|
|
15979
17132
|
// ─── sanctuary_bootstrap ───────────────────────────────────────────
|
|
15980
17133
|
{
|
|
15981
|
-
name: "
|
|
17134
|
+
name: "sanctuary_bootstrap",
|
|
15982
17135
|
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.",
|
|
15983
17136
|
inputSchema: {
|
|
15984
17137
|
type: "object",
|
|
@@ -16116,7 +17269,7 @@ function createSanctuaryTools(opts) {
|
|
|
16116
17269
|
},
|
|
16117
17270
|
// ─── sanctuary_policy_status ───────────────────────────────────────
|
|
16118
17271
|
{
|
|
16119
|
-
name: "
|
|
17272
|
+
name: "sanctuary_policy_status",
|
|
16120
17273
|
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).",
|
|
16121
17274
|
inputSchema: {
|
|
16122
17275
|
type: "object",
|
|
@@ -16146,7 +17299,7 @@ function createSanctuaryTools(opts) {
|
|
|
16146
17299
|
},
|
|
16147
17300
|
// ─── sanctuary_export_identity_bundle ──────────────────────────────
|
|
16148
17301
|
{
|
|
16149
|
-
name: "
|
|
17302
|
+
name: "sanctuary_export_identity_bundle",
|
|
16150
17303
|
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.",
|
|
16151
17304
|
inputSchema: {
|
|
16152
17305
|
type: "object",
|
|
@@ -16215,7 +17368,7 @@ function createSanctuaryTools(opts) {
|
|
|
16215
17368
|
},
|
|
16216
17369
|
// ─── sanctuary_link_to_human ───────────────────────────────────────
|
|
16217
17370
|
{
|
|
16218
|
-
name: "
|
|
17371
|
+
name: "sanctuary_link_to_human",
|
|
16219
17372
|
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.",
|
|
16220
17373
|
inputSchema: {
|
|
16221
17374
|
type: "object",
|
|
@@ -16270,7 +17423,7 @@ function createSanctuaryTools(opts) {
|
|
|
16270
17423
|
},
|
|
16271
17424
|
// ─── sanctuary_sign_challenge ──────────────────────────────────────
|
|
16272
17425
|
{
|
|
16273
|
-
name: "
|
|
17426
|
+
name: "sanctuary_sign_challenge",
|
|
16274
17427
|
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.",
|
|
16275
17428
|
inputSchema: {
|
|
16276
17429
|
type: "object",
|
|
@@ -16644,7 +17797,7 @@ async function createSanctuaryServer(options) {
|
|
|
16644
17797
|
}
|
|
16645
17798
|
const l2Tools = [
|
|
16646
17799
|
{
|
|
16647
|
-
name: "
|
|
17800
|
+
name: "exec_attest",
|
|
16648
17801
|
description: "Generate an attestation of the current execution environment, including sovereignty assessment and degradation report.",
|
|
16649
17802
|
inputSchema: {
|
|
16650
17803
|
type: "object",
|
|
@@ -16695,7 +17848,7 @@ async function createSanctuaryServer(options) {
|
|
|
16695
17848
|
}
|
|
16696
17849
|
},
|
|
16697
17850
|
{
|
|
16698
|
-
name: "
|
|
17851
|
+
name: "monitor_health",
|
|
16699
17852
|
description: "Sanctuary Health Report (SHR) \u2014 standardized sovereignty status.",
|
|
16700
17853
|
inputSchema: { type: "object", properties: {} },
|
|
16701
17854
|
handler: async () => {
|
|
@@ -16744,7 +17897,7 @@ async function createSanctuaryServer(options) {
|
|
|
16744
17897
|
}
|
|
16745
17898
|
},
|
|
16746
17899
|
{
|
|
16747
|
-
name: "
|
|
17900
|
+
name: "monitor_audit_log",
|
|
16748
17901
|
description: "Query the sovereignty audit log.",
|
|
16749
17902
|
inputSchema: {
|
|
16750
17903
|
type: "object",
|
|
@@ -16770,7 +17923,7 @@ async function createSanctuaryServer(options) {
|
|
|
16770
17923
|
}
|
|
16771
17924
|
];
|
|
16772
17925
|
const manifestTool = {
|
|
16773
|
-
name: "
|
|
17926
|
+
name: "manifest",
|
|
16774
17927
|
description: "Generate the Sanctuary Interface Manifest (SIM) \u2014 a machine-readable declaration of this server's capabilities.",
|
|
16775
17928
|
inputSchema: { type: "object", properties: {} },
|
|
16776
17929
|
handler: async () => {
|
|
@@ -16882,6 +18035,7 @@ async function createSanctuaryServer(options) {
|
|
|
16882
18035
|
handshakeResults
|
|
16883
18036
|
);
|
|
16884
18037
|
const { tools: auditTools } = createAuditTools(config);
|
|
18038
|
+
const { tools: siemTools } = createSIEMTools(auditLog);
|
|
16885
18039
|
const { tools: contextGateTools, enforcer: contextGateEnforcer } = createContextGateTools(storage, masterKey, auditLog);
|
|
16886
18040
|
const hardeningTools = createL2HardeningTools(config.storage_path, auditLog);
|
|
16887
18041
|
const profileStore = new SovereigntyProfileStore(storage, masterKey);
|
|
@@ -16964,7 +18118,7 @@ async function createSanctuaryServer(options) {
|
|
|
16964
18118
|
const dashboardTools = [];
|
|
16965
18119
|
if (dashboard) {
|
|
16966
18120
|
dashboardTools.push({
|
|
16967
|
-
name: "
|
|
18121
|
+
name: "dashboard_open",
|
|
16968
18122
|
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.",
|
|
16969
18123
|
inputSchema: {
|
|
16970
18124
|
type: "object",
|
|
@@ -16998,6 +18152,7 @@ async function createSanctuaryServer(options) {
|
|
|
16998
18152
|
...federationTools,
|
|
16999
18153
|
...bridgeTools,
|
|
17000
18154
|
...auditTools,
|
|
18155
|
+
...siemTools,
|
|
17001
18156
|
...contextGateTools,
|
|
17002
18157
|
...hardeningTools,
|
|
17003
18158
|
...profileTools,
|
|
@@ -17044,7 +18199,12 @@ async function createSanctuaryServer(options) {
|
|
|
17044
18199
|
}
|
|
17045
18200
|
return args;
|
|
17046
18201
|
},
|
|
17047
|
-
governor
|
|
18202
|
+
governor,
|
|
18203
|
+
onProxyCall: (data) => {
|
|
18204
|
+
if (dashboard) {
|
|
18205
|
+
dashboard.broadcastProxyCall(data);
|
|
18206
|
+
}
|
|
18207
|
+
}
|
|
17048
18208
|
}
|
|
17049
18209
|
);
|
|
17050
18210
|
clientManager.configure(enabledServers).catch((err) => {
|
|
@@ -17062,6 +18222,7 @@ async function createSanctuaryServer(options) {
|
|
|
17062
18222
|
auditLog,
|
|
17063
18223
|
clientManager
|
|
17064
18224
|
});
|
|
18225
|
+
dashboard.enableFortressView(enabledServers.length);
|
|
17065
18226
|
}
|
|
17066
18227
|
}
|
|
17067
18228
|
}
|