Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions cartridges/0.2-AI-MANIFEST.a2ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# SPDX-License-Identifier: MPL-2.0
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath)
a2ml_version: "0.2"
manifest_kind: directory
name: standards/cartridges
purpose: |
Canonical specification of the BoJ cartridge format.
Hosts (boj-server, panll, others) consume this spec; cartridge registries
(canonically hyperpolymath/boj-server-cartridges) produce manifests conforming
to cartridge-v1.json.
contents:
- file: CARTRIDGE-FORMAT.adoc
purpose: Prose specification (this document is its companion machine-readable form).
- file: cartridge-v1.json
purpose: JSON Schema for cartridge.json manifests. Canonical URL.
schema_url: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json
- file: 0.2-AI-MANIFEST.a2ml
purpose: This file.
mirrors:
- location: boj-server/schemas/cartridge-v1.json
relationship: SHA-pinned mirror (vendored copy with explicit pinning in boj-server's manifest).
- location: panll/src/abi/cartridge-schema.json
relationship: Predecessor (v0.3.0 multi-protocol fleet manifest). Absorbed into v1 single-cartridge form.
related_adrs:
- ADR-002-cartridge-format-canonical-home
152 changes: 152 additions & 0 deletions cartridges/CARTRIDGE-FORMAT.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath)
= BoJ Cartridge Format — Canonical Specification
Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
:toc: macro
:icons: font

toc::[]

== Status

*Stable v1* — supersedes the locally-held schemas at:

* `boj-server/schemas/cartridge-v1.json` (MCP-only subset; now a SHA-pinned mirror of this file)
* `panll/src/abi/cartridge-schema.json` v0.3.0 (multi-protocol fleet manifest; absorbed)

See link:../docs/decisions/ADR-002-cartridge-format-canonical-home.adoc[ADR-002] for the
canonicalisation rationale.

== What is a cartridge?

A *cartridge* is a self-contained server unit consumed by a host (typically `boj-server`)
to extend its tool surface, language-server reach, debug-adapter capabilities, build-tool
integration, or other server-role mode. Cartridges are *process-isolated* (each backend
listens on its own loopback port) and *content-addressable* (each manifest may include
an optional `source.sha256` for fetched cartridges).

A cartridge directory at rest looks like:

----
<cartridge-name>/
├── cartridge.json ← manifest (this spec)
├── mod.js ← host entry point (calls loopback backend)
├── abi/ ← Idris2 ABI definitions (REQUIRED for Teranga/Shield tier)
├── ffi/ ← Zig FFI implementation
├── adapter/ ← V-lang or Deno adapter (the running server)
├── panels/ ← optional UI panels
├── schemas/ ← cartridge-internal JSON schemas
└── README.adoc
----

== Naming and role suffix

Cartridge names *must* end in one of the canonical role suffixes:

[cols="1,3", options="header"]
|===
| Suffix | Role

| `-mcp` | Model Context Protocol server (default for tool surfaces)
| `-lsp` | Language Server Protocol
| `-dap` | Debug Adapter Protocol
| `-bsp` | Build Server Protocol
| `-debug` | Debugger / debug-only operations (when not strictly DAP)
| `-format` | Code formatter
| `-lint` | Linter / static analyser
| `-build` | Build orchestration
| `-nesy` | Neurosymbolic reasoning surface
| `-agentic` | Agent harness (OODA-style state machine)
| `-fleet` | Fleet orchestrator / multi-cartridge coordinator
|===

Regex: `^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$`

A single domain may have multiple cartridges across roles: e.g. `database-mcp`,
`database-lsp`, `database-format` are three cartridges sharing the `database` domain.

== Taxonomy (directory layout in registries)

Cartridge registries (canonically `hyperpolymath/boj-server-cartridges`) lay out
their contents as the *Hybrid taxonomy*:

----
cartridges/
├── domains/
│ ├── cloud/
│ │ ├── mcp.cartridge/ ← cloud-mcp
│ │ ├── lsp.cartridge/ ← cloud-lsp
│ │ └── format.cartridge/ ← cloud-format
│ ├── database/
│ │ ├── mcp.cartridge/
│ │ ├── lsp.cartridge/
│ │ ├── postgres-adapter/ ← collection: database/<provider>-adapter
│ │ └── mongodb-adapter/
│ └── orchestration/
│ └── fleet-mcp/
├── cross-cutting/
│ ├── nesy/
│ ├── agentic/
│ └── debug-harness/
└── templates/
└── gossamer-mcp/ ← canonical scaffold for new cartridges
----

The directory name `<role>.cartridge` is preferred for in-domain cartridges; the
cartridge.json `name` field carries the role suffix explicitly
(e.g. `"name": "cloud-mcp"`).

== Manifest fields

See link:cartridge-v1.json[`cartridge-v1.json`] for the machine-readable schema.

Required:

* `$schema` — must point to this canonical URL
* `spdx`, `copyright` — licensing
* `name` — role-suffix-terminated, kebab-case
* `version` — semver (pre-release suffix permitted)
* `description` — one-line human prose
* `domain` — functional domain (cloud, database, git, ...)
* `category` — `domain` | `cross-cutting` | `template`
* `tier` — `Teranga` (core) | `Shield` (security-critical) | `Ayo` (community)
* `protocols` — at least one of {MCP, LSP, DAP, BSP, REST, GraphQL, gRPC, NeSy,
Agentic, Fleet, Format, Lint, Build, Debug}
* `auth` — `{method, env_var, credential_source}`. `method` ∈ {none, api-key, oauth2, vault}
* `api` — `{base_url, content_type}`. `base_url` form: `local://<name>` (canonical loopback) or `http://127.0.0.1:<port>` (explicit port)
* `tools` — array of `{name, description, inputSchema}`

Optional:

* `ports` — `{allowed, denied}` port permission lists
* `states` — state-machine states (per panll v0.3.0 — useful for Agentic/BSP cartridges)
* `source` — `{registry, path, sha256}` for cartridges loaded from a remote registry

== Trust tiers

* *Teranga* (core) — always available; ships with the host or fetched at first run.
Requires Idris2 ABI + Zig FFI + formal correctness proof. Cannot be removed by
user without reinstall.
* *Shield* (security-critical) — handles credentials, audit trails, sandboxing
boundaries. Same proof obligations as Teranga; additionally requires CVE-watch
subscription + signed releases.
* *Ayo* (community-contributed) — broader surface; formal proofs are encouraged
but not mandatory for v1 admission. Subject to sandbox quarantine on first run.

== Registry and on-demand fetch

The canonical cartridge registry is the GitHub repository
`hyperpolymath/boj-server-cartridges`. Hosts fetch cartridges from this registry
on demand into a host-local cache (e.g. `~/.boj/cartridges/` for boj-server).
The host catalog (e.g. `BojRest.Catalog`) loads cartridge.json from the cache
at boot or on demand. See ADR-002 for the catalog-refresh contract.

Cartridges may also be fetched from other registries identified by GitHub URL.
The `tray` UI exposes "Add cartridge source" with optional verification against
`source.sha256`.

== Versioning

This document is v1. Backwards-incompatible changes will be published as
`cartridge-v2.json` with a migration ADR. v1 manifests remain consumable
indefinitely; hosts implementing v2 must accept both.
153 changes: 153 additions & 0 deletions cartridges/cartridge-v1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json",
"title": "BoJ Cartridge Manifest (canonical)",
"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.",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"format": "uri",
"description": "Schema URL. Canonical form: https://hyperpolymath.dev/standards/cartridges/cartridge-v1.json"
},
"spdx": {
"type": "string",
"description": "SPDX license identifier (e.g. MPL-2.0)"
},
"copyright": {
"type": "string"
},
"name": {
"type": "string",
"description": "Lowercase kebab-case cartridge name with role suffix. The suffix indicates the server role this cartridge implements.",
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*-(mcp|lsp|dap|bsp|debug|format|lint|build|nesy|agentic|fleet)$"
},
"version": {
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[a-z0-9.-]+)?$",
"description": "Semantic version. Pre-release suffix permitted."
},
"description": {
"type": "string"
},
"domain": {
"type": "string",
"description": "Functional domain (e.g. cloud, database, git, k8s, observability, proof-verification, ssg, orchestration). For cross-cutting cartridges, use cross-cutting."
},
"category": {
"type": "string",
"enum": ["domain", "cross-cutting", "template"],
"description": "Taxonomy category. Domain cartridges live under cartridges/domains/<domain>/. Cross-cutting under cartridges/cross-cutting/. Templates under cartridges/templates/."
},
"tier": {
"type": "string",
"enum": ["Teranga", "Shield", "Ayo"],
"description": "Trust tier. Teranga = core/always-available. Shield = security-critical/elevated-trust. Ayo = community-contributed."
},
"protocols": {
"type": "array",
"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.",
"items": {
"type": "string",
"enum": ["MCP", "LSP", "DAP", "BSP", "REST", "GraphQL", "gRPC", "NeSy", "Agentic", "Fleet", "Format", "Lint", "Build", "Debug"]
},
"minItems": 1
},
"auth": {
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": ["none", "api-key", "oauth2", "vault"]
},
"env_var": {
"type": ["string", "null"]
},
"credential_source": {
"type": ["string", "null"]
}
},
"required": ["method", "env_var", "credential_source"]
},
"api": {
"type": "object",
"properties": {
"base_url": {
"type": "string",
"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."
},
"content_type": {
"type": "string"
}
},
"required": ["base_url", "content_type"]
},
"ports": {
"type": "object",
"description": "Port permissions for the cartridge runtime.",
"properties": {
"allowed": {
"type": "array",
"items": { "type": "integer", "minimum": 1, "maximum": 65535 }
},
"denied": {
"type": "array",
"items": { "type": "integer", "minimum": 1, "maximum": 65535 }
}
},
"required": ["allowed", "denied"]
},
"tools": {
"type": "array",
"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).",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"description": { "type": "string" },
"inputSchema": { "type": "object" }
},
"required": ["name", "description", "inputSchema"]
}
},
"states": {
"type": "array",
"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.",
"items": { "type": "string" }
},
"source": {
"type": "object",
"description": "Where this cartridge is fetched from when not bundled locally.",
"properties": {
"registry": {
"type": "string",
"description": "Registry identifier. Canonical default: hyperpolymath/boj-server-cartridges"
},
"path": {
"type": "string",
"description": "Path within the registry, e.g. cartridges/domains/cloud/mcp.cartridge"
},
"sha256": {
"type": "string",
"pattern": "^[a-f0-9]{64}$",
"description": "Optional content hash for verification of fetched cartridge artifact."
}
}
}
},
"required": [
"$schema",
"spdx",
"copyright",
"name",
"version",
"description",
"domain",
"category",
"tier",
"protocols",
"auth",
"api",
"tools"
]
}
Loading
Loading