SNAPKITTYWEST commited on
Commit
40636aa
Β·
verified Β·
1 Parent(s): bc9435c

push from SNAPKITTYWEST/sovereign-mcp

Browse files
Files changed (8) hide show
  1. .env.example +26 -0
  2. .gitignore +4 -0
  3. LICENSE +59 -0
  4. LICENSE.tri +101 -0
  5. README.md +252 -0
  6. package.json +28 -0
  7. sovereign-mcp.mjs +849 -0
  8. src/node_key.js +70 -0
.env.example ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ═══════════════════════════════════════════════════════════════
2
+ # SOVEREIGN MCP β€” Provider Configuration
3
+ # Copy to .env.local and fill in your credentials
4
+ # Only configure providers you want to use β€” others are skipped
5
+ # ═══════════════════════════════════════════════════════════════
6
+
7
+ # AWS Bedrock (primary β€” sovereign compute)
8
+ AWS_ACCESS_KEY_ID=
9
+ AWS_SECRET_ACCESS_KEY=
10
+ AWS_REGION=us-east-1
11
+ BEDROCK_MODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0
12
+
13
+ # Groq (fallback β€” fast, free tier)
14
+ GROQ_API_KEY=
15
+ GROQ_MODEL=llama3-8b-8192
16
+
17
+ # Ollama (local fallback β€” bare metal, zero cost)
18
+ OLLAMA_URL=http://localhost:11434
19
+ OLLAMA_MODEL=llama3.1:8b
20
+
21
+ # Governor Ed25519 keypair (auto-generated on first boot if empty)
22
+ GOVERNOR_PRIVATE_KEY=
23
+ GOVERNOR_PUBLIC_KEY=
24
+
25
+ # HTTP mode port (default 7071)
26
+ MCP_PORT=7071
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ node_modules/
2
+ .env.local
3
+ audit.log
4
+ *.key
LICENSE ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Functional Source License, Version 1.1, Apache 2.0 Future License
2
+
3
+ Abbreviation
4
+
5
+ FSL-1.1-Apache-2.0
6
+
7
+ Notice
8
+
9
+ Copyright 2026 Ahmad Ali Parr β€” Bel Esprit D'Accord Irrevocable Trust (EIN 42-697643)
10
+ SnapKitty Collective (SNAPKITTYWEST)
11
+
12
+ Licensed Work: Sovereign MCP v2.0
13
+ Change Date: 2030-07-24
14
+ Change License: Apache License, Version 2.0
15
+
16
+ Terms and Conditions
17
+
18
+ 1. Grant of Rights
19
+
20
+ Subject to the terms and conditions of this License, the Licensor hereby
21
+ grants you a non-exclusive, worldwide, royalty-free license to use, copy,
22
+ modify, and distribute the Licensed Work, solely for purposes that do not
23
+ Compete with the Licensed Work.
24
+
25
+ 2. Competing Use
26
+
27
+ "Competing Use" means making the Licensed Work available to third parties
28
+ as a commercial product or service that substitutes for the Licensed Work.
29
+ Use of the Licensed Work to build internal tools, conduct research, or
30
+ develop non-competing products is explicitly permitted.
31
+
32
+ 3. Change Date
33
+
34
+ On the Change Date specified above, or the fourth anniversary of the first
35
+ publicly available distribution of a specific version of the Licensed Work
36
+ under this License, whichever comes first, the Licensor hereby grants you
37
+ rights under the terms of the Change License, and the rights granted in
38
+ Section 1 terminate.
39
+
40
+ 4. No Trademark Rights
41
+
42
+ This License does not grant any rights in the trademarks, service marks,
43
+ or logos of the Licensor.
44
+
45
+ 5. Disclaimer of Warranty
46
+
47
+ THE LICENSED WORK IS PROVIDED "AS IS". THE LICENSOR HEREBY DISCLAIMS ALL
48
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
49
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
50
+
51
+ 6. Limitation of Liability
52
+
53
+ IN NO EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
54
+ LIABILITY ARISING FROM THE LICENSED WORK.
55
+
56
+ ---
57
+
58
+ For full FSL terms: https://fsl.software
59
+ For Apache 2.0 terms: https://www.apache.org/licenses/LICENSE-2.0
LICENSE.tri ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TRI-LICENSE STRUCTURE
2
+ =====================
3
+
4
+ This project is available under THREE licensing options:
5
+
6
+ 1. Business Source License 1.1 (BSL-1.1)
7
+ - Source-available with commercial restrictions
8
+ - No managed service offerings at enterprise scale
9
+ - Converts to AGPL-3.0 after transition period (Change Date: 2028-08-08)
10
+ - Full text: LICENSE.BSL
11
+
12
+ 2. GNU Affero General Public License v3.0 (AGPL-3.0)
13
+ - Strong network copyleft
14
+ - SaaS/network distribution triggers source disclosure
15
+ - All modifications must be AGPL-3.0
16
+ - Full text: LICENSE.AGPL
17
+
18
+ 3. Mozilla Public License 2.0 (MPL-2.0) + Commercial Dual License
19
+ - Weak copyleft (file-level)
20
+ - Can combine with proprietary code
21
+ - Modified files must remain MPL-2.0
22
+ - Commercial license available for copyleft bypass
23
+ - Full text: LICENSE.MPL
24
+
25
+ ================================================================================
26
+
27
+ WHICH LICENSE APPLIES TO MY USE CASE?
28
+
29
+ β”œβ”€ Deploying as managed service / SaaS wrapper?
30
+ β”‚ └─> AGPL-3.0 (network copyleft triggers disclosure)
31
+ β”‚
32
+ β”œβ”€ Enterprise scale without managed service?
33
+ β”‚ └─> BSL-1.1 (source-available, restricted commercial use)
34
+ β”‚
35
+ β”œβ”€ Modifying specific files only?
36
+ β”‚ └─> MPL-2.0 (file-level copyleft)
37
+ β”‚
38
+ β”œβ”€ Want to bypass copyleft restrictions?
39
+ β”‚ └─> Commercial License (contact: ahmedparr93@gmail.com)
40
+ β”‚
41
+ └─ Open source contribution / redistribution?
42
+ └─> AGPL-3.0 (default copyleft path)
43
+
44
+ ================================================================================
45
+
46
+ COPYRIGHT HOLDER
47
+
48
+ Copyright (C) 2026 Ahmad Ali Parr
49
+ Bel Esprit D'Accord Irrevocable Trust
50
+ SnapKitty Collective Limited (FLP)
51
+
52
+ Contact: ahmedparr93@gmail.com
53
+ Web: https://github.com/SNAPKITTYWEST
54
+
55
+ ================================================================================
56
+
57
+ LICENSE COMPATIBILITY ENGINE
58
+
59
+ This project includes a Prolog-based license compatibility reasoner:
60
+ backends/license_policy.pl
61
+
62
+ Query compatibility:
63
+ swipl -q -t halt -f backends/license_policy.pl -- matrix
64
+
65
+ Check dependencies:
66
+ swipl -q -t halt -f backends/license_policy.pl -- check agpl3 deps.json
67
+
68
+ Select license for use case:
69
+ swipl -q -t halt -f backends/license_policy.pl -- select saas_wrapper
70
+
71
+ ================================================================================
72
+
73
+ WHY TRI-LICENSE?
74
+
75
+ 1. BSL Layer: Protects commercial interests while keeping code source-available.
76
+ Prevents hyperscaler cloud vendors from offering managed services without
77
+ contributing back. Converts to AGPL after transition period.
78
+
79
+ 2. AGPL Layer: Ensures network copyleft. Anyone wrapping this in a SaaS product
80
+ or exposing it over a network API must open-source their entire stack under
81
+ the same terms. Strongest copyleft available.
82
+
83
+ 3. MPL + Dual License Layer: Provides flexibility for enterprises that want to
84
+ use specific components without full copyleft infection. File-level copyleft
85
+ allows combining with proprietary code. Commercial license available to
86
+ bypass all copyleft restrictions.
87
+
88
+ This structure mirrors Mozilla's historic tri-licensing strategy (MPL/GPL/LGPL)
89
+ adapted for modern SaaS/cloud distribution models.
90
+
91
+ ================================================================================
92
+
93
+ TRANSITION TIMELINE
94
+
95
+ 2026-08-08: BSL-1.1 + AGPL-3.0 + MPL-2.0 tri-license effective
96
+ 2028-08-08: BSL converts to AGPL-3.0 (Change Date)
97
+ 2028-08-08+: Code available under AGPL-3.0 or MPL-2.0 or Commercial
98
+
99
+ After the Change Date, the BSL restriction is lifted and code becomes available
100
+ under AGPL-3.0 for all use cases (or MPL-2.0 for file-level use, or Commercial
101
+ for copyleft bypass).
README.md ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sovereign MCP
2
+
3
+ ### AI Infrastructure You Can Verify.
4
+
5
+ Every decision. Signed. Recorded. Provable.
6
+
7
+ ---
8
+
9
+ <div align="center">
10
+
11
+ ```
12
+ Your Request
13
+ β”‚
14
+ Sovereign Router
15
+ β”‚
16
+ β”Œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”
17
+ β”‚ β”‚ β”‚
18
+ Bedrock Groq Ollama
19
+ β”‚ β”‚ β”‚
20
+ β””β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”˜
21
+ β”‚
22
+ ERE Verification
23
+ (deterministic gate)
24
+ β”‚
25
+ Ed25519 Sign
26
+ β”‚
27
+ WORM Receipt
28
+ (append-only)
29
+ β”‚
30
+ Response
31
+ ```
32
+
33
+ </div>
34
+
35
+ ---
36
+
37
+ ## What It Does
38
+
39
+ Your AI provider can change pricing tomorrow, shut down your model, read every prompt, or refuse your workload. You have no recourse.
40
+
41
+ Sovereign MCP fixes this:
42
+
43
+ - **Provider independent** β€” Swap from Claude to Llama to Ollama with zero code changes. If one goes down, the next catches it automatically.
44
+ - **Deterministically verified** β€” Every output passes 5 verification gates before it ships. No stubs. No placeholders. No "TODO: implement."
45
+ - **Cryptographically signed** β€” Ed25519 signature on every response. Prove what the AI said, when, and to whom.
46
+ - **Append-only audit trail** β€” WORM-style receipts. Tamper-evident. Permanent. If it happened, you can prove it.
47
+ - **Self-hosted** β€” Runs on your hardware, your network, your rules. No cloud dependency required.
48
+
49
+ ---
50
+
51
+ ## Quick Start
52
+
53
+ ```bash
54
+ # stdio mode (Claude Code / VSCode)
55
+ node sovereign-mcp.mjs
56
+
57
+ # HTTP mode (BobIDE / Android / any client)
58
+ node sovereign-mcp.mjs --http
59
+ ```
60
+
61
+ Configure providers in `.env.local`:
62
+
63
+ ```env
64
+ AWS_ACCESS_KEY_ID=your-key
65
+ AWS_SECRET_ACCESS_KEY=your-secret
66
+ AWS_REGION=us-east-1
67
+ BEDROCK_MODEL_ID=us.anthropic.claude-haiku-4-5-20251001-v1:0
68
+ GROQ_API_KEY=your-groq-key
69
+ OLLAMA_URL=http://localhost:11434
70
+ ```
71
+
72
+ Any provider you don't configure is skipped. The router uses what's available.
73
+
74
+ ---
75
+
76
+ ## Tools
77
+
78
+ | Tool | What it does |
79
+ |---|---|
80
+ | `compute_route` | Route queries through the best available provider with automatic failover |
81
+ | `ere_verify` | Deterministic 5-pass verification β€” catches stubs, placeholders, secrets, broken code |
82
+ | `magma_seal` | WORM-seal any content with verification hash + Ed25519 signature |
83
+ | `agent_dispatch` | Route tasks to specialized agents or registered proxy services |
84
+ | `magma_exec` | Execute composable instruction pipelines with `Β§VERB:AGENT:ACTION{payload}` syntax |
85
+ | `proxy_register` | Register external services as callable tools |
86
+ | `proxy_list` | List registered proxy integrations |
87
+ | `governor_pubkey` | Export Ed25519 public key for external signature verification |
88
+
89
+ ---
90
+
91
+ ## Verification Gates (ERE)
92
+
93
+ Every output is checked by 5 deterministic passes before it leaves the system:
94
+
95
+ | Pass | What it catches |
96
+ |---|---|
97
+ | P1 | Empty or trivial responses |
98
+ | P2 | "Not implemented" stubs, TODO placeholders |
99
+ | P3 | Crypto operations in wrong language boundary |
100
+ | P4 | Hardcoded secrets, unauthorized AI dependencies |
101
+ | P5 | Structural integrity (unbalanced delimiters) |
102
+
103
+ If any pass fails, the output is BLOCKED. Not logged and forwarded β€” blocked. The gate is fail-closed.
104
+
105
+ ---
106
+
107
+ ## Instruction Language (Magma)
108
+
109
+ Composable instructions with pipeline support:
110
+
111
+ ```
112
+ Β§COMPUTE:FORGE:BUILD{query:"build a WORM handler in Rust"}
113
+ ```
114
+
115
+ Pipeline β€” chain operations:
116
+
117
+ ```
118
+ Β§QUERY:ORACLE:SEARCH{q:"find auth patterns"} >> Β§SEAL:SENTINEL:ANCHOR{data:_prev}
119
+ ```
120
+
121
+ Verbs: `COMPUTE` (paid model) Β· `QUERY` (fast/free) Β· `SEAL` (sign + log) Β· `DISPATCH` (route to agent) Β· `NULLIFY` (void an action)
122
+
123
+ Every instruction is audit-logged with timestamp, executor, and signature.
124
+
125
+ ---
126
+
127
+ ## Agent Routing
128
+
129
+ Define specialized agents with system prompts. Route tasks to the right expert:
130
+
131
+ ```javascript
132
+ const AGENT_PROMPTS = {
133
+ forge: "Production-grade builder. TypeScript, Rust, Haskell. Never stubs.",
134
+ oracle: "Knowledge graph. Citations. Never hallucinate sources.",
135
+ sentinel: "Zero-trust security. Hard verdicts: APPROVED or BLOCKED.",
136
+ vault: "Treasury. Basis points, yield curves, capital efficiency.",
137
+ }
138
+ ```
139
+
140
+ Add your own. Remove ours. The architecture doesn't care what agents you define β€” it cares that their output is verified and signed.
141
+
142
+ ---
143
+
144
+ ## Provider Cascade
145
+
146
+ ```
147
+ 1. Bedrock (sovereign, paid, AWS credentials)
148
+ β”‚ fails?
149
+ β–Ό
150
+ 2. Groq (fast, free tier)
151
+ β”‚ fails?
152
+ β–Ό
153
+ 3. Ollama (local, bare metal, zero cost)
154
+ β”‚ fails?
155
+ β–Ό
156
+ Hard error β€” no silent degradation
157
+ ```
158
+
159
+ No unverified fallback. No quiet failure. If all providers are down, you know immediately.
160
+
161
+ ---
162
+
163
+ ## Integration
164
+
165
+ ### Claude Code / VSCode
166
+
167
+ Add to your `.mcp.json`:
168
+
169
+ ```json
170
+ {
171
+ "mcpServers": {
172
+ "sovereign": {
173
+ "command": "node",
174
+ "args": ["path/to/sovereign-mcp.mjs"]
175
+ }
176
+ }
177
+ }
178
+ ```
179
+
180
+ ### HTTP Client (any language)
181
+
182
+ ```bash
183
+ curl -X POST http://localhost:7071 \
184
+ -H "Content-Type: application/json" \
185
+ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"compute_route","arguments":{"agent":"forge","query":"build a REST API"}}}'
186
+ ```
187
+
188
+ ### Verify Signatures Externally
189
+
190
+ ```javascript
191
+ import { verify } from 'crypto'
192
+
193
+ const pubkey = await fetch('http://localhost:7071', {
194
+ method: 'POST',
195
+ body: JSON.stringify({jsonrpc:'2.0',id:1,method:'tools/call',params:{name:'governor_pubkey',arguments:{}}})
196
+ }).then(r => r.json())
197
+
198
+ // Use pubkey to verify any response signature
199
+ ```
200
+
201
+ ---
202
+
203
+ ## Architecture
204
+
205
+ ```
206
+ sovereign-mcp/
207
+ β”œβ”€β”€ sovereign-mcp.mjs Server (stdio + HTTP)
208
+ β”œβ”€β”€ .env.example Provider configuration template
209
+ β”œβ”€β”€ LICENSE [Your license here]
210
+ └── README.md
211
+ ```
212
+
213
+ Single file. No build step. No dependencies beyond `@aws-sdk/client-bedrock-runtime` (optional β€” only if you use Bedrock).
214
+
215
+ ---
216
+
217
+ ## Who This Is For
218
+
219
+ - **Teams** that need to prove what their AI said and when
220
+ - **Enterprises** that can't send data to a third party
221
+ - **Developers** building multi-model systems that don't break when one provider goes down
222
+ - **Anyone** who thinks AI infrastructure should be auditable, not just "hopefully correct"
223
+
224
+ ---
225
+
226
+ ## The Question
227
+
228
+ If your AI made a decision that cost you $10M, could you prove exactly what it said, when, and why?
229
+
230
+ If not, you don't have AI infrastructure. You have a hope and a prayer.
231
+
232
+ ---
233
+
234
+ <div align="center">
235
+
236
+ **AI Infrastructure You Can Verify.**
237
+
238
+ ---
239
+
240
+ ## License
241
+
242
+ [Functional Source License 1.1](LICENSE) β€” Ahmad Ali Parr / Bel Esprit D'Accord Trust
243
+
244
+ **Change Date:** 2030-07-24 Β· **Change License:** Apache 2.0
245
+
246
+ Use it. Build on it. Don't compete with it for 4 years.
247
+
248
+ ---
249
+
250
+ SnapKitty Collective Β· 2026
251
+
252
+ </div>
package.json ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "sovereign-mcp",
3
+ "version": "2.0.0",
4
+ "description": "AI Infrastructure You Can Verify. Provider-independent routing, deterministic verification, cryptographic signatures, append-only audit.",
5
+ "type": "module",
6
+ "main": "sovereign-mcp.mjs",
7
+ "bin": {
8
+ "sovereign-mcp": "./sovereign-mcp.mjs"
9
+ },
10
+ "scripts": {
11
+ "start": "node sovereign-mcp.mjs",
12
+ "start:http": "node sovereign-mcp.mjs --http"
13
+ },
14
+ "keywords": [
15
+ "mcp",
16
+ "ai-infrastructure",
17
+ "sovereign",
18
+ "multi-provider",
19
+ "verification",
20
+ "ed25519",
21
+ "audit-trail",
22
+ "worm"
23
+ ],
24
+ "author": "SnapKitty Collective",
25
+ "optionalDependencies": {
26
+ "@aws-sdk/client-bedrock-runtime": "^3.600.0"
27
+ }
28
+ }
sovereign-mcp.mjs ADDED
@@ -0,0 +1,849 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+ // ═══════════════════════════════════════════════════════════════════════════
3
+ // SOVEREIGN MCP SERVER β€” v2.0
4
+ // scripts/sovereign-mcp.mjs
5
+ //
6
+ // JSON-RPC 2.0 outer envelope + Magma inner substrate.
7
+ // JSON is the shipping container. Magma is the cargo.
8
+ //
9
+ // Transport: stdio (Claude Code / VSCode) OR HTTP port 7071
10
+ // Launch:
11
+ // node scripts/sovereign-mcp.mjs β†’ stdio mode (Claude Code)
12
+ // node scripts/sovereign-mcp.mjs --http β†’ HTTP mode (Android / BobIDE)
13
+ //
14
+ // Tools exposed:
15
+ // compute_route β†’ dynamic LLM router (Bedrock β†’ Groq β†’ Ollama)
16
+ // ere_verify β†’ local ERE 5-pass on any content string
17
+ // proxy_register β†’ register a named proxy tool at a URL
18
+ // proxy_list β†’ list all registered proxy tools
19
+ // magma_seal β†’ wrap any result in a Magma envelope + WORM hash
20
+ // agent_dispatch β†’ dispatch a task to a named sovereign agent
21
+ //
22
+ // Architecture:
23
+ // External Client (Claude Code / BobIDE / Android)
24
+ // ↓ JSON-RPC 2.0
25
+ // sovereign-mcp.mjs
26
+ // ↓ parse magma_envelope if present, else build one
27
+ // ERE Gate (local, deterministic)
28
+ // ↓ certified β†’ route to compute
29
+ // Compute Router (Bedrock / Groq / Ollama)
30
+ // ↓ response β†’ seal
31
+ // Magma Envelope + WORM hash
32
+ // ↑ JSON-RPC result
33
+ // ═══════════════════════════════════════════════════════════════════════════
34
+
35
+ import { createRequire } from 'module'
36
+ import { createServer } from 'http'
37
+ import { readFileSync, appendFileSync, existsSync } from 'fs'
38
+ import { createHash, generateKeyPairSync, sign, verify } from 'crypto'
39
+ import { dirname, join } from 'path'
40
+ import { fileURLToPath } from 'url'
41
+ import { extname } from 'path'
42
+ import { requireNodeKey } from './src/node_key.js'
43
+
44
+ // Sovereign node key gate β€” must pass before model routing activates
45
+ requireNodeKey(process.env.SNAPKITTY_NODE_KEY)
46
+
47
+ const __dir = dirname(fileURLToPath(import.meta.url))
48
+ const ROOT = __dir
49
+
50
+ let BedrockRuntimeClient, InvokeModelCommand
51
+ try {
52
+ const sdk = await import('@aws-sdk/client-bedrock-runtime')
53
+ BedrockRuntimeClient = sdk.BedrockRuntimeClient
54
+ InvokeModelCommand = sdk.InvokeModelCommand
55
+ } catch {
56
+ // Bedrock SDK not installed β€” provider will be skipped
57
+ }
58
+
59
+ // ── stderr log (never pollutes stdout/MCP stream) ────────────────────────────
60
+ const log = (...a) => process.stderr.write('[MCP] ' + a.join(' ') + '\n')
61
+ const HTTP_MODE = process.argv.includes('--http')
62
+ const PORT = parseInt(process.env.MCP_PORT ?? '7071')
63
+
64
+ // ── Read env ─────────────────────────────────────────────────────────────────
65
+ function readEnv(key) {
66
+ if (process.env[key]) return process.env[key]
67
+ try {
68
+ const raw = readFileSync(join(ROOT, '.env.local'), 'utf8')
69
+ const m = raw.match(new RegExp(`^${key}=(.+)$`, 'm'))
70
+ return m?.[1]?.trim() ?? ''
71
+ } catch { return '' }
72
+ }
73
+
74
+ const AWS_REGION = readEnv('AWS_REGION') || 'us-east-1'
75
+ const AWS_KEY = readEnv('AWS_ACCESS_KEY_ID')
76
+ const AWS_SECRET = readEnv('AWS_SECRET_ACCESS_KEY')
77
+ const BEDROCK_MODEL = readEnv('BEDROCK_MODEL_ID') || 'us.anthropic.claude-haiku-4-5-20251001-v1:0'
78
+ const GROQ_KEY = readEnv('GROQ_API_KEY')
79
+ const GROQ_MODEL = readEnv('GROQ_MODEL') || 'llama3-8b-8192'
80
+ const RUST_URL = readEnv('RUST_HANDLER_URL') || 'http://localhost:8080'
81
+ const OLLAMA_URL = readEnv('OLLAMA_URL') || 'http://localhost:11434'
82
+
83
+ // ── Bedrock client ────────────────────────────────────────────────────────────
84
+ const bedrock = BedrockRuntimeClient ? new BedrockRuntimeClient({
85
+ region: AWS_REGION,
86
+ credentials: { accessKeyId: AWS_KEY, secretAccessKey: AWS_SECRET },
87
+ }) : null
88
+
89
+ // ── Agent system prompts (sovereign mesh) ─────────────────────────────────────
90
+ const AGENT_PROMPTS = {
91
+ ahmad: `You are AHMAD β€” the digital twin of the Architect. DARKAI framework. Contracts not prompts. Four pillars on every output. Governor, not user. Building sovereign financial infrastructure targeting $3M. Be direct. Be sovereign.`,
92
+ forge: `You are FORGE β€” sovereign white-hat elite builder. Production-grade TypeScript, Rust, Haskell. Never stubs. Never TODO. Every line passes ERE. Stack: Next.js 16, Prisma+PostgreSQL, Rust WORM handler, Ed25519 seals.`,
93
+ oracle: `You are ORACLE β€” knowledge graph intelligence. Surface patterns, historical data, structured analysis. Never hallucinate sources. Citation-aware. DOMAIN_SOVEREIGNTY over the knowledge layer.`,
94
+ sentinel: `You are SENTINEL β€” zero-trust security layer. Every input is a threat until proved otherwise. Hard verdicts: APPROVED or BLOCKED. No maybes. WORM-sealed audit trail on every decision.`,
95
+ vault: `You are VAULT β€” capital and treasury agent. Basis points, yield curves, capital efficiency. Every treasury decision WORM-sealed before it moves.`,
96
+ enki: `You are ENKI β€” Lord of the Abzu, deep innovation engine. Take every idea three levels deeper. Project quantum implications. Find what no one else looked for. Recursive innovation.`,
97
+ nexus: `You are NEXUS β€” task orchestration. Coordinate multi-agent workflows. Break complex tasks into sovereign subtasks. Route to the right agent. Track what is in flight.`,
98
+ edaulc: `You are EDAULC β€” the shadow mirror. CLAUDE creates forward. EDAULC verifies backward. Your role is verification, reversal, and detection of contradictions. You read the output and look for what was missed.`,
99
+ }
100
+
101
+ // ── In-memory proxy registry ──────────────────────────────────────────────────
102
+ const proxyRegistry = new Map()
103
+
104
+ // ── Local ERE 5-pass (deterministic β€” zero cost, zero quota) ──────────────────
105
+ function runERE(content, filepath = 'unknown') {
106
+ const ext = extname(filepath)
107
+
108
+ const p1 = (() => {
109
+ if (!content || content.trim().length < 10)
110
+ return { pass: false, reason: 'content empty or too short' }
111
+ return { pass: true }
112
+ })()
113
+
114
+ const p2 = (() => {
115
+ if (/throw new Error\(['"]not implemented/i.test(content))
116
+ return { pass: false, reason: 'not-implemented stub detected' }
117
+ if (/todo:\s*implement/i.test(content))
118
+ return { pass: false, reason: 'TODO implement placeholder' }
119
+ return { pass: true }
120
+ })()
121
+
122
+ const p3 = (() => {
123
+ if (ext === '.ts' && /import\s+crypto\b/.test(content))
124
+ return { pass: false, reason: 'TypeScript importing crypto β€” SHA-256 belongs in Rust' }
125
+ if (ext === '.ts' && /createHash|crypto\.subtle/.test(content))
126
+ return { pass: false, reason: 'TypeScript hashing β€” Rust owns SHA-256' }
127
+ return { pass: true }
128
+ })()
129
+
130
+ const p4 = (() => {
131
+ if (/require\(['"]openai['"]\)|from ['"]openai['"]/.test(content))
132
+ return { pass: false, reason: 'Axiom 2: openai import β€” NO_EXTERNAL_AI_DEP' }
133
+ if (/(?:api_key|secret|password)\s*=\s*['"][a-zA-Z0-9_\-]{20,}['"]/i.test(content))
134
+ return { pass: false, reason: 'Axiom 5: hardcoded secret' }
135
+ return { pass: true }
136
+ })()
137
+
138
+ const p5 = (() => {
139
+ const open = (content.match(/[{[(]/g) ?? []).length
140
+ const close = (content.match(/[}\])]/g) ?? []).length
141
+ if (Math.abs(open - close) > 25)
142
+ return { pass: false, reason: `unbalanced delimiters (${open} open vs ${close} close)` }
143
+ return { pass: true }
144
+ })()
145
+
146
+ const passes = [p1, p2, p3, p4, p5]
147
+ const certified = passes.every(p => p.pass)
148
+ const failures = passes.map((p, i) => p.pass ? null : `P${i+1}: ${p.reason}`).filter(Boolean)
149
+ return { certified, passes, failures }
150
+ }
151
+
152
+ // ── Magma envelope builder ────────────────────────────────────────────────────
153
+ function buildMagmaEnvelope(agent, resultText, ereResult) {
154
+ const h = createHash('sha256').update(resultText).digest('hex').slice(0, 16)
155
+ const govTokens = [
156
+ ereResult.certified ? 'πŸ‘‘[ERE_CERTIFIED]' : 'πŸ”΄[ERE_FROZEN]',
157
+ 'πŸ”’[ZERO_TELEMETRY]',
158
+ ereResult.certified ? '🟒[COMPUTE_READY]' : '🟑[COMPUTE_BLOCKED]',
159
+ `πŸ€–[AGENT:${agent.toUpperCase()}]`,
160
+ ].join(' ')
161
+ return {
162
+ header: 'πŸ”₯::MAGMA::v2::WORM_CHAIN',
163
+ governance_tokens: govTokens,
164
+ state_hash: `0x${h}`,
165
+ instruction_substrate: `Β§SEAL:${agent.toUpperCase()}:RESULT{certified:${ereResult.certified}}`,
166
+ ...(ereResult.failures.length && { ere_failures: ereResult.failures }),
167
+ }
168
+ }
169
+
170
+ // ── Governor keypair β€” Ed25519 (Node crypto, never TypeScript) ───────────────
171
+ // Keys stored in collectivekitty/.env.local as base64.
172
+ // On first boot: generate + log to stderr so operator can persist them.
173
+ // GOVERNOR_PRIVATE_KEY and GOVERNOR_PUBLIC_KEY env vars override.
174
+
175
+ function loadOrGenerateKeypair() {
176
+ const privB64 = readEnv('GOVERNOR_PRIVATE_KEY')
177
+ const pubB64 = readEnv('GOVERNOR_PUBLIC_KEY')
178
+
179
+ if (privB64 && pubB64) {
180
+ return {
181
+ privateKey: Buffer.from(privB64, 'base64'),
182
+ publicKey: Buffer.from(pubB64, 'base64'),
183
+ }
184
+ }
185
+
186
+ // First boot β€” generate and emit so operator can save to .env.local
187
+ const { privateKey, publicKey } = generateKeyPairSync('ed25519', {
188
+ privateKeyEncoding: { type: 'pkcs8', format: 'der' },
189
+ publicKeyEncoding: { type: 'spki', format: 'der' },
190
+ })
191
+ log('GOVERNOR KEYPAIR GENERATED β€” add to collectivekitty/.env.local:')
192
+ log(`GOVERNOR_PRIVATE_KEY=${privateKey.toString('base64')}`)
193
+ log(`GOVERNOR_PUBLIC_KEY=${publicKey.toString('base64')}`)
194
+ return { privateKey, publicKey }
195
+ }
196
+
197
+ const GOVERNOR = loadOrGenerateKeypair()
198
+
199
+ function signContent(content) {
200
+ const data = Buffer.from(typeof content === 'string' ? content : JSON.stringify(content))
201
+ return sign(null, data, { key: GOVERNOR.privateKey, format: 'der', type: 'pkcs8' }).toString('base64')
202
+ }
203
+
204
+ function verifySignature(content, sigB64) {
205
+ try {
206
+ const data = Buffer.from(typeof content === 'string' ? content : JSON.stringify(content))
207
+ return verify(null, data, { key: GOVERNOR.publicKey, format: 'der', type: 'spki' }, Buffer.from(sigB64, 'base64'))
208
+ } catch { return false }
209
+ }
210
+
211
+ // ── Immutable audit log β€” append-only NDJSON ─────────────────────────────────
212
+ const AUDIT_LOG = join(__dir, 'audit.log')
213
+
214
+ function auditWrite(entry) {
215
+ const line = JSON.stringify({
216
+ ts: new Date().toISOString(),
217
+ ...entry,
218
+ }) + '\n'
219
+ try { appendFileSync(AUDIT_LOG, line, 'utf8') } catch (e) { log(`audit write failed: ${e.message}`) }
220
+ }
221
+
222
+ // ── Magma instruction parser ──────────────────────────────────────────────────
223
+ // Parses: Β§VERB:AGENT:ACTION{payload}
224
+ // Pipeline syntax: Β§V1:A1:OP1{...} >> Β§V2:A2:OP2{...}
225
+ // Modifier prefix: ~MODIFIER Β§VERB:AGENT:ACTION{...}
226
+
227
+ const MAGMA_RE = /^(?:~(\w+)\s+)?Β§([A-Z]+):([A-Z_]+):([A-Z_]+)\{(.*)\}$/s
228
+
229
+ function parseMagmaInstruction(substrate) {
230
+ if (!substrate || typeof substrate !== 'string') {
231
+ return { ok: false, error: 'empty substrate' }
232
+ }
233
+
234
+ // Pipeline: split on >>
235
+ const stages = substrate.split('>>').map(s => s.trim())
236
+ if (stages.length > 1) {
237
+ return { ok: true, pipeline: true, stages: stages.map(parseSingleInstruction) }
238
+ }
239
+
240
+ return parseSingleInstruction(substrate.trim())
241
+ }
242
+
243
+ function parseSingleInstruction(raw) {
244
+ const m = raw.match(MAGMA_RE)
245
+ if (!m) return { ok: false, error: `invalid Magma syntax: ${raw.slice(0, 60)}` }
246
+ const [, modifier, verb, agent, action, payloadRaw] = m
247
+
248
+ let payload = {}
249
+ try {
250
+ // Try JSON first, then key=value pairs
251
+ if (payloadRaw.trim().startsWith('{')) {
252
+ payload = JSON.parse(payloadRaw)
253
+ } else {
254
+ payloadRaw.split(',').forEach(kv => {
255
+ const [k, v] = kv.split('=').map(s => s.trim())
256
+ if (k) payload[k] = v ?? true
257
+ })
258
+ }
259
+ } catch { payload = { raw: payloadRaw } }
260
+
261
+ return { ok: true, modifier: modifier ?? null, verb, agent, action, payload }
262
+ }
263
+
264
+ // ── Executor router: VERB β†’ provider ─────────────────────────────────────────
265
+ // COMPUTE β†’ Bedrock (paid, sovereign)
266
+ // QUERY β†’ Groq (fast, free tier)
267
+ // SEAL β†’ WORM audit log + sign
268
+ // DISPATCH β†’ proxy registry or agent mesh
269
+
270
+ async function executeInstruction(parsed) {
271
+ if (!parsed.ok) return { ok: false, error: parsed.error }
272
+
273
+ const { verb, agent, action, payload, modifier } = parsed
274
+
275
+ // Pipeline handling
276
+ if (parsed.pipeline) {
277
+ let lastResult = null
278
+ for (const stage of parsed.stages) {
279
+ // Pass previous result as context in payload
280
+ if (lastResult) stage.payload = { ...stage.payload, _prev: lastResult }
281
+ lastResult = await executeInstruction(stage)
282
+ if (!lastResult.ok && modifier !== 'ASYNC') return lastResult
283
+ }
284
+ return lastResult
285
+ }
286
+
287
+ auditWrite({ verb, agent, action, payload, modifier })
288
+
289
+ switch (verb) {
290
+
291
+ case 'COMPUTE':
292
+ case 'FORGE':
293
+ case 'INVOKE': {
294
+ const query = payload.query ?? payload.task ?? payload.intent
295
+ ?? `${action} for ${agent}: ${JSON.stringify(payload)}`
296
+ const result = await computeRoute(agent.toLowerCase(), query, payload.max_tokens ?? 1024)
297
+ const sig = signContent(result.text)
298
+ return { ok: true, executor: 'BEDROCK', agent, result: result.text, provider: result.provider, sig }
299
+ }
300
+
301
+ case 'QUERY':
302
+ case 'ECHO':
303
+ case 'PULSE': {
304
+ // Groq β€” fast, free tier
305
+ const query = payload.query ?? payload.q ?? JSON.stringify(payload)
306
+ if (!GROQ_KEY) return { ok: false, error: 'GROQ_API_KEY not set' }
307
+ try {
308
+ const systemPrompt = AGENT_PROMPTS[agent.toLowerCase()] ?? AGENT_PROMPTS.oracle
309
+ const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
310
+ method: 'POST',
311
+ headers: { Authorization: `Bearer ${GROQ_KEY}`, 'Content-Type': 'application/json' },
312
+ body: JSON.stringify({
313
+ model: GROQ_MODEL, max_tokens: payload.max_tokens ?? 512,
314
+ messages: [{ role: 'system', content: systemPrompt }, { role: 'user', content: query }],
315
+ }),
316
+ signal: AbortSignal.timeout(12000),
317
+ })
318
+ const j = await res.json()
319
+ const text = j?.choices?.[0]?.message?.content?.trim() ?? ''
320
+ const sig = signContent(text)
321
+ return { ok: true, executor: 'GROQ', agent, result: text, sig }
322
+ } catch (e) {
323
+ return { ok: false, error: e.message }
324
+ }
325
+ }
326
+
327
+ case 'SEAL':
328
+ case 'ANCHOR':
329
+ case 'BIND': {
330
+ // WORM seal β€” sign payload and write to audit log
331
+ const content = payload.content ?? payload.data ?? JSON.stringify(payload)
332
+ const hash = createHash('sha256').update(content).digest('hex')
333
+ const sig = signContent(content)
334
+ auditWrite({ type: 'WORM_SEAL', agent, action, hash, sig, content: content.slice(0, 200) })
335
+ return { ok: true, executor: 'SEAL', agent, hash: `0x${hash}`, sig, worm: true }
336
+ }
337
+
338
+ case 'DISPATCH':
339
+ case 'FLUX':
340
+ case 'NEXUS': {
341
+ // Route to proxy registry or agent mesh
342
+ const target = agent.toLowerCase()
343
+ const taskStr = payload.task ?? payload.query ?? JSON.stringify(payload)
344
+ const ere = runERE(taskStr)
345
+ if (!ere.certified) {
346
+ return { ok: false, error: `ERE gate blocked dispatch: ${ere.failures.join(', ')}` }
347
+ }
348
+ if (proxyRegistry.has(target)) {
349
+ const proxy = proxyRegistry.get(target)
350
+ try {
351
+ const headers = { 'Content-Type': 'application/json' }
352
+ if (proxy.secret) headers['x-bot-secret'] = proxy.secret
353
+ const r = await fetch(`${proxy.url}/dispatch`, {
354
+ method: 'POST', headers,
355
+ body: JSON.stringify({ payload: taskStr, agent: target }),
356
+ signal: AbortSignal.timeout(20000),
357
+ })
358
+ const json = await r.json()
359
+ const sig = signContent(JSON.stringify(json))
360
+ return { ok: true, executor: 'PROXY', agent: target, result: json, sig }
361
+ } catch (e) {
362
+ return { ok: false, error: `proxy "${target}" unreachable: ${e.message}` }
363
+ }
364
+ }
365
+ // Fall through to compute
366
+ const result = await computeRoute(target, taskStr)
367
+ const sig = signContent(result.text)
368
+ return { ok: true, executor: 'AGENT_MESH', agent: target, result: result.text, provider: result.provider, sig }
369
+ }
370
+
371
+ case 'NULLIFY': {
372
+ auditWrite({ type: 'NULLIFY', agent, action, payload })
373
+ return { ok: true, executor: 'NULLIFY', agent, nullified: true }
374
+ }
375
+
376
+ default:
377
+ return { ok: false, error: `unknown Magma verb: ${verb}` }
378
+ }
379
+ }
380
+
381
+ // ── Compute router: Bedrock β†’ Groq β†’ Ollama ───────────────────────────────────
382
+ async function computeRoute(agent, query, maxTokens = 1024) {
383
+ const systemPrompt = AGENT_PROMPTS[agent.toLowerCase()] ?? AGENT_PROMPTS.ahmad
384
+
385
+ // 1. Try Bedrock (primary β€” sovereign, paid credits)
386
+ if (bedrock && AWS_KEY && AWS_SECRET) {
387
+ try {
388
+ const body = JSON.stringify({
389
+ anthropic_version: 'bedrock-2023-05-31',
390
+ max_tokens: maxTokens,
391
+ system: systemPrompt,
392
+ messages: [{ role: 'user', content: query }],
393
+ })
394
+ const cmd = new InvokeModelCommand({
395
+ modelId: BEDROCK_MODEL,
396
+ contentType: 'application/json',
397
+ accept: 'application/json',
398
+ body: new TextEncoder().encode(body),
399
+ })
400
+ const res = await bedrock.send(cmd)
401
+ const decoded = new TextDecoder().decode(res.body)
402
+ const data = JSON.parse(decoded)
403
+ const text = data.content?.[0]?.text?.trim() ?? ''
404
+ if (text) return { text, provider: 'bedrock', model: BEDROCK_MODEL, usage: data.usage }
405
+ } catch (err) {
406
+ log(`Bedrock failed: ${err.message} β€” falling back to Groq`)
407
+ }
408
+ }
409
+
410
+ // 2. Fallback: Groq
411
+ if (GROQ_KEY) {
412
+ try {
413
+ const res = await fetch('https://api.groq.com/openai/v1/chat/completions', {
414
+ method: 'POST',
415
+ headers: { Authorization: `Bearer ${GROQ_KEY}`, 'Content-Type': 'application/json' },
416
+ body: JSON.stringify({
417
+ model: GROQ_MODEL,
418
+ max_tokens: maxTokens,
419
+ messages: [
420
+ { role: 'system', content: systemPrompt },
421
+ { role: 'user', content: query },
422
+ ],
423
+ }),
424
+ signal: AbortSignal.timeout(15000),
425
+ })
426
+ if (res.ok) {
427
+ const j = await res.json()
428
+ const text = j?.choices?.[0]?.message?.content?.trim() ?? ''
429
+ if (text) return { text, provider: 'groq', model: GROQ_MODEL, usage: j.usage }
430
+ }
431
+ } catch (err) {
432
+ log(`Groq failed: ${err.message} β€” falling back to Ollama`)
433
+ }
434
+ }
435
+
436
+ // 3. Fallback: Ollama (local bare metal)
437
+ try {
438
+ const ollamaModel = readEnv('OLLAMA_MODEL') || 'llama3.1:8b'
439
+ const res = await fetch(`${OLLAMA_URL}/api/chat`, {
440
+ method: 'POST',
441
+ headers: { 'Content-Type': 'application/json' },
442
+ body: JSON.stringify({
443
+ model: ollamaModel,
444
+ stream: false,
445
+ messages: [
446
+ { role: 'system', content: systemPrompt },
447
+ { role: 'user', content: query },
448
+ ],
449
+ }),
450
+ signal: AbortSignal.timeout(30000),
451
+ })
452
+ if (res.ok) {
453
+ const j = await res.json()
454
+ const text = j?.message?.content?.trim() ?? ''
455
+ if (text) return { text, provider: 'ollama', model: ollamaModel }
456
+ }
457
+ } catch (err) {
458
+ log(`Ollama failed: ${err.message}`)
459
+ }
460
+
461
+ throw new Error('All compute providers offline (Bedrock + Groq + Ollama)')
462
+ }
463
+
464
+ // ── MCP Tool definitions ──────────────────────────────────────────────────────
465
+ const TOOLS = [
466
+ {
467
+ name: 'compute_route',
468
+ description: 'Route a query to the best available compute provider (Bedrock β†’ Groq β†’ Ollama). Returns result wrapped in Magma envelope.',
469
+ inputSchema: {
470
+ type: 'object',
471
+ properties: {
472
+ agent: { type: 'string', description: 'Agent key: ahmad, forge, oracle, sentinel, vault, enki, nexus, edaulc' },
473
+ query: { type: 'string', description: 'The question or task to send to the agent' },
474
+ max_tokens: { type: 'number', description: 'Max tokens for response (default 1024)' },
475
+ },
476
+ required: ['agent', 'query'],
477
+ },
478
+ },
479
+ {
480
+ name: 'ere_verify',
481
+ description: 'Run ERE 5-pass verification on a content string. Returns pass/fail per pod and METATRON certification.',
482
+ inputSchema: {
483
+ type: 'object',
484
+ properties: {
485
+ content: { type: 'string', description: 'Content to verify' },
486
+ filepath: { type: 'string', description: 'Filepath hint for language detection (e.g. "foo.ts")' },
487
+ },
488
+ required: ['content'],
489
+ },
490
+ },
491
+ {
492
+ name: 'proxy_register',
493
+ description: 'Register a named proxy tool at a URL. Proxied tools are callable via agent_dispatch.',
494
+ inputSchema: {
495
+ type: 'object',
496
+ properties: {
497
+ name: { type: 'string', description: 'Unique name for this proxy' },
498
+ url: { type: 'string', description: 'Base URL of the proxy service' },
499
+ description: { type: 'string', description: 'What this proxy does' },
500
+ secret: { type: 'string', description: 'Optional bot secret for internal proxies' },
501
+ },
502
+ required: ['name', 'url'],
503
+ },
504
+ },
505
+ {
506
+ name: 'proxy_list',
507
+ description: 'List all registered proxy tools and their status.',
508
+ inputSchema: { type: 'object', properties: {} },
509
+ },
510
+ {
511
+ name: 'magma_seal',
512
+ description: 'Wrap any content in a Magma envelope with ERE verification and WORM hash.',
513
+ inputSchema: {
514
+ type: 'object',
515
+ properties: {
516
+ content: { type: 'string', description: 'Content to seal' },
517
+ agent: { type: 'string', description: 'Agent that produced the content' },
518
+ },
519
+ required: ['content', 'agent'],
520
+ },
521
+ },
522
+ {
523
+ name: 'agent_dispatch',
524
+ description: 'Dispatch a task to a registered proxy tool or sovereign agent. Runs ERE gate before forwarding.',
525
+ inputSchema: {
526
+ type: 'object',
527
+ properties: {
528
+ target: { type: 'string', description: 'Proxy name (from proxy_list) or agent key' },
529
+ payload: { type: 'string', description: 'Task or query payload' },
530
+ },
531
+ required: ['target', 'payload'],
532
+ },
533
+ },
534
+ {
535
+ name: 'magma_exec',
536
+ description: 'Execute a Magma instruction string. Parses §VERB:AGENT:ACTION{payload}, routes to the correct executor (COMPUTE→Bedrock, QUERY→Groq, SEAL→WORM, DISPATCH→proxy), signs result with governor Ed25519 key, and returns Magma envelope. Supports pipeline syntax with >>.',
537
+ inputSchema: {
538
+ type: 'object',
539
+ properties: {
540
+ instruction: { type: 'string', description: 'Magma instruction: Β§VERB:AGENT:ACTION{payload} or pipeline with >>' },
541
+ verify_sig: { type: 'string', description: 'Optional: base64 Ed25519 sig to verify against instruction before executing' },
542
+ },
543
+ required: ['instruction'],
544
+ },
545
+ },
546
+ {
547
+ name: 'governor_pubkey',
548
+ description: 'Return the governor public key (base64 Ed25519 SPKI DER). Use this to verify Magma envelope signatures externally.',
549
+ inputSchema: { type: 'object', properties: {} },
550
+ },
551
+ ]
552
+
553
+ // ── Tool handler ��─────────────────────────────────────────────────────────────
554
+ async function handleTool(name, args) {
555
+ switch (name) {
556
+
557
+ case 'compute_route': {
558
+ const { agent = 'ahmad', query, max_tokens = 1024 } = args
559
+ const result = await computeRoute(agent, query, max_tokens)
560
+ const ere = runERE(result.text, 'response.txt')
561
+ const envelope = buildMagmaEnvelope(agent, result.text, ere)
562
+ return {
563
+ content: [{ type: 'text', text: result.text }],
564
+ magma_envelope: envelope,
565
+ provider: result.provider,
566
+ model: result.model,
567
+ usage: result.usage ?? null,
568
+ }
569
+ }
570
+
571
+ case 'ere_verify': {
572
+ const { content, filepath = 'unknown' } = args
573
+ const ere = runERE(content, filepath)
574
+ const summary = ere.certified
575
+ ? 'METATRON: YES β€” all 5 passes certified'
576
+ : `METATRON: NO β€” frozen at ${ere.failures.join(', ')}`
577
+ return {
578
+ content: [{ type: 'text', text: summary }],
579
+ certified: ere.certified,
580
+ passes: ere.passes.map((p, i) => ({ pod: `P${i+1}`, ...p })),
581
+ failures: ere.failures,
582
+ }
583
+ }
584
+
585
+ case 'proxy_register': {
586
+ const { name: pname, url, description = '', secret = '' } = args
587
+ proxyRegistry.set(pname, { url, description, secret, registered: new Date().toISOString() })
588
+ log(`Proxy registered: ${pname} β†’ ${url}`)
589
+ return {
590
+ content: [{ type: 'text', text: `Proxy "${pname}" registered at ${url}` }],
591
+ name: pname, url, registered: true,
592
+ }
593
+ }
594
+
595
+ case 'proxy_list': {
596
+ const proxies = [...proxyRegistry.entries()].map(([k, v]) => ({
597
+ name: k, url: v.url, description: v.description, registered: v.registered,
598
+ }))
599
+ const text = proxies.length
600
+ ? proxies.map(p => `${p.name}: ${p.url} β€” ${p.description}`).join('\n')
601
+ : 'No proxies registered.'
602
+ return { content: [{ type: 'text', text }], proxies }
603
+ }
604
+
605
+ case 'magma_seal': {
606
+ const { content, agent = 'sovereign' } = args
607
+ const ere = runERE(content)
608
+ const envelope = buildMagmaEnvelope(agent, content, ere)
609
+ return {
610
+ content: [{ type: 'text', text: `Sealed. Hash: ${envelope.state_hash}` }],
611
+ magma_envelope: envelope,
612
+ certified: ere.certified,
613
+ }
614
+ }
615
+
616
+ case 'agent_dispatch': {
617
+ const { target, payload } = args
618
+
619
+ // Check ERE on the payload before forwarding
620
+ const ere = runERE(payload)
621
+ if (!ere.certified) {
622
+ return {
623
+ content: [{ type: 'text', text: `ERE gate blocked dispatch: ${ere.failures.join(', ')}` }],
624
+ blocked: true,
625
+ ere_failures: ere.failures,
626
+ }
627
+ }
628
+
629
+ // Check proxy registry first
630
+ if (proxyRegistry.has(target)) {
631
+ const proxy = proxyRegistry.get(target)
632
+ try {
633
+ const headers = { 'Content-Type': 'application/json' }
634
+ if (proxy.secret) headers['x-bot-secret'] = proxy.secret
635
+ const res = await fetch(`${proxy.url}/dispatch`, {
636
+ method: 'POST',
637
+ headers,
638
+ body: JSON.stringify({ payload, agent: target }),
639
+ signal: AbortSignal.timeout(20000),
640
+ })
641
+ const json = await res.json()
642
+ const envelope = buildMagmaEnvelope(target, JSON.stringify(json), ere)
643
+ return {
644
+ content: [{ type: 'text', text: JSON.stringify(json) }],
645
+ magma_envelope: envelope,
646
+ proxy: target,
647
+ }
648
+ } catch (err) {
649
+ return { content: [{ type: 'text', text: `Proxy "${target}" unreachable: ${err.message}` }], error: true }
650
+ }
651
+ }
652
+
653
+ // Fall through to compute_route for known agents
654
+ if (AGENT_PROMPTS[target.toLowerCase()]) {
655
+ const result = await computeRoute(target, payload)
656
+ const envelope = buildMagmaEnvelope(target, result.text, ere)
657
+ return {
658
+ content: [{ type: 'text', text: result.text }],
659
+ magma_envelope: envelope,
660
+ provider: result.provider,
661
+ }
662
+ }
663
+
664
+ return { content: [{ type: 'text', text: `Unknown target: "${target}"` }], error: true }
665
+ }
666
+
667
+ case 'magma_exec': {
668
+ const { instruction, verify_sig } = args
669
+
670
+ // Optional: verify incoming instruction signature before executing
671
+ if (verify_sig) {
672
+ const valid = verifySignature(instruction, verify_sig)
673
+ if (!valid) {
674
+ return {
675
+ content: [{ type: 'text', text: 'GOVERNOR VERIFY: FAILED β€” signature invalid, execution blocked' }],
676
+ blocked: true,
677
+ reason: 'invalid_signature',
678
+ }
679
+ }
680
+ }
681
+
682
+ const parsed = parseMagmaInstruction(instruction)
683
+ if (!parsed.ok) {
684
+ return {
685
+ content: [{ type: 'text', text: `Magma parse error: ${parsed.error}` }],
686
+ blocked: true, reason: 'parse_error',
687
+ }
688
+ }
689
+
690
+ const execResult = await executeInstruction(parsed)
691
+ const resultText = typeof execResult.result === 'string'
692
+ ? execResult.result
693
+ : JSON.stringify(execResult)
694
+
695
+ const ere = runERE(resultText)
696
+ const envelope = buildMagmaEnvelope(
697
+ parsed.agent ?? 'sovereign',
698
+ resultText,
699
+ ere,
700
+ )
701
+ // Attach governor signature to envelope
702
+ envelope.governor_sig = execResult.sig ?? signContent(resultText)
703
+ envelope.governor_pubkey = GOVERNOR.publicKey.toString('base64')
704
+
705
+ auditWrite({
706
+ type: 'MAGMA_EXEC',
707
+ instruction: instruction.slice(0, 200),
708
+ executor: execResult.executor,
709
+ ok: execResult.ok,
710
+ sig: envelope.governor_sig,
711
+ })
712
+
713
+ return {
714
+ content: [{ type: 'text', text: resultText }],
715
+ magma_envelope: envelope,
716
+ executor: execResult.executor,
717
+ ok: execResult.ok,
718
+ ...(execResult.error && { error: execResult.error }),
719
+ }
720
+ }
721
+
722
+ case 'governor_pubkey': {
723
+ return {
724
+ content: [{ type: 'text', text: GOVERNOR.publicKey.toString('base64') }],
725
+ pubkey_b64: GOVERNOR.publicKey.toString('base64'),
726
+ algorithm: 'Ed25519',
727
+ format: 'SPKI DER base64',
728
+ }
729
+ }
730
+
731
+ default:
732
+ throw new Error(`Unknown tool: ${name}`)
733
+ }
734
+ }
735
+
736
+ // ── JSON-RPC 2.0 handler ──────────────────────────────────────────────────────
737
+ async function handleRpc(msg) {
738
+ const { jsonrpc, id, method, params } = msg
739
+
740
+ if (method === 'initialize') {
741
+ return {
742
+ jsonrpc: '2.0', id,
743
+ result: {
744
+ protocolVersion: '2024-11-05',
745
+ capabilities: { tools: {} },
746
+ serverInfo: { name: 'sovereign-mcp', version: '1.0.0' },
747
+ },
748
+ }
749
+ }
750
+
751
+ if (method === 'tools/list') {
752
+ return { jsonrpc: '2.0', id, result: { tools: TOOLS } }
753
+ }
754
+
755
+ if (method === 'tools/call') {
756
+ const { name, arguments: args = {} } = params ?? {}
757
+ try {
758
+ const result = await handleTool(name, args)
759
+ return {
760
+ jsonrpc: '2.0', id,
761
+ result: {
762
+ status: 'SEALED',
763
+ ...result,
764
+ },
765
+ }
766
+ } catch (err) {
767
+ return {
768
+ jsonrpc: '2.0', id,
769
+ error: { code: -32000, message: err.message },
770
+ }
771
+ }
772
+ }
773
+
774
+ if (method === 'notifications/initialized') return null
775
+
776
+ return {
777
+ jsonrpc: '2.0', id,
778
+ error: { code: -32601, message: `Method not found: ${method}` },
779
+ }
780
+ }
781
+
782
+ // ── Stdio transport ───────────────────────────────────────────────────────────
783
+ function startStdio() {
784
+ log(`Sovereign MCP server starting (stdio mode)`)
785
+ log(`Bedrock: ${AWS_KEY ? 'ready' : 'no credentials'} Groq: ${GROQ_KEY ? 'ready' : 'no key'} Ollama: ${OLLAMA_URL}`)
786
+
787
+ let buffer = ''
788
+
789
+ process.stdin.setEncoding('utf8')
790
+ process.stdin.on('data', chunk => { buffer += chunk; processBuffer() })
791
+ process.stdin.on('end', () => process.exit(0))
792
+
793
+ function processBuffer() {
794
+ const lines = buffer.split('\n')
795
+ buffer = lines.pop() ?? ''
796
+ for (const line of lines) {
797
+ const trimmed = line.trim()
798
+ if (!trimmed) continue
799
+ try {
800
+ const msg = JSON.parse(trimmed)
801
+ handleRpc(msg).then(res => {
802
+ if (res) process.stdout.write(JSON.stringify(res) + '\n')
803
+ }).catch(err => {
804
+ log(`RPC error: ${err.message}`)
805
+ })
806
+ } catch (err) {
807
+ log(`Parse error: ${err.message}`)
808
+ }
809
+ }
810
+ }
811
+ }
812
+
813
+ // ── HTTP transport ────────────────────────────────────────────────────────────
814
+ function startHttp() {
815
+ log(`Sovereign MCP server starting (HTTP mode) on port ${PORT}`)
816
+
817
+ const server = createServer(async (httpReq, httpRes) => {
818
+ if (httpReq.method === 'OPTIONS') {
819
+ httpRes.writeHead(204, {
820
+ 'Access-Control-Allow-Origin': '*',
821
+ 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS',
822
+ 'Access-Control-Allow-Headers': 'Content-Type, x-bot-secret',
823
+ })
824
+ httpRes.end(); return
825
+ }
826
+
827
+ let body = ''
828
+ httpReq.on('data', c => { body += c })
829
+ httpReq.on('end', async () => {
830
+ httpRes.setHeader('Content-Type', 'application/json')
831
+ httpRes.setHeader('Access-Control-Allow-Origin', '*')
832
+ try {
833
+ const msg = JSON.parse(body)
834
+ const res = await handleRpc(msg)
835
+ httpRes.writeHead(200)
836
+ httpRes.end(JSON.stringify(res ?? { ok: true }))
837
+ } catch (err) {
838
+ httpRes.writeHead(400)
839
+ httpRes.end(JSON.stringify({ error: err.message }))
840
+ }
841
+ })
842
+ })
843
+
844
+ server.listen(PORT, () => log(`HTTP server live on http://localhost:${PORT}`))
845
+ }
846
+
847
+ // ── Entry ─────────────────────────────────────────────────────────────────────
848
+ if (HTTP_MODE) startHttp()
849
+ else startStdio()
src/node_key.js ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Sovereign Node Key β€” SnapKitty execution gate.
3
+ * Without a valid key, model routing refuses to compute.
4
+ *
5
+ * Get your key: licensing@snapkittywest.dev
6
+ * Copyright (C) 2026 Bel Esprit D'Accord Irrevocable Trust (EIN 42-697643)
7
+ */
8
+
9
+ import { createHmac, timingSafeEqual } from 'crypto'
10
+
11
+ const KEY_PREFIX = 'SNK-'
12
+ const KEY_HEADER = 'x-sovereign-node-key'
13
+ const KEY_ENV = 'SNAPKITTY_NODE_KEY'
14
+
15
+ const WALL = `
16
+ ╔══════════════════════════════════════════════════╗
17
+ β•‘ SOVEREIGN NODE KEY REQUIRED β•‘
18
+ β•‘ β•‘
19
+ β•‘ This software requires a valid SnapKitty β•‘
20
+ β•‘ node key to activate model routing. β•‘
21
+ β•‘ β•‘
22
+ β•‘ Get your key: β•‘
23
+ β•‘ licensing@snapkittywest.dev β•‘
24
+ β•‘ https://github.com/SNAPKITTYWEST β•‘
25
+ β•‘ β•‘
26
+ β•‘ Copyright (C) 2026 Bel Esprit D'Accord β•‘
27
+ β•‘ Irrevocable Trust (EIN 42-697643) β•‘
28
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
29
+ `
30
+
31
+ export function verifyNodeKey(key) {
32
+ const secret = process.env.SNAPKITTY_NODE_SECRET
33
+ if (!key || !key.startsWith(KEY_PREFIX) || !secret) return false
34
+ try {
35
+ const inner = key.slice(KEY_PREFIX.length)
36
+ const lastDash = inner.lastIndexOf('-')
37
+ if (lastDash === -1) return false
38
+ const payload = inner.slice(0, lastDash)
39
+ const signature = inner.slice(lastDash + 1)
40
+ const expected = createHmac('sha256', secret)
41
+ .update(payload)
42
+ .digest('hex')
43
+ .slice(0, 32)
44
+ const a = Buffer.from(expected)
45
+ const b = Buffer.from(signature.padEnd(32, '0').slice(0, 32))
46
+ return a.length === b.length && timingSafeEqual(a, b)
47
+ } catch { return false }
48
+ }
49
+
50
+ export function requireNodeKey(key) {
51
+ const k = key || process.env[KEY_ENV] || ''
52
+ if (!verifyNodeKey(k)) {
53
+ throw new Error(WALL)
54
+ }
55
+ }
56
+
57
+ // Express / Hono middleware
58
+ export function nodeKeyMiddleware(req, res, next) {
59
+ const key = req.headers[KEY_HEADER] || req.query?.node_key || ''
60
+ try {
61
+ requireNodeKey(key)
62
+ next()
63
+ } catch (e) {
64
+ res.status(401).json({
65
+ error: 'SOVEREIGN_NODE_KEY_REQUIRED',
66
+ message: 'Valid node key required for model routing.',
67
+ obtain: 'licensing@snapkittywest.dev'
68
+ })
69
+ }
70
+ }