Skip to content

Commit 7c2b815

Browse files
feat(cartridges): canonicalise BoJ cartridge format in standards/ (#200)
## Summary Adds the canonical cartridge format specification + JSON schema as a new top-level `standards/cartridges/` directory, plus ADR-002 ratifying standards as the canonical home with boj-server's local schema becoming a SHA-pinned mirror. This is the foundational PR for an estate-wide initiative to (1) extract the 125 cartridges currently bundled in `boj-server/cartridges/` into a new `hyperpolymath/boj-server-cartridges` repo with on-demand fetch, and (2) expand cartridge coverage beyond MCP into LSP/DAP/BSP/Format/Lint/Build/NeSy/Agentic/Fleet roles (using polystack's archived LSP code as the seed for the LSP halves). ## Files - `standards/cartridges/CARTRIDGE-FORMAT.adoc` — prose spec - `standards/cartridges/cartridge-v1.json` — JSON Schema (canonical URL `https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json`) - `standards/cartridges/0.2-AI-MANIFEST.a2ml` — directory manifest - `standards/docs/decisions/ADR-002-cartridge-format-canonical-home.adoc` — decision record ## Key schema changes vs prior boj-server schema - Drops the `-mcp`-only name restriction. Cartridges may end in any canonical role suffix: `(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)`. - Adds required `category` (domain | cross-cutting | template). - Adds optional `states` (per panll v0.3.0) and `source` (registry + path + sha256). - Absorbs `panll/src/abi/cartridge-schema.json` v0.3.0's broader protocol set. ## Companion / follow-up PRs (this session) - boj-server: replace its local schema with SHA-pinned mirror + add `category` field to 125 cartridges (mechanical sweep) - Create `hyperpolymath/boj-server-cartridges` repo + populate with copies (PR 2 in plan) - Cartridge minter tool (PR 3) - Catalog refactor for on-demand fetch (PR 4) - boj-server: remove `cartridges/` dir (PR 5) - polystack 12 LSP halves → new cartridges (PR 6) - fleet-mcp redesign from poly-orchestrator-lsp (PR 7) ## Test plan - [x] Schema is self-consistent JSON - [x] All commit GPG-signed - [ ] CI green - [ ] No code paths consume this spec yet; additive only 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0ec0b83 commit 7c2b815

4 files changed

Lines changed: 421 additions & 0 deletions

File tree

cartridges/0.2-AI-MANIFEST.a2ml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-License-Identifier: MPL-2.0
2+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath)
3+
a2ml_version: "0.2"
4+
manifest_kind: directory
5+
name: standards/cartridges
6+
purpose: |
7+
Canonical specification of the BoJ cartridge format.
8+
Hosts (boj-server, panll, others) consume this spec; cartridge registries
9+
(canonically hyperpolymath/boj-server-cartridges) produce manifests conforming
10+
to cartridge-v1.json.
11+
contents:
12+
- file: CARTRIDGE-FORMAT.adoc
13+
purpose: Prose specification (this document is its companion machine-readable form).
14+
- file: cartridge-v1.json
15+
purpose: JSON Schema for cartridge.json manifests. Canonical URL.
16+
schema_url: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json
17+
- file: 0.2-AI-MANIFEST.a2ml
18+
purpose: This file.
19+
mirrors:
20+
- location: boj-server/schemas/cartridge-v1.json
21+
relationship: SHA-pinned mirror (vendored copy with explicit pinning in boj-server's manifest).
22+
- location: panll/src/abi/cartridge-schema.json
23+
relationship: Predecessor (v0.3.0 multi-protocol fleet manifest). Absorbed into v1 single-cartridge form.
24+
related_adrs:
25+
- ADR-002-cartridge-format-canonical-home

cartridges/CARTRIDGE-FORMAT.adoc

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath)
3+
= BoJ Cartridge Format — Canonical Specification
4+
Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
5+
:toc: macro
6+
:icons: font
7+
8+
toc::[]
9+
10+
== Status
11+
12+
*Stable v1* — supersedes the locally-held schemas at:
13+
14+
* `boj-server/schemas/cartridge-v1.json` (MCP-only subset; now a SHA-pinned mirror of this file)
15+
* `panll/src/abi/cartridge-schema.json` v0.3.0 (multi-protocol fleet manifest; absorbed)
16+
17+
See link:../docs/decisions/ADR-002-cartridge-format-canonical-home.adoc[ADR-002] for the
18+
canonicalisation rationale.
19+
20+
== What is a cartridge?
21+
22+
A *cartridge* is a self-contained server unit consumed by a host (typically `boj-server`)
23+
to extend its tool surface, language-server reach, debug-adapter capabilities, build-tool
24+
integration, or other server-role mode. Cartridges are *process-isolated* (each backend
25+
listens on its own loopback port) and *content-addressable* (each manifest may include
26+
an optional `source.sha256` for fetched cartridges).
27+
28+
A cartridge directory at rest looks like:
29+
30+
----
31+
<cartridge-name>/
32+
├── cartridge.json ← manifest (this spec)
33+
├── mod.js ← host entry point (calls loopback backend)
34+
├── abi/ ← Idris2 ABI definitions (REQUIRED for Teranga/Shield tier)
35+
├── ffi/ ← Zig FFI implementation
36+
├── adapter/ ← V-lang or Deno adapter (the running server)
37+
├── panels/ ← optional UI panels
38+
├── schemas/ ← cartridge-internal JSON schemas
39+
└── README.adoc
40+
----
41+
42+
== Naming and role suffix
43+
44+
Cartridge names *must* end in one of the canonical role suffixes:
45+
46+
[cols="1,3", options="header"]
47+
|===
48+
| Suffix | Role
49+
50+
| `-mcp` | Model Context Protocol server (default for tool surfaces)
51+
| `-lsp` | Language Server Protocol
52+
| `-dap` | Debug Adapter Protocol
53+
| `-bsp` | Build Server Protocol
54+
| `-debug` | Debugger / debug-only operations (when not strictly DAP)
55+
| `-format` | Code formatter
56+
| `-lint` | Linter / static analyser
57+
| `-build` | Build orchestration
58+
| `-nesy` | Neurosymbolic reasoning surface
59+
| `-agentic` | Agent harness (OODA-style state machine)
60+
| `-fleet` | Fleet orchestrator / multi-cartridge coordinator
61+
|===
62+
63+
Regex: `^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$`
64+
65+
A single domain may have multiple cartridges across roles: e.g. `database-mcp`,
66+
`database-lsp`, `database-format` are three cartridges sharing the `database` domain.
67+
68+
== Taxonomy (directory layout in registries)
69+
70+
Cartridge registries (canonically `hyperpolymath/boj-server-cartridges`) lay out
71+
their contents as the *Hybrid taxonomy*:
72+
73+
----
74+
cartridges/
75+
├── domains/
76+
│ ├── cloud/
77+
│ │ ├── mcp.cartridge/ ← cloud-mcp
78+
│ │ ├── lsp.cartridge/ ← cloud-lsp
79+
│ │ └── format.cartridge/ ← cloud-format
80+
│ ├── database/
81+
│ │ ├── mcp.cartridge/
82+
│ │ ├── lsp.cartridge/
83+
│ │ ├── postgres-adapter/ ← collection: database/<provider>-adapter
84+
│ │ └── mongodb-adapter/
85+
│ └── orchestration/
86+
│ └── fleet-mcp/
87+
├── cross-cutting/
88+
│ ├── nesy/
89+
│ ├── agentic/
90+
│ └── debug-harness/
91+
└── templates/
92+
└── gossamer-mcp/ ← canonical scaffold for new cartridges
93+
----
94+
95+
The directory name `<role>.cartridge` is preferred for in-domain cartridges; the
96+
cartridge.json `name` field carries the role suffix explicitly
97+
(e.g. `"name": "cloud-mcp"`).
98+
99+
== Manifest fields
100+
101+
See link:cartridge-v1.json[`cartridge-v1.json`] for the machine-readable schema.
102+
103+
Required:
104+
105+
* `$schema` — must point to this canonical URL
106+
* `spdx`, `copyright` — licensing
107+
* `name` — role-suffix-terminated, kebab-case
108+
* `version` — semver (pre-release suffix permitted)
109+
* `description` — one-line human prose
110+
* `domain` — functional domain (cloud, database, git, ...)
111+
* `category` — `domain` | `cross-cutting` | `template`
112+
* `tier` — `Teranga` (core) | `Shield` (security-critical) | `Ayo` (community)
113+
* `protocols` — at least one of {MCP, LSP, DAP, BSP, REST, GraphQL, gRPC, NeSy,
114+
Agentic, Fleet, Format, Lint, Build, Debug}
115+
* `auth` — `{method, env_var, credential_source}`. `method` ∈ {none, api-key, oauth2, vault}
116+
* `api` — `{base_url, content_type}`. `base_url` form: `local://<name>` (canonical loopback) or `http://127.0.0.1:<port>` (explicit port)
117+
* `tools` — array of `{name, description, inputSchema}`
118+
119+
Optional:
120+
121+
* `ports` — `{allowed, denied}` port permission lists
122+
* `states` — state-machine states (per panll v0.3.0 — useful for Agentic/BSP cartridges)
123+
* `source` — `{registry, path, sha256}` for cartridges loaded from a remote registry
124+
125+
== Trust tiers
126+
127+
* *Teranga* (core) — always available; ships with the host or fetched at first run.
128+
Requires Idris2 ABI + Zig FFI + formal correctness proof. Cannot be removed by
129+
user without reinstall.
130+
* *Shield* (security-critical) — handles credentials, audit trails, sandboxing
131+
boundaries. Same proof obligations as Teranga; additionally requires CVE-watch
132+
subscription + signed releases.
133+
* *Ayo* (community-contributed) — broader surface; formal proofs are encouraged
134+
but not mandatory for v1 admission. Subject to sandbox quarantine on first run.
135+
136+
== Registry and on-demand fetch
137+
138+
The canonical cartridge registry is the GitHub repository
139+
`hyperpolymath/boj-server-cartridges`. Hosts fetch cartridges from this registry
140+
on demand into a host-local cache (e.g. `~/.boj/cartridges/` for boj-server).
141+
The host catalog (e.g. `BojRest.Catalog`) loads cartridge.json from the cache
142+
at boot or on demand. See ADR-002 for the catalog-refresh contract.
143+
144+
Cartridges may also be fetched from other registries identified by GitHub URL.
145+
The `tray` UI exposes "Add cartridge source" with optional verification against
146+
`source.sha256`.
147+
148+
== Versioning
149+
150+
This document is v1. Backwards-incompatible changes will be published as
151+
`cartridge-v2.json` with a migration ADR. v1 manifests remain consumable
152+
indefinitely; hosts implementing v2 must accept both.

cartridges/cartridge-v1.json

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json",
4+
"title": "BoJ Cartridge Manifest (canonical)",
5+
"description": "Canonical schema for BoJ cartridge manifests. Mirrors the schema previously held in boj-server/schemas/cartridge-v1.json, expanded to admit the full BoJ server-role taxonomy (MCP, LSP, DAP, BSP, Debug, Format, Lint, Build, NeSy, Agentic, Fleet) per panll/src/abi/cartridge-schema.json v0.3.0. SPDX-License-Identifier: MPL-2.0.",
6+
"type": "object",
7+
"properties": {
8+
"$schema": {
9+
"type": "string",
10+
"format": "uri",
11+
"description": "Schema URL. Canonical form: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json"
12+
},
13+
"spdx": {
14+
"type": "string",
15+
"description": "SPDX license identifier (e.g. MPL-2.0)"
16+
},
17+
"copyright": {
18+
"type": "string"
19+
},
20+
"name": {
21+
"type": "string",
22+
"description": "Lowercase kebab-case cartridge name with role suffix. The suffix indicates the server role this cartridge implements.",
23+
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$"
24+
},
25+
"version": {
26+
"type": "string",
27+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.-]+)?$",
28+
"description": "Semantic version. Pre-release suffix permitted."
29+
},
30+
"description": {
31+
"type": "string"
32+
},
33+
"domain": {
34+
"type": "string",
35+
"description": "Functional domain (e.g. cloud, database, git, k8s, observability, proof-verification, ssg, orchestration). For cross-cutting cartridges, use cross-cutting."
36+
},
37+
"category": {
38+
"type": "string",
39+
"enum": ["domain", "cross-cutting", "template"],
40+
"description": "Taxonomy category. Domain cartridges live under cartridges/domains/<domain>/. Cross-cutting under cartridges/cross-cutting/. Templates under cartridges/templates/."
41+
},
42+
"tier": {
43+
"type": "string",
44+
"enum": ["Teranga", "Shield", "Ayo"],
45+
"description": "Trust tier. Teranga = core/always-available. Shield = security-critical/elevated-trust. Ayo = community-contributed."
46+
},
47+
"protocols": {
48+
"type": "array",
49+
"description": "Server protocols this cartridge speaks. A cartridge may expose more than one protocol if its role naturally spans them (e.g. MCP+REST). The role suffix in 'name' indicates the PRIMARY protocol.",
50+
"items": {
51+
"type": "string",
52+
"enum": ["MCP", "LSP", "DAP", "BSP", "REST", "GraphQL", "gRPC", "NeSy", "Agentic", "Fleet", "Format", "Lint", "Build", "Debug"]
53+
},
54+
"minItems": 1
55+
},
56+
"auth": {
57+
"type": "object",
58+
"properties": {
59+
"method": {
60+
"type": "string",
61+
"enum": ["none", "api-key", "oauth2", "vault"]
62+
},
63+
"env_var": {
64+
"type": ["string", "null"]
65+
},
66+
"credential_source": {
67+
"type": ["string", "null"]
68+
}
69+
},
70+
"required": ["method", "env_var", "credential_source"]
71+
},
72+
"api": {
73+
"type": "object",
74+
"properties": {
75+
"base_url": {
76+
"type": "string",
77+
"description": "Loopback URL the cartridge backend listens on. Form: local://<cartridge-name> for the canonical loopback scheme, or http://127.0.0.1:<port> for explicit-port form."
78+
},
79+
"content_type": {
80+
"type": "string"
81+
}
82+
},
83+
"required": ["base_url", "content_type"]
84+
},
85+
"ports": {
86+
"type": "object",
87+
"description": "Port permissions for the cartridge runtime.",
88+
"properties": {
89+
"allowed": {
90+
"type": "array",
91+
"items": { "type": "integer", "minimum": 1, "maximum": 65535 }
92+
},
93+
"denied": {
94+
"type": "array",
95+
"items": { "type": "integer", "minimum": 1, "maximum": 65535 }
96+
}
97+
},
98+
"required": ["allowed", "denied"]
99+
},
100+
"tools": {
101+
"type": "array",
102+
"description": "Tool surface exposed to the host (MCP-style; LSP/DAP/BSP cartridges may declare their protocol-native operations here too, with inputSchema describing the expected payload).",
103+
"items": {
104+
"type": "object",
105+
"properties": {
106+
"name": { "type": "string" },
107+
"description": { "type": "string" },
108+
"inputSchema": { "type": "object" }
109+
},
110+
"required": ["name", "description", "inputSchema"]
111+
}
112+
},
113+
"states": {
114+
"type": "array",
115+
"description": "Optional state-machine states a cartridge backend transitions through (per panll v0.3.0). E.g. observe/orient/decide/act/halted for an Agentic cartridge.",
116+
"items": { "type": "string" }
117+
},
118+
"source": {
119+
"type": "object",
120+
"description": "Where this cartridge is fetched from when not bundled locally.",
121+
"properties": {
122+
"registry": {
123+
"type": "string",
124+
"description": "Registry identifier. Canonical default: hyperpolymath/boj-server-cartridges"
125+
},
126+
"path": {
127+
"type": "string",
128+
"description": "Path within the registry, e.g. cartridges/domains/cloud/mcp.cartridge"
129+
},
130+
"sha256": {
131+
"type": "string",
132+
"pattern": "^[a-f0-9]{64}$",
133+
"description": "Optional content hash for verification of fetched cartridge artifact."
134+
}
135+
}
136+
}
137+
},
138+
"required": [
139+
"$schema",
140+
"spdx",
141+
"copyright",
142+
"name",
143+
"version",
144+
"description",
145+
"domain",
146+
"category",
147+
"tier",
148+
"protocols",
149+
"auth",
150+
"api",
151+
"tools"
152+
]
153+
}

0 commit comments

Comments
 (0)