Skip to content

Commit 797a85d

Browse files
hyperpolymathclaude
andcommitted
feat(v1.1.0): gateway sidecar in compose.dev.yaml + retire static policy
- compose.dev.yaml: add capability-gateway service (port 7800 → boj-rest:7700) using BOJ_CARTRIDGES_ROOT catalog mode — no static YAML needed at runtime; depends_on boj-rest healthy before starting - gateway-policy.yaml: retired (replaced by catalog mode); content replaced with historical reference comment explaining the migration - ROADMAP.adoc: tick both v1.1.0 gateway items as complete - STATE.a2ml: version 1.0.1-wip → 1.1.0-wip; Phase 8 completion 15→100; 112 cartridges throughout; js-dispatch updated to JsWorkerPool; resolved gaps 3/4/6/7 removed; new session-history entry; gateway architecture updated - llm-warmup-dev.md: gateway-policy.yaml marked RETIRED Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f682834 commit 797a85d

5 files changed

Lines changed: 88 additions & 92 deletions

File tree

.machine_readable/6a2/STATE.a2ml

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,22 @@
55

66
[metadata]
77
project = "boj-server"
8-
version = "1.0.1-wip"
8+
version = "1.1.0-wip"
99
last-updated = "2026-04-25"
1010
status = "active"
1111
grade = "D-alpha"
1212

1313
[project-context]
1414
name = "Bundle of Joy Server"
15-
purpose = "Unified server capability catalogue. AI goes to ONE place instead of hunting across dozens of MCP servers. Distributed via community-hosted Umoja nodes with gossip protocol and hash attestation. 106 cartridges covering the full developer-tool matrix."
15+
purpose = "Unified server capability catalogue. AI goes to ONE place instead of hunting across dozens of MCP servers. Distributed via community-hosted Umoja nodes with gossip protocol and hash attestation. 112 cartridges covering the full developer-tool matrix."
1616
completion-percentage = 65
1717

1818
[stack]
1919
# Current authoritative stack — V-lang adapter layer is GONE (banned 2026-04-10, removed 2026-04-12)
2020
abi-layer = "Idris2 — formal interface definitions with dependent-type proofs"
2121
ffi-layer = "Zig — C-ABI .so shared libraries (boj-invoke CLI for dlopen dispatch)"
2222
rest-server = "Elixir (Plug/Cowboy) — HTTP surface on port 7700, 5 routes"
23-
js-dispatch = "Deno — mod.js cartridges invoked via priv/js_runner.js per-call fork"
23+
js-dispatch = "Deno — mod.js cartridges dispatched via BojRest.JsWorkerPool (persistent pool, consistent-hash routing) with JsInvoker fork-per-call fallback"
2424
container = "Chainguard wolfi-base (Containerfile, 3-stage Zig+Elixir build)"
2525
federation = "Zig QUIC gossip (X25519 + ChaCha20-Poly1305, UDP 9999)"
2626

@@ -32,52 +32,48 @@ milestones = [
3232
{ name = "Phase 3: V-lang adapter layer", completion = 100, note = "RETIRED — V-lang banned 2026-04-10, all .v files removed 2026-04-12" },
3333
{ name = "Phase 4: First cartridges — boj-health Zig FFI reference impl", completion = 100 },
3434
{ name = "Phase 5: Umoja federation & PanLL panel", completion = 100 },
35-
{ name = "Phase 6: 106-cartridge matrix (all have cartridge.json + mod.js)", completion = 100 },
36-
{ name = "Phase 7: Elixir REST server wired to Zig FFI + JS Deno dispatch", completion = 100 },
37-
{ name = "Phase 8: Gateway sidecar (http-capability-gateway policy + catalog integration)", completion = 15 },
35+
{ name = "Phase 6: 112-cartridge matrix (all have cartridge.json + mod.js)", completion = 100 },
36+
{ name = "Phase 7: Elixir REST server wired to Zig FFI + JsWorkerPool (persistent Deno pool)", completion = 100 },
37+
{ name = "Phase 8: Gateway sidecar (http-capability-gateway catalog mode + compose.dev.yaml)", completion = 100 },
3838
{ name = "Phase 9: Auth layer (HTTP invoke caller auth, trust-level forwarding, credential proxy)", completion = 0 },
3939
{ name = "Phase 10: Grade D→C (QUIC multi-node, SLA, seed nodes, external validation)", completion = 30 },
4040
]
4141

4242
[quality]
43-
cartridges-total = 106 # all 106 have cartridge.json + mod.js as of 2026-04-25
43+
cartridges-total = 112 # all 112 have cartridge.json + mod.js as of 2026-04-25
4444
cartridges-with-zig-ffi = 1 # boj-health only — the Zig .so reference implementation
45-
cartridges-with-js-mod = 105 # mod.js dispatch via Deno (includes model-router-mcp pure-local)
45+
cartridges-with-js-mod = 111 # mod.js dispatch via Deno worker pool
4646
cartridges-with-both = 0 # no cartridge uses both paths
47+
exunit-tests = 50 # catalog(11) + router(12) + credential_decryptor(12) + node_key(7) + js_invoker(8)
4748
zig-ffi-chain-verified = true # boj-invoke → dlopen → libboj_health.so end-to-end tested 2026-04-25
48-
js-dispatch-chain-verified = true # deno run js_runner.js → mod.js handleTool — tested 2026-04-25
49+
js-dispatch-chain-verified = true # JsWorkerPool → JsWorkerDeno port → mod.js handleTool
4950
believe-me-count = 4 # 3 axiomatic primitives + 1 logSafeBounded — reduced from 31 on 2026-04-12
5051
v-lang-files = 0 # all .v files removed 2026-04-12
5152

5253
[open-gaps]
5354
# Ordered by impact on users hitting POST /cartridge/:name/invoke
5455
gap-1 = "No HTTP-layer auth on invoke endpoint — any caller can hit any cartridge (see docs/AUTH-DESIGN.adoc)"
5556
gap-2 = "Credential forwarding in federation — user API keys live on their node, not on volunteer nodes"
56-
gap-3 = "Deno fork-per-call overhead ~200ms cold start — worker pool is Phase 2"
57-
gap-4 = "Gateway catalog-mode (PolicyLoader.load_from_boj_catalog) not yet implemented in http-capability-gateway"
58-
gap-5 = "Load-aware inter-node routing not wired — invocations are local only"
59-
gap-6 = "TEST-NEEDS.md references V-lang era tests — needs rewrite against Elixir/Deno stack"
60-
gap-7 = "STATE.a2ml was massively stale until 2026-04-25 — all session-history entries before this date describe the old V-lang stack"
57+
gap-3 = "Load-aware inter-node routing not wired — invocations are local only"
58+
gap-4 = "Test coverage at CRG D (50 tests) — Grade C requires 165+ tests (see TEST-NEEDS.md)"
6159

6260
[critical-next-actions]
6361
actions = [
62+
"Expand test suite from 50 → 165+ tests to reach CRG C (see TEST-NEEDS.md: property tests, contract harnesses, aspect harnesses, benchmarks)",
6463
"Decide and implement HTTP invoke auth (see docs/AUTH-DESIGN.adoc — options: bearer token, mTLS header, or node-identity header)",
6564
"Implement credential-forwarding design for federation (user keys travel with the request encrypted, vs node-level key config)",
66-
"Implement PolicyLoader.load_from_boj_catalog in http-capability-gateway (see docs/gateway-catalog-integration.adoc)",
67-
"Rewrite TEST-NEEDS.md against Elixir/Deno stack",
68-
"Deploy gateway sidecar in compose.dev.yaml once catalog mode is ready",
6965
"Deploy 4 seed nodes to actual infrastructure",
7066
]
7167

7268
[blockers-and-issues]
7369
invoke-auth = "OPEN — HTTP invoke endpoint has no caller auth (see docs/AUTH-DESIGN.adoc)"
7470
credential-forwarding = "OPEN — design not yet decided; critical for federation use case"
75-
gateway-catalog-mode = "OPEN — PolicyLoader.load_from_boj_catalog not yet implemented"
76-
test-needs-stale = "OPEN — TEST-NEEDS.md references V-lang era, needs rewrite"
71+
test-coverage = "OPEN — 50 ExUnit tests (CRG D); Grade C requires 165+ (see TEST-NEEDS.md)"
7772

7873
[session-history]
7974
entries = [
80-
{ date = "2026-04-25", description = "Major session: (1) Fixed Zig ElfDynLib bug — boj-invoke now targets x86_64-linux-gnu, forcing DlDynLib/real dlopen. (2) boj-health Zig FFI cartridge fully implemented and tested. (3) Elixir REST server wired: Containerfile rewritten (3-stage Zig+Elixir build), compose.dev.yaml created. (4) All 106 cartridges now have mod.js (7 were missing). (5) model-router-mcp gets cartridge.json. (6) JS dispatch path implemented: BojRest.JsInvoker + priv/js_runner.js + Router dispatch branch. (7) cartridge_so_path/1 fixed to read ffi.so_path from manifest. (8) Gateway policy + catalog-integration design doc added. (9) ROADMAP v1.1.0 milestone added. (10) STATE.a2ml rewritten to reflect current reality." },
75+
{ date = "2026-04-25", description = "v1.1.0 + v1.0.1 doc sweep: (1) ExUnit test suite 50 tests across 5 modules. (2) StreamData property test dep added. (3) BojRest.JsWorkerPool + JsWorker (persistent Deno pool) implemented; Router now dispatches via pool. (4) PolicyLoader.load_from_boj_catalog/1 in http-capability-gateway — 12 tests. (5) compose.dev.yaml updated: capability-gateway sidecar (port 7800 → boj-rest:7700, BOJ_CARTRIDGES_ROOT catalog mode). (6) gateway-policy.yaml retired (static policy superseded by catalog mode). (7) 4 cartridges fixed (academic-workflow, ephapax, fireflag, sanctify — missing domain/tier/auth/ffi fields). (8) Full doc sweep: TOPOLOGY.md, ROADMAP.adoc, EXPLAINME.adoc, QUICKSTART-USER.adoc, llm-warmup-dev.md — all V-lang refs removed, counts 97/99/106 → 112. (9) TEST-NEEDS.md rewritten as honest CRG D gap analysis." },
76+
{ date = "2026-04-25", description = "Earlier session: (1) Fixed Zig ElfDynLib bug — boj-invoke now targets x86_64-linux-gnu, forcing DlDynLib/real dlopen. (2) boj-health Zig FFI cartridge fully implemented and tested. (3) Elixir REST server wired: Containerfile rewritten (3-stage Zig+Elixir build), compose.dev.yaml created. (4) All 112 cartridges now have mod.js. (5) model-router-mcp gets cartridge.json. (6) JS dispatch path implemented: BojRest.JsInvoker + priv/js_runner.js + Router dispatch branch. (7) cartridge_so_path/1 fixed to read ffi.so_path from manifest. (8) Gateway policy + catalog-integration design doc added. (9) ROADMAP v1.1.0 milestone added." },
8177
{ date = "2026-04-20", description = "Started Elixir REST backend on port 7700 as V-lang adapter replacement. Fixed just doctor in Justfile." },
8278
{ date = "2026-04-17", description = "Spec bump v0.4.0: V-lang ban documented. ADR-0002 created. EXPLAINME.adoc corrections." },
8379
{ date = "2026-04-12", description = "V-lang sweep complete: 0 .v files remain. believe_me sweep: 31→4. CI fixes." },
@@ -86,11 +82,11 @@ entries = [
8682
]
8783

8884
[architecture]
89-
stack-summary = "Idris2 ABI → Zig FFI (.so) → Elixir REST (HTTP/7700) → Deno JS (mod.js per-call)"
90-
dispatch-rule = "cartridge.json with 'ffi' key → boj-invoke CLI; without 'ffi' key → deno js_runner.js"
85+
stack-summary = "Idris2 ABI → Zig FFI (.so) → Elixir REST (HTTP/7700) → Deno JS (mod.js via JsWorkerPool)"
86+
dispatch-rule = "cartridge.json with 'ffi' key → BojRest.Invoker (boj-invoke CLI); without 'ffi' key → BojRest.JsWorkerPool (persistent Deno pool)"
9187
federation = "Zig QUIC gossip: X25519 handshake + ChaCha20-Poly1305, SDP allow-list, hash attestation"
9288
auth-gap = "Federation trust is gossip-layer only; HTTP invoke has no caller auth — see docs/AUTH-DESIGN.adoc"
93-
gateway = "http-capability-gateway planned as sidecar (port 7800 → boj-rest:7700); static policy in container/gateway-policy.yaml; catalog-mode design in docs/gateway-catalog-integration.adoc"
89+
gateway = "http-capability-gateway sidecar LIVE in compose.dev.yaml (port 7800 → boj-rest:7700); catalog mode active via BOJ_CARTRIDGES_ROOT; gateway-policy.yaml retired 2026-04-25"
9490
cultural-pillars = ["Teranga (hospitality)", "Umoja (unity)", "Ayo (joy)"]
9591

9692
[ecosystem]

ROADMAP.adoc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ Gateway sidecar (`http-capability-gateway` in front of boj-server):
9292

9393
* [x] Add `gateway-policy.yaml` (done — `container/gateway-policy.yaml`) to dev compose as optional sidecar
9494
* [x] Implement `PolicyLoader.load_from_boj_catalog/1` in `http-capability-gateway` — reads `BOJ_CARTRIDGES_ROOT/*/cartridge.json` at boot, infers per-cartridge invoke exposure from `auth.method` (`none` → `public`, others → `authenticated`). Zero manual policy maintenance thereafter. See `docs/gateway-catalog-integration.adoc`.
95-
* [ ] Update `compose.dev.yaml` with gateway sidecar entry (port 7800 → boj-rest:7700)
96-
* [ ] Retire static `gateway-policy.yaml` once catalog mode is live
95+
* [x] Update `compose.dev.yaml` with gateway sidecar entry (port 7800 → boj-rest:7700)
96+
* [x] Retire static `gateway-policy.yaml` catalog mode live 2026-04-25; file kept as reference only
9797

9898
== Future Directions
9999

container/compose.dev.yaml

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,21 @@
22
#
33
# BoJ Server — dev compose (Podman Compose compatible)
44
#
5-
# Runs the Elixir server with pre-built boj-invoke and cartridge .so files
6-
# mounted from the local source tree. No container build needed.
5+
# Two services:
6+
# boj-rest Elixir/BEAM REST server on port 7700
7+
# capability-gateway http-capability-gateway sidecar on port 7800
8+
# proxies → boj-rest:7700, auto-builds policy from
9+
# BOJ_CARTRIDGES_ROOT (catalog mode — no static YAML needed)
710
#
811
# Prerequisites:
912
# cd ffi/zig && zig build invoke # build boj-invoke
1013
# cd cartridges/boj-health/ffi && zig build # build boj-health .so
1114
# cd elixir && mix deps.get # fetch Elixir deps
15+
# cd ../../web-ecosystem/http-capability-gateway && mix deps.get
1216
#
1317
# Usage:
1418
# podman-compose -f container/compose.dev.yaml up
19+
# podman-compose -f container/compose.dev.yaml up boj-rest # server only
1520
# podman-compose -f container/compose.dev.yaml down
1621

1722
services:
@@ -37,3 +42,32 @@ services:
3742
retries: 3
3843
start_period: 20s
3944
restart: unless-stopped
45+
46+
capability-gateway:
47+
image: docker.io/hexpm/elixir:1.19.5-erlang-27.3.4-debian-bookworm-20250520-slim
48+
working_dir: /gateway
49+
command: ["mix", "run", "--no-halt"]
50+
depends_on:
51+
boj-rest:
52+
condition: service_healthy
53+
ports:
54+
- "7800:7800"
55+
volumes:
56+
# Gateway source lives in sibling repo web-ecosystem/http-capability-gateway
57+
- ../../web-ecosystem/http-capability-gateway:/gateway:z
58+
# Workspace mount so BOJ_CARTRIDGES_ROOT can reach cartridges/*/cartridge.json
59+
- ..:/workspace:ro,z
60+
environment:
61+
MIX_ENV: dev
62+
PORT: "7800"
63+
BACKEND_URL: http://boj-rest:7700
64+
# Catalog mode: policy auto-derived from cartridge.json auth.method fields.
65+
# gateway-policy.yaml is NOT loaded when this var is set.
66+
BOJ_CARTRIDGES_ROOT: /workspace/cartridges
67+
healthcheck:
68+
test: ["CMD", "curl", "-sf", "http://localhost:7800/health"]
69+
interval: 15s
70+
timeout: 5s
71+
retries: 3
72+
start_period: 30s
73+
restart: unless-stopped

container/gateway-policy.yaml

Lines changed: 30 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,36 @@
11
# SPDX-License-Identifier: PMPL-1.0-or-later
22
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
33
#
4-
# BoJ Server — http-capability-gateway verb governance policy (DSL v1)
4+
# RETIRED — gateway-policy.yaml (Phase 1 static policy)
55
#
6-
# Sits in front of boj-server (port 7700). Gateway listens on port 7800
7-
# (override PORT env var). Backend: BACKEND_URL=http://127.0.0.1:7700
6+
# This file is no longer loaded. Catalog mode is now active:
87
#
9-
# Route surface (all 5 boj-server routes are covered):
10-
# GET /health — public, liveness probe
11-
# GET /menu — public, cartridge menu
12-
# GET /cartridges — public, cartridge name list
13-
# GET /cartridge/:name — public, cartridge metadata
14-
# POST /cartridge/:name/invoke — auth-required for keyed cartridges
15-
# (public fallback until catalog-mode lands)
8+
# PolicyLoader.load_from_boj_catalog/1 reads BOJ_CARTRIDGES_ROOT/*/cartridge.json
9+
# at gateway boot and auto-generates per-cartridge invoke exposure rules from
10+
# the auth.method field:
11+
# "none" → exposure: public
12+
# anything → exposure: authenticated
1613
#
17-
# Phase 1 (this file): static policy, all invoke calls public.
18-
# Phase 2 (catalog-mode): gateway reads BOJ_CARTRIDGES_ROOT/*/cartridge.json
19-
# at boot and auto-derives per-cartridge exposure from auth.method:
20-
# "none" → "public"
21-
# "api-key" → "authenticated"
22-
# Replaces this file entirely; zero ongoing maintenance.
23-
# See ROADMAP.adoc §v1.1.0 and docs/gateway-catalog-integration.adoc.
24-
25-
dsl_version: "1"
26-
27-
governance:
28-
# Global default: only GET is allowed on any path not listed below.
29-
# Prevents stray PUT / PATCH / DELETE / OPTIONS from reaching boj-server.
30-
global_verbs:
31-
- GET
32-
33-
routes:
34-
# Liveness + readiness probe
35-
- path: "^/health$"
36-
verbs: [GET]
37-
exposure: public
38-
narrative: "Liveness probe — no auth required"
39-
40-
# Cartridge menu (human-readable catalogue with tiers)
41-
- path: "^/menu$"
42-
verbs: [GET]
43-
exposure: public
44-
narrative: "Cartridge menu — public read"
45-
46-
# Cartridge name list
47-
- path: "^/cartridges$"
48-
verbs: [GET]
49-
exposure: public
50-
narrative: "Cartridge inventory — public read"
51-
52-
# Per-cartridge metadata (cartridge.json contents)
53-
- path: "^/cartridge/[^/]+$"
54-
verbs: [GET]
55-
exposure: public
56-
narrative: "Cartridge metadata — public read"
57-
58-
# Tool invocation — POST only, no other verbs.
59-
# Phase 1: public exposure (no auth wired yet in boj-server).
60-
# Phase 2: replaced by per-cartridge rules generated from auth.method.
61-
- path: "^/cartridge/[^/]+/invoke$"
62-
verbs: [POST]
63-
exposure: public
64-
narrative: "Tool invocation — Phase 1 public; Phase 2 auto-derived from cartridge auth.method"
65-
66-
stealth:
67-
# Return 404 (not 403) for denied requests so the route surface does
68-
# not advertise which paths exist to unauthenticated callers.
69-
enabled: true
70-
status_code: 404
14+
# The compose.dev.yaml capability-gateway service sets BOJ_CARTRIDGES_ROOT, which
15+
# takes priority over POLICY_PATH in the gateway's resolution order. This file
16+
# will never be loaded by that service.
17+
#
18+
# Kept for reference only. See:
19+
# docs/gateway-catalog-integration.adoc — catalog mode design
20+
# web-ecosystem/http-capability-gateway/lib/http_capability_gateway/policy_loader.ex
21+
# container/compose.dev.yaml — capability-gateway service definition
22+
#
23+
# Phase 1 policy (historical record):
24+
#
25+
# dsl_version: "1"
26+
# governance:
27+
# global_verbs: [GET]
28+
# routes:
29+
# - path: "^/health$" verbs: [GET] exposure: public
30+
# - path: "^/menu$" verbs: [GET] exposure: public
31+
# - path: "^/cartridges$" verbs: [GET] exposure: public
32+
# - path: "^/cartridge/[^/]+$" verbs: [GET] exposure: public
33+
# - path: "^/cartridge/[^/]+/invoke$" verbs: [POST] exposure: public
34+
# stealth:
35+
# enabled: true
36+
# status_code: 404

llm-warmup-dev.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ container/ Stapeln container ecosystem
7676
Containerfile Multi-stage OCI (Chainguard base)
7777
compose.toml selur-compose orchestration
7878
compose.dev.yaml Dev compose with gateway sidecar (port 7800)
79-
gateway-policy.yaml Static gateway policy (to be retired once catalog mode is live)
79+
gateway-policy.yaml RETIRED — historical reference only; catalog mode supersedes it
8080
vordr.toml Runtime monitoring
8181
8282
web-ecosystem/

0 commit comments

Comments
 (0)