Skip to content

Commit 8814b38

Browse files
hyperpolymathclaude
andcommitted
chore: sync local changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 83a1c28 commit 8814b38

5 files changed

Lines changed: 500 additions & 110 deletions

File tree

README.md

Lines changed: 61 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,28 @@
1-
# Bundle of Joy (BoJ) Server
1+
<!-- SPDX-License-Identifier: PMPL-1.0-or-later -->
22

3-
Unified server capability catalogue with formally verified cartridges, distributed community hosting, and the Teranga menu system.
3+
# BoJ -- Bundle of Joy
44

5-
> **AI-Assisted Install:** Just tell any AI assistant:
6-
> `Set up Bundle of Joy Server from https://github.com/hyperpolymath/boj-server`
7-
> The AI reads this repo, asks you a few questions, and handles everything.
5+
One MCP server that gives any AI assistant access to your databases, containers, git repos, secrets, and more -- instead of installing seven separate tools.
86

9-
<a href="https://glama.ai/mcp/servers/hyperpolymath/boj-server">
10-
<img width="380" height="200" src="https://glama.ai/mcp/servers/hyperpolymath/boj-server/badge" alt="BoJ MCP Server on Glama" />
11-
</a>
12-
<a href="https://www.bestpractices.dev/projects/12164">
13-
<img src="https://www.bestpractices.dev/projects/12164/badge" alt="OpenSSF Best Practices passing (115%)" />
14-
</a>
15-
16-
## What is this?
17-
18-
BoJ solves the **combinatoric explosion of developer server protocols**. Instead of hunting across dozens of MCP, LSP, DAP, and other servers, AI goes to ONE place — the Teranga menu — and orders what it needs.
19-
20-
The server is **distributed**: community nodes volunteer compute time (like Tor/IPFS), with cryptographic hash attestation ensuring integrity. No central hosting required.
21-
22-
## The 2D Capability Matrix
23-
24-
Capabilities are organised as a 2D matrix:
25-
26-
- **Columns** = protocol types (MCP, LSP, DAP, BSP, NeSy, Agentic, Fleet, gRPC, REST)
27-
- **Rows** = capability domains (Cloud, Container, Database, K8s, Git, Secrets, Queues, IaC, Observe, SSG, Proof, Fleet, NeSy)
28-
- **Cells** = cartridges (formally verified, swappable capability modules)
29-
30-
Each cartridge follows the **three-layer stack**:
31-
32-
| Layer | Language | Purpose |
33-
|-------|----------|---------|
34-
| **ABI** | Idris2 | Formal proofs, `%default total`, zero `believe_me` |
35-
| **FFI** | Zig | C-compatible native execution, zero runtime deps |
36-
| **Adapter** | V-lang | Triple API (REST + gRPC + GraphQL) |
37-
38-
## The Teranga Menu
39-
40-
The menu has three tiers:
41-
42-
- **Teranga (Core)**: Cartridges maintained by the project
43-
- **Shield**: Privacy and security (SDP, DoQ/DoH, oDNS)
44-
- **Ayo (Community)**: Community-contributed cartridges
45-
46-
AI agents act as the "Maître D'" — presenting the menu to users as honoured guests.
7+
## Quick Start
478

48-
See: `.machine_readable/servers/menu.a2ml`
9+
**1. Clone and build**
4910

50-
## MCP Installation
11+
```bash
12+
git clone https://github.com/hyperpolymath/boj-server
13+
cd boj-server
14+
cd ffi/zig && zig build
15+
```
5116

52-
The BoJ server exposes its cartridges via MCP (Model Context Protocol) over stdio.
17+
**2. Start the server**
5318

54-
**Prerequisites:** The BoJ REST server must be running on port 7700 (see Quick Start below).
19+
```bash
20+
cd boj-server && deno run --allow-net --allow-env mcp-bridge/main.js
21+
```
5522

56-
### Claude Code
23+
**3. Connect your AI assistant**
5724

58-
Add to `~/.config/claude/mcp_servers.json`:
25+
Add to your Claude Code MCP config (`~/.config/claude/mcp_servers.json`):
5926

6027
```json
6128
{
@@ -67,83 +34,68 @@ Add to `~/.config/claude/mcp_servers.json`:
6734
}
6835
```
6936

70-
### Other MCP Clients
37+
> HTTP/SSE remote transport for ChatGPT, Gemini, and other clients is coming soon.
7138
72-
```bash
73-
node mcp-bridge/main.js
74-
# or
75-
deno run --allow-net --allow-env mcp-bridge/main.js
76-
```
39+
## What Can It Do?
7740

78-
### MCP Tools
41+
BoJ organises capabilities into **cartridges** (pluggable modules), each covering a domain. Here is what is available today:
7942

80-
| Tool | Description |
81-
|------|-------------|
82-
| `boj_health` | Server health check |
83-
| `boj_menu` | List all cartridges (tiers, domains, protocols) |
84-
| `boj_cartridges` | Protocol x domain capability matrix |
85-
| `boj_cartridge_info` | Detailed info for a specific cartridge |
86-
| `boj_cartridge_invoke` | Execute a cartridge operation |
43+
| Domain | Cartridge | Example |
44+
|--------|-----------|---------|
45+
| Database | database-mcp | "Query my PostgreSQL database for all users created this week" |
46+
| Containers | container-mcp | "List running containers and restart the web server" |
47+
| Git | git-mcp | "Show me the diff between main and this branch across all three forges" |
48+
| Secrets | secrets-mcp | "Rotate the API key stored in Vault and update the deployment" |
49+
| Observability | observe-mcp | "Show me error rates from the last hour and correlate with recent deploys" |
50+
| Cloud | cloud-mcp | "Spin up a staging instance on my cloud provider" |
51+
| Kubernetes | k8s-mcp | "Scale the worker deployment to 5 replicas" |
52+
| Queues | queues-mcp | "Check the dead-letter queue depth and replay failed messages" |
53+
| Infrastructure | iac-mcp | "Plan the Terraform changes for the new VPC" |
54+
| Static Sites | ssg-mcp | "Build and preview the documentation site" |
55+
| Proof Assistants | proof-mcp | "Type-check the Idris2 module and show any holes" |
56+
| Language Servers | lsp-mcp | "Get completions and diagnostics for this file" |
57+
| Debugging | dap-mcp | "Set a breakpoint at line 42 and inspect the variable" |
58+
| Build Servers | bsp-mcp | "Run the build and report compile errors" |
59+
| Bot Fleet | fleet-mcp | "Run the security scan bots across all repositories" |
60+
| Neurosymbolic | nesy-mcp | "Classify this input using the symbolic reasoning pipeline" |
61+
| Agents | agent-mcp | "Dispatch an OODA-loop agent to investigate the incident" |
8762

88-
## Quick Start
63+
## Why BoJ Instead of Separate MCP Servers?
8964

90-
```bash
91-
# Clone and enter
92-
git clone https://github.com/hyperpolymath/boj-server
93-
cd boj-server
65+
**One connection, not seventeen.** Your AI assistant connects to one server and gets access to all domains through a single menu.
9466

95-
# Type-check the Idris2 ABI
96-
cd src/abi && idris2 --build boj.ipkg
67+
**Lower memory footprint.** One native process instead of 7+ separate `npm exec` processes each consuming 200-300 MB of RAM.
9768

98-
# Build the Zig FFI
99-
cd ../../ffi/zig && zig build
100-
101-
# Run tests
102-
cd ffi/zig && zig build test
103-
```
69+
**Verified state machines.** Each cartridge's lifecycle (connect, query, disconnect) is modelled as a state machine with formal proofs that prevent invalid transitions. This means your AI cannot, for example, issue a query on a closed database connection.
10470

105-
## Distributed Hosting (Umoja Network)
71+
**Federation-ready.** BoJ nodes can form a peer-to-peer network (Umoja federation) for production-scale distributed hosting. Community nodes volunteer compute, with cryptographic hash attestation ensuring integrity.
10672

107-
BoJ servers are community-hosted:
73+
## Current Status
10874

109-
- Pull the container from the Stapeln supply chain
110-
- Run it locally (Podman, Chainguard base)
111-
- Your node appears in the Umoja network via gossip protocol
112-
- Hash attestation ensures integrity — tampered nodes are excluded from the community network but can still run locally
75+
**Grade D (Alpha)** -- usable for experimentation, not yet production-hardened.
11376

114-
See: `docs/FEDERATION.md`
77+
| What | Status |
78+
|------|--------|
79+
| Cartridges built | 17 of 17, all with compiled .so files |
80+
| Tests passing | 307 |
81+
| MCP bridge | Working (stdio) |
82+
| REST / gRPC / GraphQL | Adapter compiles and routes |
83+
| Federation (Umoja) | Real UDP gossip with hash attestation |
84+
| Remote transport (HTTP/SSE) | Not yet |
85+
| External dogfooding | Not yet |
11586

116-
## Contributing Cartridges
87+
See [docs/READINESS.md](docs/READINESS.md) for the full component-by-component assessment.
11788

118-
Build a cartridge that passes the `IsUnbreakable` proof and it goes in the Ayo menu with your name honoured.
89+
## Architecture (For Contributors)
11990

120-
See: `docs/DEVELOPERS.md`
91+
BoJ uses Idris2 for interface proofs (zero `believe_me`), Zig for the C-compatible FFI layer, V-lang for the REST/gRPC/GraphQL adapter, and JavaScript for the MCP bridge. If that sounds like a lot of languages, it is -- each was chosen for a specific guarantee. See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for the rationale and contributor guide.
12192

122-
## Cultural Terminology
123-
124-
We use terms from Wolof (*Teranga* — hospitality), Swahili (*Umoja* — unity), and Yoruba (*Ayo* — joy) to align our architecture with global values.
125-
126-
See: `docs/CULTURAL-RESPECT.md`
93+
Cartridges are loaded as shared libraries (`.so` files) at runtime, verified by hash before mounting. Community-contributed cartridges go through the same verification pipeline.
12794

12895
## License
12996

130-
PMPL-1.0-or-later (Palimpsest License). The license's provenance requirements (crypto signatures, emotional lineage) align directly with the hash attestation model.
131-
132-
## Project Status
133-
134-
**Grade D (Alpha)** — 18 cartridges, 307 tests passing, thread-safe FFI (mutex-hardened), panic-attack validated. See `docs/READINESS.md` for the full CRG assessment and `.machine_readable/STATE.a2ml` for milestone progress.
135-
136-
## A Community Project
137-
138-
This is a community project. Nobody makes money from it. It exists because the problem (too many servers, too much fragmentation) needed solving, and the solution needed building.
139-
140-
**What would help most right now:**
141-
142-
- **Host a node** — Even for a few hours a week. Pull the container, let it gossip. Every node strengthens the network. See `docs/OPERATOR-QUICKSTART.md`.
143-
- **Try it out** — Use it via MCP, REST, or gRPC. Break things. Tell me what's confusing, what's missing, what doesn't work.
144-
- **Build on it** — Write a cartridge, create an extension, wire it to your own tools. The third axis is wide open.
145-
- **Give feedback** — Comments, issues, PRs, emails, carrier pigeons. All welcome.
97+
SPDX-License-Identifier: PMPL-1.0-or-later (Palimpsest License)
14698

147-
A huge thank you to anyone who takes the time to look at this. Even a quick glance and an honest opinion helps enormously. I built this to learn from it, and I learn most from other people using it.
99+
Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath)
148100

149-
Contact: `j.d.a.jewell@open.ac.uk` | GitHub: [hyperpolymath](https://github.com/hyperpolymath) | Issues: [boj-server/issues](https://github.com/hyperpolymath/boj-server/issues)
101+
See [LICENSE](LICENSE) for the full text.

adapter/v/src/main.v

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1418,7 +1418,53 @@ fn invoke_database(tool string, args string) http.Response {
14181418
'backends': 'verisimdb,postgresql,sqlite,redis'
14191419
}))
14201420
}
1421-
return error_response(400, 'unknown database-mcp tool: "${tool}" — available: health, list_octads, create_octad, query, drift, list_backends')
1421+
if tool == 'sql' {
1422+
// Direct SQLite query execution via the Zig FFI state machine.
1423+
// Args: {"path": "/path/to/db.sqlite", "sql": "SELECT ..."}
1424+
params := json.decode(map[string]string, args) or {
1425+
return error_response(400, 'sql requires {"path": "/path/to/db.sqlite", "sql": "SELECT ..."}')
1426+
}
1427+
db_path := params['path'] or { '' }
1428+
sql_text := params['sql'] or { '' }
1429+
if db_path == '' || sql_text == '' {
1430+
return error_response(400, 'sql requires both "path" and "sql" fields')
1431+
}
1432+
// Validate path does not contain shell-unsafe characters (defense in depth)
1433+
_ := sanitize_shell_arg(db_path) or {
1434+
return error_response(400, 'invalid path: ${err.msg()}')
1435+
}
1436+
1437+
// Connect to SQLite via Zig FFI
1438+
conn := database_adapter.connect_sqlite(db_path) or {
1439+
return json_response(json.encode({
1440+
'tool': 'sql'
1441+
'status': 'error'
1442+
'error': 'connect failed: ${err.msg()}'
1443+
}))
1444+
}
1445+
1446+
// Execute the SQL query
1447+
result_json := database_adapter.execute_sql(conn.slot, sql_text) or {
1448+
// Disconnect on error (error state -> disconnected)
1449+
database_adapter.disconnect(conn.slot) or {}
1450+
return json_response(json.encode({
1451+
'tool': 'sql'
1452+
'status': 'error'
1453+
'error': 'query failed: ${err.msg()}'
1454+
}))
1455+
}
1456+
1457+
// Disconnect after query
1458+
database_adapter.disconnect(conn.slot) or {}
1459+
1460+
return json_response(json.encode({
1461+
'tool': 'sql'
1462+
'status': 'ok'
1463+
'path': db_path
1464+
'data': result_json
1465+
}))
1466+
}
1467+
return error_response(400, 'unknown database-mcp tool: "${tool}" — available: health, list_octads, create_octad, query, drift, list_backends, sql')
14221468
}
14231469

14241470
// --- ssg-mcp: Static site generation (Zola, Hugo, ddraig) ---

cartridges/database-mcp/adapter/database_adapter.v

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import json
1616
// ═══════════════════════════════════════════════════════════════════════
1717

1818
fn C.db_connect(backend int) int
19+
fn C.db_connect_sqlite(path_ptr &u8, path_len usize) int
1920
fn C.db_disconnect(slot_idx int) int
2021
fn C.db_state(slot_idx int) int
2122
fn C.db_begin_query(slot_idx int) int
2223
fn C.db_end_query(slot_idx int) int
2324
fn C.db_query_error(slot_idx int) int
2425
fn C.db_can_transition(from int, to int) int
26+
fn C.db_execute_sql(slot u8, sql_ptr &u8, sql_len usize, out_ptr &u8, out_len usize) int
2527
fn C.db_reset()
2628

2729
// ═══════════════════════════════════════════════════════════════════════
@@ -154,6 +156,41 @@ pub fn can_transition(from int, to int) TransitionResponse {
154156
}
155157
}
156158

159+
pub fn connect_sqlite(path string) !ConnectResponse {
160+
slot := C.db_connect_sqlite(path.str, usize(path.len))
161+
if slot < 0 {
162+
return match slot {
163+
-1 { error('no connection slots available') }
164+
-3 { error('sqlite3_open failed for path: ${path}') }
165+
else { error('unknown error (code ${slot})') }
166+
}
167+
}
168+
return ConnectResponse{
169+
slot: slot
170+
backend: 'sqlite'
171+
state: 'connected'
172+
}
173+
}
174+
175+
pub fn execute_sql(slot int, sql string) !string {
176+
if slot < 0 || slot > 255 {
177+
return error('invalid slot index: ${slot}')
178+
}
179+
mut out_buf := []u8{len: 65536}
180+
result := C.db_execute_sql(u8(slot), sql.str, usize(sql.len), out_buf.data, usize(out_buf.len))
181+
if result < 0 {
182+
return match result {
183+
-1 { error('invalid or inactive slot') }
184+
-2 { error('connection not in queryable state') }
185+
-3 { error('slot does not have a sqlite handle (wrong backend?)') }
186+
-4 { error('sqlite3_exec failed') }
187+
-5 { error('output buffer too small') }
188+
else { error('unknown error (code ${result})') }
189+
}
190+
}
191+
return out_buf[..result].bytestr()
192+
}
193+
157194
pub fn reset() {
158195
C.db_reset()
159196
}

cartridges/database-mcp/ffi/build.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33
//
44
// Database-MCP Cartridge — Zig FFI build configuration (Zig 0.15+).
5+
// Links against system libsqlite3 for real database operations.
56

67
const std = @import("std");
78

@@ -19,6 +20,8 @@ pub fn build(b: *std.Build) void {
1920
const db_tests = b.addTest(.{
2021
.root_module = db_mod,
2122
});
23+
db_tests.linkSystemLibrary("sqlite3");
24+
db_tests.linkLibC();
2225

2326
const run_tests = b.addRunArtifact(db_tests);
2427

@@ -35,6 +38,8 @@ pub fn build(b: *std.Build) void {
3538
}),
3639
.linkage = .dynamic,
3740
});
41+
lib.linkSystemLibrary("sqlite3");
42+
lib.linkLibC();
3843
b.installArtifact(lib);
3944

4045
const lib_step = b.step("lib", "Build shared library");

0 commit comments

Comments
 (0)