push from SNAPKITTYWEST/sovereign-mcp
Browse files- .env.example +26 -0
- .gitignore +4 -0
- LICENSE +59 -0
- LICENSE.tri +101 -0
- README.md +252 -0
- package.json +28 -0
- sovereign-mcp.mjs +849 -0
- 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 |
+
}
|