You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(architecture): rewrite ARCHITECTURE.md to reflect v1.1.0 stack
Replaces the stale V-lang/triple-API description with the actual current
architecture: Idris2 ABI → Zig FFI (.so) → boj-invoke dlopen → Elixir
REST (port 7700). Key corrections:
- Three-layer stack → canonical four-layer stack with Elixir REST layer
- Removes Zig HTTP adapter / V-lang adapter references
- Documents the ADR-0006 five-symbol ABI and seven return codes
- Documents both dispatch paths (boj-invoke for .so; JsWorkerPool for
mod.js transitional) and the cartridge.json selector
- Documents HTTP surface (5 routes + pubkey endpoint; gRPC/GraphQL/SSE
marked as not yet live)
- Documents ECDH credential forwarding format
- Documents cartridge production process via launch-scaffolder; flags
cartridge-minter as legacy (Node.js, banned)
- Thread safety: attributes BEAM concurrency to Elixir layer; retains
Zig mutex discipline as defence-in-depth description
- Removes V-lang references from Unified-Zig-API section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The developer server ecosystem is fragmenting. MCP servers, LSP servers, DAP servers, build servers — each tool has its own server, each AI needs different capabilities. Developers drown in configuration. AI agents hunt across dozens of endpoints.
9
+
The developer server ecosystem is fragmenting. MCP servers, LSP servers, DAP servers,
10
+
build servers — each tool has its own server, each AI needs different capabilities.
11
+
Developers drown in configuration. AI agents hunt across dozens of endpoints.
6
12
7
13
**BoJ solves this.** AI goes to ONE place — the Teranga menu — and orders what it needs.
8
14
@@ -17,68 +23,235 @@ BoJ organises server capabilities as a 2D matrix:
|**Dispatcher**| Zig (`boj-invoke`) |`dlopen` the `.so`; single CLI call; zero persistent process |
56
+
|**REST server**| Elixir (Plug/Cowboy) | HTTP surface on port 7700; routes calls to Invoker or JsWorkerPool |
57
57
58
58
### Why these languages?
59
59
60
-
**Idris2** has dependent types that prove interface correctness at compile-time. The `IsUnbreakable` proof type mathematically guarantees that only `Ready` cartridges can be activated. This isn't aspirational — it's enforced by the type checker.
60
+
**Idris2** has dependent types that prove interface correctness at compile-time. The
61
+
`IsUnbreakable` proof type mathematically guarantees that only `Ready` cartridges can
62
+
be activated. This isn't aspirational — it's enforced by the type checker.
63
+
64
+
**Zig** provides native C ABI compatibility without runtime overhead. It bridges the
65
+
gap between Idris2's proofs and actual system calls. Cross-compilation is built-in,
66
+
which matters for community nodes running on varied hardware.
67
+
68
+
**Elixir/OTP** provides the HTTP surface, supervision tree, ETS-backed catalog, and
69
+
BEAM concurrency — replacing the former V-lang adapter layer (V-lang was banned
70
+
estate-wide 2026-04-10; all `.v` files removed 2026-04-12, commit c4674f8).
71
+
72
+
## The ADR-0006 Cartridge ABI
73
+
74
+
Every compiled cartridge exposes exactly five C-ABI symbols:
75
+
76
+
```
77
+
boj_cartridge_init() → i32 initialise module state
78
+
boj_cartridge_deinit() → void release module state
79
+
boj_cartridge_name() → [*c]u8 null-terminated name string
80
+
boj_cartridge_version() → [*c]u8 semver string
81
+
boj_cartridge_invoke(
82
+
tool_name: [*c]const u8,
83
+
json_args: [*c]const u8,
84
+
out_buf: [*c]u8,
85
+
in_out_len:[*c]usize,
86
+
) → i32
87
+
```
88
+
89
+
Return codes (frozen by ADR-0006; new failure modes use error JSON, not new integers):
90
+
91
+
| Code | Meaning |
92
+
|------|---------|
93
+
|`0`| success |
94
+
|`-1`| unknown tool |
95
+
|`-2`| bad args (null pointer) |
96
+
|`-3`| buffer too small (retry with larger buffer) |
97
+
|`-4`| runtime error |
98
+
|`-5`| panic |
99
+
|`-6`| auth denied |
100
+
101
+
The `cartridge_shim.zig` in every `ffi/` directory centralises null-guards,
102
+
tool-name comparison, and the buffer-too-small retry pattern so domain `.zig` files
→ configures auth method, credential requirements, loopback port
61
204
62
-
**Zig** provides native C ABI compatibility without runtime overhead. It bridges the gap between Idris2's proofs and actual system calls. Cross-compilation is built-in, which matters for community nodes running on varied hardware. Zig now covers both the FFI layer and the adapter (HTTP server) layer.
AI agents act as the "Maitre D'" — presenting the menu to users, taking their order, and having the kitchen prepare it.
226
+
AI agents act as the "Maitre D'" — presenting the menu to users, taking their order,
227
+
and having the kitchen prepare it.
73
228
74
229
## The Order-Ticket Protocol
75
230
76
231
1. AI reads the Teranga menu (`menu.a2ml`)
77
232
2. AI writes an order ticket (`order-ticket.scm`)
78
-
3. BoJ validates the order against the catalogue (checks `IsUnbreakable`)
79
-
4. BoJ mounts requested cartridges via Zig FFI
80
-
5. Zig adapter exposes mounted cartridges as REST+gRPC+GraphQL
81
-
6. AI receives confirmation with endpoints
233
+
3. BoJ validates the order against the catalogue (`IsUnbreakable` check)
234
+
4. BoJ routes invoke calls via `BojRest.Router`:
235
+
- Cartridge has `"ffi"` key → `BojRest.Invoker` → `boj-invoke dlopen`
236
+
- Cartridge has no `"ffi"` key → `BojRest.JsWorkerPool` → Deno `mod.js`
237
+
5. Results returned as JSON; credentials forwarded encrypted end-to-end
238
+
6. AI receives tool output
239
+
240
+
## Thread Safety
241
+
242
+
**BEAM layer (Elixir):** Concurrency is handled by OTP. Each invoke request runs in
243
+
its own Elixir process. `BojRest.Catalog` uses a named ETS table (`:boj_catalog`)
244
+
for lock-free concurrent reads. `BojRest.NodeKey` uses `:boj_node_key` ETS for the
245
+
same reason. `BojRest.JsWorkerPool` serialises per-worker via `GenServer.call/3`.
246
+
247
+
**Zig FFI layer:** Each `.so`'s domain module uses `std.Thread.Mutex` to guard
248
+
global state, following the two-tier convention:
249
+
-`pub export fn boj_<domain>_*` — acquires mutex; called by `boj-invoke` / external C
250
+
-`fn <domain>_*_impl` — mutex-free; called by other functions already holding the lock
251
+
252
+
Since `boj-invoke` is a single-shot process, re-entrant deadlocks cannot occur in
253
+
the current dispatch model. The mutex discipline remains as defence-in-depth and for
254
+
future hot-path call modes.
82
255
83
256
## Distributed Hosting (Umoja Network)
84
257
@@ -88,7 +261,7 @@ BoJ servers are community-hosted, like Tor or IPFS:
88
261
-**Hash attestation**: each node's binary hash must match the canonical build
89
262
-**Tampered nodes**: excluded from the community network, but can still run locally
90
263
-**Gossip protocol**: nodes discover each other via IPv6 gossip (Byzantine fault tolerant)
91
-
-**Load-aware routing**: requests go to healthy nodes (under 80% capacity)
264
+
-**Load-aware routing**: requests go to healthy nodes (under 80% capacity) — _not yet wired_
92
265
-**PMPL provenance**: the license's cryptographic provenance requirements ARE the attestation
93
266
94
267
### Seed Nodes (Day 1)
@@ -112,85 +285,25 @@ BoJ servers are community-hosted, like Tor or IPFS:
112
285
BoJ doesn't start from scratch:
113
286
114
287
-**proven-servers** (108 components, zero `believe_me`): MCP types, connectors, core primitives
115
-
-**polystack** (13 components — being superseded by BoJ): capability domain mapping
288
+
-**polystack** (13 components — superseded by BoJ): capability domain mapping
116
289
-**stapeln**: container supply chain
117
290
118
-
## Thread Safety
119
-
120
-
All 9 Zig FFI modules (`database.zig`, `secrets.zig`, `observe.zig`, `fleet.zig`, `nesy.zig`, `federation.zig`, `lsp.zig`, `dap.zig`, `bsp.zig`) use `std.Thread.Mutex` to guarantee safe concurrent access from the Zig HTTP adapter worker threads.
121
-
122
-
**Mutex discipline:**
123
-
124
-
- Every `pub export fn` (C-ABI boundary) acquires the module's mutex before touching any global state and releases it on return via `defer`.
125
-
- 55 global variables are protected across 120+ exported functions.
126
-
- No Zig FFI function can be entered concurrently on two threads with conflicting access to the same global.
127
-
128
-
**Deadlock prevention:**
129
-
130
-
Some exported functions need to call other exported functions within the same module (e.g. `federation.zig` where a gossip handler invokes node-lookup logic). If both the caller and callee acquire the same mutex, the thread deadlocks on itself. The solution is a two-tier naming convention:
131
-
132
-
1.`pub export fn boj_federation_*` — acquires the mutex, called only from the Zig adapter / external C code.
133
-
2.`fn federation_*_impl` — mutex-free internal implementation, called by other functions that already hold the lock.
134
-
135
-
Exported functions delegate to `_impl` helpers, and re-entrant internal call paths use `_impl` directly, so the mutex is acquired exactly once per external call.
136
-
137
-
**Concurrency model:**
138
-
139
-
The Zig HTTP adapter spawns worker threads that call into the Zig FFI concurrently. Because every C-ABI entry point serialises on the module mutex, workers never observe torn or partially-updated state. The cost is per-module serialisation, which is acceptable at current scale; future work may introduce per-resource fine-grained locks if profiling warrants it.
140
-
141
-
**panic-attack validation:**
142
-
143
-
The panic-attack security scanner validated the thread-safety model across all 9 modules. Results: 1 expected weak point (QUIC crypto — inherent to the protocol's 0-RTT replay window, mitigated at the application layer), 0 critical vulnerabilities.
144
-
145
-
## Transport Layer
146
-
147
-
BoJ supports multiple transport protocols:
148
-
149
-
-**stdio** (default): JSON-RPC 2.0 over stdin/stdout (Claude Code, Glama, etc.)
150
-
-**REST** (port 7700): HTTP/1.1 JSON API
151
-
-**gRPC** (port 7701): Binary protocol for high-performance clients
152
-
-**GraphQL** (port 7702): Flexible querying for UI/integration layers
153
-
-**SSE** (port 7703): Server-Sent Events for real-time updates
154
-
155
-
Each cartridge declares its supported protocols in its manifest. The MCP bridge routes calls to the appropriate transport.
156
-
157
-
### Auto-Reconnect
158
-
159
-
The transport layer includes an exponential backoff with jitter mechanism for improved resilience. This ensures that the system can recover from connection issues and continue to operate smoothly.
160
-
161
-
### Health Monitoring
162
-
163
-
The transport layer provides detailed health information, including cartridge connection status. This makes it easy to monitor the system's health and identify any issues.
164
-
165
-
## Unified-Zig-API Stack
291
+
## Unified-Zig-API Stack Alignment
166
292
167
293
The estate standard for all Zig-edge service boundaries is the **unified-zig-api
168
-
stack** documented in `developer-ecosystem/UNIFIED-ZIG-API-STACK.adoc`. It provides
| Idris2 ABI |`developer-ecosystem/zig-api/src/ZigApi/ABI/`| Dependent-type proofs of the C ABI surface |
175
-
| Zig runtime |`developer-ecosystem/zig-api/ffi/zig/src/`|`uapi_*` symbol set; gnosis HTTP server + connector pool |
176
-
| C adaptor |`developer-ecosystem/zig-api/generated/abi/zig_api.h`| Auto-generated; never edit by hand |
294
+
stack** documented in `developer-ecosystem/UNIFIED-ZIG-API-STACK.adoc`.
177
295
178
-
**BoJ alignment status (2026-04-17):** BoJ does **not yet** consume `libzig_api`
296
+
**BoJ alignment status (2026-04-25):** BoJ does **not yet** consume `libzig_api`
179
297
in code. The cartridge ABI + FFI layers (Idris2 + Zig) are BoJ-local today.
180
298
Full alignment — calling `uapi_gnosis_*` from the BoJ adapter and linking
181
-
`libzig_api.so` — is tracked in ADR-0002 as future work. First estate consumers
182
-
wired on 2026-04-17: lol-gateway, aerie, emergency-button/emergency-room.
183
-
184
-
The `uapi_gnosis_set_handler` pattern (single-port handler dispatch) is in flight
185
-
as of this version; BoJ will adopt it once stabilised upstream.
186
-
187
-
**Open question:** When BoJ adopts `uapi_gnosis_*`, the per-port V-legacy adapter
188
-
pattern (ports 7700/7701/7702) will consolidate to a single-port gnosis server.
189
-
Port assignments are documented in `docs/API-CONTRACT.md` and are stable until
190
-
a major version bump. Track progress in ADR-0002.
299
+
`libzig_api.so` — is tracked in ADR-0002 as future work.
191
300
192
-
See `developer-ecosystem/UNIFIED-ZIG-API-STACK.adoc` for the canonical wiring guide.
301
+
When BoJ adopts `uapi_gnosis_*`, the per-port adapter pattern (REST/7700) will
302
+
consolidate to a single-port gnosis server. Port assignments are in `docs/API-CONTRACT.md`
303
+
and are stable until a major version bump.
193
304
194
305
## License
195
306
196
-
PMPL-1.0-or-later. The license's provenance requirements (crypto signatures, emotional lineage) align directly with the hash attestation model — the legal framework and the technical framework say the same thing.
307
+
PMPL-1.0-or-later. The license's provenance requirements (crypto signatures,
308
+
emotional lineage) align directly with the hash attestation model — the legal
309
+
framework and the technical framework say the same thing.
0 commit comments