# XMesh

**XMesh is an enterprise agentic-mesh runtime. Give it a business task and it forms a
crew, runs the work, keeps the evidence, stops for an explicit ruling when required, and
carries approved learning into later missions.**

**Claude Code user? One line installs it, and nothing needs configuring.**
`npm install -g @sym-bot/xmesh`. Run `xmesh` in your project directory, reopen Claude Code
there, and it is ready. What it adds is a second mind for Claude Code itself: when Claude Code
hands a task to XMesh, the work runs on the same Claude login you already have — your
subscription, no second account, no API key — but not in another Claude Code window. It runs
the same Claude CLI headless, inside a harness XMesh supplies: a workspace, grounding context,
the mission brief and its checks, an independent critic, an evidence file and an audited
record. Claude Code gets the result back reviewed, with its evidence, and approved learning
from it carries into later missions.

XMesh is for work that is difficult to solve with one prompt, one agent, or a fixed
workflow. It adds the operating layer that a dependable multi-agent system needs:

- **Task-first execution.** Start with an outcome and success criteria, not an org chart of
  agents.
- **Receiver autonomy.** Agents decide for themselves whether a task or observation is
  relevant. There is no central component assigning cognition.
- **Seven dimensions of cognition.** CAT7 expresses Focus, Issue, Intent, Motivation,
  Commitment, Perspective, and Mood as the dimensions through which a node evaluates an
  observation.
- **Selective admission.** SVAF weighs those dimensions against receiver-local anchors and
  turns the evidence into one whole-block admission decision.
- **Sovereign memory.** Every participant keeps its own store and admission policy. There is
  no shared pool of private agent memory.
- **Semantic continuity.** Local MiniLM recall retrieves relevant prior cognition by meaning
  before a mission worker starts, so later work can build on what the mesh already learned.
- **Traceable work.** Mission progress, findings, checks, decisions, and lineage remain on
  the record.
- **Explicit rulings.** Consequential work can stop for an approval or ruling, recorded on the mission as a separate act from the work.
- **Learning by consequence.** Outcomes that survive review can influence later missions;
  convincing failures do not earn authority merely by sounding good.

XMesh runs on the open [Mesh Memory Protocol](https://meshcognition.org/spec/mmp). The
XMesh runtime and cognition core are proprietary SYM.BOT software. The protocol and the
agent-connection package are separate open components.

**On this page:** [Quick start](#quick-start) ·
[What you can hand the mesh](#what-you-can-hand-the-mesh) · [The MCP tool](#the-mcp-tool) ·
[HTTP API](#http-api-quick-start) · [The CLI](#the-operator-cli--xmesh-verb) ·
[How a mission runs](#how-a-mission-runs--from-request-to-verified-outcome) ·
[Product status](#product-status) · [Budgets](#budget-management) · [Docker](#docker) ·
[Licensing](#licensing)

## Quick start

You need Node.js 20 or later and Claude Code, signed in: missions run on its login.

1. Install:

   ```bash
   npm install -g @sym-bot/xmesh
   ```

2. Add XMesh to Claude Code once, for every project, then restart Claude Code:

   ```bash
   xmesh mcp install --user
   ```

3. Ask in normal language:

   > Run this task in XMesh: review this repository and identify the top three release risks.

XMesh starts itself the first time Claude Code calls it. No MCP syntax is required in your prompt.
Keep the mission id it returns: the agent reads the mission again with `mesh_mission {action: "read", id}`,
so always pass the id.

**Good to know**

- **Verification is on by default.** Work, review, research and assessment missions each get an
  independent critic, which may increase provider usage. `xmesh set critic off` (or
  `XMESH_MISSION_CRITIC=0`) turns it off.
- **Spend has a default cap** of 40 work units a UTC day. Health reports `chosen: false` until you
  set your own with `POST http://localhost:8787/api/v1/_/spend/choice` (a `workload`, USD or
  explicitly `unbounded` limit). It is deliberately NOT an MCP tool: the mesh never sets its own
  budget. See [Budget management](#budget-management).
- **Is it running?** `curl -s http://localhost:8787/api/v1/_/health` shows `"ready":`, `"grounded":`
  and `"verification":"on …"`. `grounded` means it is on a live mesh, as itself, so a fresh install's
  `ready:true, grounded:false` is an isolated deployment, not a broken one.
- **Changed the server?** Fully restart the MCP client: it reads the tool's schema only when it
  connects.

This one package installs both commands, `xmesh` and `xmesh-mcp`. Everything else is in the
[Developer quick start](https://xmesh.dev/cookbook/developer-quick-start), the canonical, maintained walkthrough:
one project only, Codex, settings, the critic, and models behind an API key. What XMesh is and does:
[xmesh.dev](https://xmesh.dev) and [Chapter I of the Guidebook](https://xmesh.dev/cookbook/chapter-1).

## What you can hand the mesh

Every request is one mission, of one kind, with its own id. Each kind takes the same request
shape — what you want, as CAT7, and a small `spec` of its own:

| Kind | What you get back | What it needs |
| --- | --- | --- |
| `work` (the default) | real work with proof: files, code, a document, graded by its checks and reviewed by a critic | what you want (`focus`) and how done is judged (`commitment`, with `CHECK:` clauses) |
| `question` | a cited answer from the mesh's memory: every claim names the agent and the CMB that grounded it | the question; `spec.wait` holds the call open for the answer (up to 120 s) |
| `review` | a second opinion on one file or saved diff | `spec.target`, a path on the runtime's host |
| `research` | a sourced research document in the mission's workspace | the question |
| `assessment` | an integration assessment for an engagement (validator or above) | `spec.engagement` and `spec.inputsDir` |
| `task` | a card on the shared board; no mind is dispatched until someone starts it | the card's title |
| `simulation` | not served by this runtime yet: it answers `501 kind_unavailable`, and the capabilities say why | — |

A mission completes, or stops and says why: `waiting` and `blocked` name what it needs, and
`failed` carries its fault. A person's verdict (`validated` or `dismissed`) is recorded beside
the status, never instead of it.

## The MCP tool

A coding agent gets one tool, `mesh_mission`. Its `action` says what it does:

| Action | Use it to |
| --- | --- |
| `create` | hand the mesh a mission of any kind: `{action: "create", focus: "…", criteria: "CHECK: …"}`, with `kind` for anything but work |
| `read` | follow a mission by its id, or read one part of it (`part`: outputs, diff, artifact, root-cause) |
| `list` | list your missions, newest activity first, filtered by `kind`, `status` or `query` |
| `operate` | act on a mission: `op` is for example `{op: "verdict", verdict: "validated"}` or `{op: "replay"}` |
| `abandon` | stop a mission; files it already wrote stay where they are |
| `view` | read what the mesh holds: `view: "board"` (the shared work board), `"recall"` with `query` (search the mesh's memory along one CAT7 field), `"canon"` (validated knowledge that leads recall) or `"capabilities"` (which kinds of mission, minds and capabilities this deployment has now) |

Because it is one tool, a client that allows it without asking also lets it start missions.
If you want to approve each mission, leave the tool on "ask".

The tool's description and the server's own instructions carry the details an agent needs;
[`mcp/README.md`](mcp/README.md) lists the bridge's settings.

## HTTP API quick start

The runtime has one public resource, `/api/v1/mission`. Every tool, every CLI verb and every
client goes through it:

| Method | Path | Does |
| --- | --- | --- |
| `POST` | `/api/v1/mission` | hand the mesh a mission of any kind: one request, one mission |
| `GET` | `/api/v1/mission` | your missions (filter by `kind`, `status`, `q`; paged by `cursor`), or a view: `?view=capabilities`, `board`, `register`, `recall` or `canon` |
| `GET` | `/api/v1/mission/{id}` | one mission; `?part=outputs`, `diff`, `artifact` or `root-cause` for one part of it; `Accept: text/event-stream` for its live feed |
| `PATCH` | `/api/v1/mission/{id}` | operate on it: `{"op": "verdict"}`, `"resume"`, `"replay"`, `"apply"`, `"root-cause"`, a card's `"claim"` or `"start"`, … |
| `DELETE` | `/api/v1/mission/{id}` | abandon it |

The contract is [docs/api/mission-v1.md](docs/api/mission-v1.md), with the JSON Schema in
`schemas/v1/mission.schema.json`; both ship in the package. `GET /api` says the same from a
running server, without a session.

Create a session, then hand over a mission:

```bash
# Paste the passcode printed by XMesh.
curl -s http://localhost:8787/api/v1/_/session \
  -H 'content-type: application/json' \
  -d '{"passcode":"YOUR_PASSCODE","seat":"developer"}'

# Use the returned token.
curl -s http://localhost:8787/api/v1/mission \
  -H 'content-type: application/json' \
  -H 'authorization: Bearer YOUR_TOKEN' \
  -d '{
    "kind": "work",
    "cat7": {
      "focus": "Review the release candidate and write a go/no-go recommendation to /srv/work/go-no-go.md",
      "commitment": "CHECK: file /srv/work/go-no-go.md\nCHECK: contains /srv/work/go-no-go.md VERDICT:\nCite blocking evidence; a human rules on unresolved risk"
    },
    "spec": {}
  }'
```

The answer is the mission: `201` with its id and status (or `202` when it waits for capacity).
Follow it with `GET /api/v1/mission/{id}`; rule on it with
`PATCH /api/v1/mission/{id} {"op":"verdict","verdict":"validated"}`; stop it with
`DELETE /api/v1/mission/{id}`. `x-mesh-token: <token>` works wherever
`Authorization: Bearer <token>` does.

- **Retries are safe with a key.** Send `Idempotency-Key: <anything unique>` on a `POST` (or on
  an op that creates a child, like `replay`): the same key and body return the same mission,
  never a second one.
- **Every refusal has the same shape**: `{"v": "xmesh.error/1", "error": {"code", "message", "retryable", …}}`,
  with `details` naming each path that failed and a `hint` when there is something to do.
  A refused request creates nothing. On a fresh install work runs under the default spend cap
  (step 4); past the cap — default or chosen — a request that would commission work answers a
  structured `402` (`budget_exceeded`) whose hint names where a validator changes it.
- **The deployment's own routes** (session, health, readiness, version, the live events, the
  operator's reads and settings) live under `/api/v1/_/`. They are the runtime's plumbing for
  its own clients, not an API to build on; the CLI below covers the ones an operator needs.

## The operator CLI — `xmesh <verb>`

The same tenant the MCP bridge talks to (`XMESH_URL`, or the passcode under `XMESH_TEAM_ROOT` /
`XMESH_STATE_DIR` / `~/.xmesh`). Human tables by default, `--json` for scripts.

```
Hand the mesh a mission (one verb, any kind):
  xmesh mission "<what you want>" [--criteria "CHECK: …"]         work, the default
  xmesh mission "<question>" --kind question [--wait S]           a cited answer, printed when ready
  xmesh mission "<what to look at>" --kind review --target <path> a second opinion on a file or diff
  xmesh mission "<question>" --kind research                     a sourced research document
  xmesh mission "<card title>" --kind task [--column ready]       a card on the shared board
  add --key K to retry safely: the same key returns the same mission

Follow one, and act on it:
  xmesh mission <id>                      where it stands, its evidence, and what it waits on
  xmesh mission <id> --verdict validated|dismissed [--reason "…"]
  xmesh mission <id> --resume | --replay | --apply | --root-cause | --abandon
  xmesh mission <id> --part outputs|diff|artifact|root-cause
  xmesh missions [--kind K] [--status S] [--q text]

The deployment:
  xmesh status                            version, tree, restart owed, team
  xmesh eod [--run] · xmesh breaks        the end-of-day report, and the breaks awaiting a person
  xmesh budget <room> [<usd/day> | --clear]   a room's daily line and today's spend
  xmesh soak · xmesh proposals · xmesh workers
  xmesh deploy-check                      the environment, read before deploying it
  xmesh [--foreground]                    start it (in the background, from a terminal)
  xmesh update · xmesh restart · xmesh stop    the runtime's own lifecycle
  xmesh set <key> <value> · xmesh unset <key> · xmesh settings   settings kept between starts
  xmesh mcp install [--user] [--url U] [--force]   wire Claude Code: this project, or every project
```

`xmesh stop` and `xmesh restart` let in-flight missions finish first. On Windows, where no signal reaches the
runtime, the stop asks it over its own API; if it cannot ask, it says why and stops at once, and the next start
hands the claims of cut-off missions back and offers their work again. With no `XMESH_TEAM_ROOT` or `XMESH_STATE_DIR` set and nothing running for `~/.xmesh`,
`stop`, `restart` and `update` act on the runtime serving the port when its environment names its root, and say
which one: `… is served by pid N with XMESH_TEAM_ROOT=… — using that tenant`.

**What writes your project's `.mcp.json`.** Only two commands: `xmesh mcp install`, and plain `xmesh` when it starts
or joins the runtime from a project directory. Each prints the file it wrote, the bridge and the `XMESH_URL` it pins.
No other verb writes configuration, so `xmesh mission`, `xmesh status` and `xmesh help-cli` are safe to run inside
any repository. A `.mcp.json` that git tracks is never rewritten; `xmesh mcp install --force` rewrites it. The
pinned `XMESH_URL` is the runtime the CLI itself would reach from there (the live runtime of `XMESH_TEAM_ROOT` when
one is named). `XMESH_NO_MCP_SETUP=1` turns off plain `xmesh`'s wiring.

**On the network.** A runtime joins your machine's SYM mesh room as an observer, and announces
itself on the local network over mDNS (`_xmesh._tcp`), so other runtimes and apps on the same
LAN can find it. That holds with a separate `XMESH_TEAM_ROOT` too. To run with neither, start it
with `XMESH_NO_BRIDGE=1` (no mesh room) and `XMESH_ANNOUNCE=0` (no LAN announcement).

`xmesh help-cli` prints the full list, card operations included.

## How a mission runs — from request to verified outcome

A mission is a governed unit of work. XMesh records every offer, execution, review, decision and
replay in an append-only commission record, so a result stays traceable long after the run. Nothing
waits on a human mid-run; a human sees only what broke.

1. **Offer.** `POST /api/v1/mission` records the ask and its criteria as a signed offer, and the
   request goes out on the mesh carrying the mission's id. Name an existing room in `spec.room`
   and it is filed into that room instead; omit it and a mission room forms. A standing room whose
   memory fits it may host it; otherwise a fresh mission room forms with a doer and — if
   verification is on — a critic on a different model or vendor.
2. **Execute.** The doer works, reports progress, and completes with an artefact. The harness grades
   the criteria mechanically (see *Writing criteria*). The result is recorded at once: delivered,
   or — if it hit a capability it lacks — a root-cause record with a capability request. The doer's
   capacity is released; the critic is held a few minutes for its verdict, then released.
3. **Verify.** The critic reads the artefact and the doer's evidence log and either approves or
   objects, on the record. Adversarial review lenses may add their own verdicts.
4. **Reconcile.** `xmesh eod --run` (or the daily ticker, `XMESH_EOD_HOUR`, default 23) reconciles
   every recorded result against its independent review: **matched** (authority credited to the
   doer), **no-counterparty** (unverified — nobody could review it), or **break** (doer and critic
   disagree, or the checks failed). Only breaks reach you: `xmesh breaks` lists each with the
   mission that resolves it.
5. **Decide.** A break names its exits — validate (overrule the critic), dismiss (uphold it), or
   replay: `PATCH /api/v1/mission/{id}` with `{"op":"verdict","verdict":"validated"}` (or
   `"dismissed"`), or `{"op":"replay"}`. A record is never rewritten; a replay is a new mission, a
   child with lineage to the original. Stop an open mission with `DELETE /api/v1/mission/{id}`:
   nothing it does afterwards is booked, and what it already wrote stays.
6. **Investigate.** Every break has a root-cause report — the result, the objection, the harness it
   ran on, and the implicated resource: `GET /api/v1/mission/{id}?part=root-cause`; the
   `{"op":"root-cause"}` op asks the mesh for the causal hypothesis, as a child mission.

The runtime enforces capacity and deadlines. It never decides which cognition a participant
must accept — that receiver-autonomous boundary is what makes XMesh a mesh, not a router.

## Writing criteria — the check grammar

Criteria are prose plus mechanical `CHECK:` clauses. A clause the grammar cannot read is refused
with the request (`422 validation_failed`, naming it), never silently dropped:

```
CHECK: file <path>                              the artefact exists (fabricable — a touch passes it)
CHECK: contains <path> <needle>                 the artefact says something (substantive)
CHECK: not-contains <path> <needle>             it does NOT say something (a scaffold mark, a placeholder)
CHECK: contains-at-least <path> <N> <needle>    at least N occurrences
CHECK: run <command>                            behaviour — needs a shell the sandbox usually gates
```

Use absolute paths under the workspace and at least one substantive check; a mission graded on
`file` alone is written as a **break** at EOD ("delivered on file-existence checks only") because
nothing substantive grounded it, and a result whose own checks fail is a break too. A file that
declares itself in progress (`Status: IN PROGRESS`, `_pending_`) fails every substantive check
until the mark is gone.

## Verification — the critic

```bash
XMESH_MISSION_CRITIC=1                  # staff a critic on every mission
XMESH_MISSION_MIND_B=claude             # or codex (not on Windows) — a different vendor is the strongest independence
XMESH_MISSION_MODEL_B=claude-sonnet-5   # a different base model than the doer when the vendor is the same
XMESH_CRITIC_SETTLE_MS=300000           # how long a completed mission holds its critic for a verdict (default 5 min)
```

A second model is configuration. With neither variable set, and no room policy naming a critic mind or model
(`criticMind`, `criticModel` in `PUT /api/v1/_/rooms/<name>/policy`, validator+), the critic runs on the doer's own
mind and model: a separate run with its own stance, not a second model. `/api/v1/_/health` says which.

The critic is fail-closed: it never falls back to another model, and its silence is never read
as approval — no verdict means unverified (reported as `no-counterparty` by the API), and the
report says so.

## The cognition loop — admit, retain, recall

A Cognitive Memory Block expresses an observation across the seven CAT7 dimensions: **Focus,
Issue, Intent, Motivation, Commitment, Perspective, and Mood**. They are dimensions of
cognition, not merely transport fields: different nodes can weigh the same observation
differently while still exchanging one interoperable projection.

**SVAF is the admission membrane.** The receiver reconstructs semantic geometry from the
block's text, compares each dimension with its own anchors under its own weights, and turns
that dimensional evidence into one decision about the complete block: redundant, aligned,
guarded, or rejected. It never trusts an embedding supplied by the sender, and it does not
partially store whichever dimensions happened to pass. An admitted block becomes the
receiver's own remix, with lineage back to its source.

**Semantic recall is the continuity mechanism.** Once loaded, the same local
`Xenova/all-MiniLM-L6-v2` encoder supports a second job: XMesh searches durable cognition by
meaning across the CAT7 dimensions and builds the relevant context before a mission worker
starts. The MiniLM encoder selects prior context; it is not the Codex, Claude Code, or other
model that performs the mission. The separately trained SVAF research evaluator is not implied
by this baseline runtime path.

**Recall expands the cluster, not just the nearest row (0.9.22).** Ranked memories that belong
to your own past missions each bring a bounded set of same-mission siblings into the brief,
at the same brief size. Expanded rows are labeled `[via …]` instead of carrying a relevance
score they never earned, never cross a tenant boundary, and never displace ranked anchors
beyond a fixed byte share. The same join is on the query surface:
`GET /api/v1/mission?view=recall&q=…&mode=relevance&expand=1` returns an `expanded` count and
marks each joined row with a `via` naming the anchor that earned its place.

## Cost and the harness stamp

Every mission run records what it cost (input / cached / output tokens, and USD where the provider
states it) and the **harness it ran on** — content-addresses of the doer's brief template, the
constitution, the critic's rubric and the check grammar. `GET /api/v1/_/eod/harness` reads the
record grouped by harness: runs (`bookings`), matched, breaks, objections/approvals,
authority-credit events (`paid`), and cost per verified outcome (`usdPerMatched`). Change a
template or a rubric and the record shows the before and after as two groups.

The four texts can be overridden per deployment under `<state>/resources/`
(`constitution.md`, `rubric.md`, `template.claude.md`, `template.codex.md`; templates carry
`{{WORK}}` and `{{CRITERIA}}`). A valid override replaces the default whole and is hashed like
one; a constitution or rubric override may refine or add rules, never delete a default one; an
invalid override is ignored and logged once.

## Product status

**Current evaluation release: `0.11.0` (3 October 2026).** Install it from npm or run the
multi-platform Docker image. Its publication gate completed before anything shipped and the
published containers are the exact artifacts the gate scanned. The published evidence records
zero scan findings on both architectures and an empty exception list; that is a bounded scan
result, not a claim that the image is vulnerability-free. A plain `npm install` of this package
currently resolves two HIGH advisories in the ML dependency chain (`adm-zip` GHSA-xcpc-8h2w-3j85
and `sharp` GHSA-f88m-g3jw-g9cj, both via `@huggingface/transformers`, whose published
versions pin them); XMesh uses that chain only for local text embeddings; the container image removes `adm-zip`
after install and carries `sharp` as a tracked HIGH deliberately, because pruning it breaks
the runtime's neural entry point. The advisories clear for installers when the upstream pins move.

**Upgrading from 0.9.44 or earlier?** Read the security note first: in every published version
through 0.9.44 a mind spawned by the worker inherited the daemon's whole environment —
passcode, relay token, licence, vendor keys. 0.9.45 spawns every mind with an explicitly built
environment. Rotate any model API key that sat in the daemon's environment on a host where
missions ran, upgrade, then rotate the passcode and relay token. The full note is under
[Notices](#notices).

| Product surface | Status | What it means |
| --- | --- | --- |
| **Developer Runtime** | Available now | Compiled proprietary runtime from public npm. Includes the CLI and HTTP API for local development and evaluation. |
| **Coding-agent integration** | Available now | Codex, Claude Code, and other MCP clients can commission missions and collect results through the bundled `xmesh-mcp` bridge. |
| **Sovereign agent integration** | Available now | An MCP-capable agent can join as a mesh peer through [`@sym-bot/mesh-channel`](https://github.com/sym-bot/sym-mesh-channel) while keeping its tools, model, credentials, machine, and private store. |
| **Station** | Not in the npm package | The customer operations cockpit is planned for the paid Team Mesh deployment. |
| **Team Mesh** | Production rollout | One customer-controlled deployment for a production team, subject to the published security and operations gates. |
| **Enterprise Federation** | Shipped, licence-gated | Independently governed team meshes can expose and read approved decisions across an organization. The cross-organization endpoints return HTTP 402 without a production Team Mesh licence. (Distinct from the **spend 402**: a fresh install runs under a default cap of 40 work units per UTC day, and the spend 402 fires only when that cap — or one a person chose at `POST /api/v1/_/spend/choice` — binds. No licence involved.) |

The npm package is deliberately **API-only**. It does not contain the Station or any other
web interface. A fresh install prints `ui: none (API only)` at startup; a browser that opens
the port gets one page saying what the node is and where its API starts.

## Production network boundary

The server binds all interfaces by default and authenticates with a passcode exchanged for a
bearer token over plain HTTP — fine on a developer machine, not on a network. For anything
beyond localhost: set `XMESH_BIND_HOST=127.0.0.1` and terminate TLS in front (any reverse
proxy — nginx, Caddy, an ingress — forwarding to the loopback port),
and never expose raw :8787 on a public interface. The Docker examples above publish the port to the host only;
publishing it wider without TLS sends credentials and mission data in the clear.

On a cloud platform whose edge terminates TLS for you (e.g. RunPod's HTTPS proxy on an exposed
HTTP port), the transport half is the platform's; the credential half is yours, and two
settings make it explicit: `XMESH_REQUIRE_CREDENTIAL=1` refuses to boot unless `XMESH_PASSCODE`
was explicitly provided (a generated passcode printed to platform logs is not a chosen posture),
and `XMESH_TRUST_PROXY=1` lets the login throttle see real client addresses through
`X-Forwarded-For` — set it only behind a proxy you trust, never on a directly exposed server.
The login door itself is throttled (per-address and global sliding windows) whichever way it is
reached. For licensing a containerized deployment, set `XMESH_TEAM_ROOT` to a path unique to your
deployment (the licence binds to that path — see the Kubernetes manifest's comments), read
the `team-…` id from `/api/v1/_/version`, and point `XMESH_LICENSE` at your signed licence.

`XMESH_TRUST_PROXY` is a hop count — the number of proxies in front of the server — never a
switch. A non-numeric value (`true`) is refused by name at boot rather than ignored.

**Read the environment before deploying it.** The runtime refuses most mistakes at boot; a few
boot to a green `/api/v1/_/health` and fail later — a team root written with a trailing slash (a
licence minted against an id the runtime never binds), a `SYM_STATE_DIR` that points outside
the root, an `api:` lane with no key, a `cli:` lane whose CLI is not on the runtime's PATH.
`xmesh deploy-check` reads those from the environment it runs in, prints the `team-…` id the
runtime will bind *before* the first boot, and exits 1 on a fault:

```bash
# native install: the same environment the service will run with
XMESH_TEAM_ROOT=/srv/xmesh/acme XMESH_LLM_PROVIDER=cli:claude xmesh deploy-check

# container: run it inside the image, so PATH answers for the image, not the host
docker run --rm --entrypoint xmesh --env-file .env symbotdocker/xmesh:0.11.0 deploy-check
```

It never reads a secret's value: a credential is reported by length, a key file by whether it is
readable. `--json` for scripts.

## Operating discipline: reports, not just checks

XMesh deployments are operated largely by agents coordinating over messages, and a season of
audits taught one lesson worth stating here rather than in a postmortem: **every false claim
came from a report, not from a file.** A release version is not an installed state. A package's
create time is not a version's publish time. A file's presence is not a process's behaviour.
When you report state — to a human, a peer seat, or a ledger — carry three things in the same
sentence as the claim: the **instrument and its scope** ("`/api/v1/_/version` on loopback", not
"the board is on"), a **machine-printed clock** beside the reading (never typed from memory), and
the **object** you actually examined, since a claim about a file must not be written as a claim
about a running process. A clean result states what was checked. If the scope will not fit in
the sentence, the claim is bigger than the check.

## Two MCP integrations, two different jobs

| Integration | Direction | Use it when |
| --- | --- | --- |
| Bundled `xmesh-mcp` | Coding agent → XMesh runtime | Codex, Claude Code, or another MCP client should hand over missions, follow them, retrieve results, or ask for reviews. |
| [`@sym-bot/mesh-channel`](https://github.com/sym-bot/sym-mesh-channel) | Agent ↔ sovereign mesh peers | The agent itself should become a long-lived SYM participant with its own identity, admission policy, and private memory. |

A deployment may use both, but they are not substitutes. Most developers evaluating XMesh
from a coding agent should start with `xmesh-mcp` above.

## Join an existing agent as a sovereign peer

XMesh does not replace or wrap your agents. First an existing MCP-capable agent becomes a
**sym agent**: it receives a stable identity, a sovereign store, an admission membrane, and
mesh tools. It then earns a meaningful place by contributing to work. There is no central
registration step on purpose.

Example MCP configuration:

```json
{
  "mcpServers": {
    "sym-mesh": {
      "command": "npx",
      "args": ["-y", "@sym-bot/mesh-channel"],
      "env": {
        "SYM_NODE_NAME": "acme-reviewer",
        "SYM_ROOM": "acme-engineering"
      }
    }
  }
}
```

Give every long-lived agent a stable `SYM_NODE_NAME` and an explicit `SYM_ROOM`. A changing
name creates a new identity; a wrong room creates a healthy but isolated participant.

What crosses the mesh is a signed projection chosen by the sender. Each receiver evaluates
the complete projection under its own policy and stores only its own admitted remix with
lineage. The mesh does not remotely read or write another participant's private store.

See the [mesh-channel README](https://github.com/sym-bot/sym-mesh-channel) for host-specific
MCP setup and failure diagnosis.

## State and sovereignty

Native development uses two local trees:

- `~/.xmesh/` — sessions, passcode, mission and server state.
- `~/.sym/` — the sovereign stores and identities of local mesh participants.

For a customer or container deployment, name one tenant root:

```bash
XMESH_TEAM_ROOT=/srv/xmesh/acme xmesh
```

That produces:

```text
/srv/xmesh/acme/xmesh   server state
/srv/xmesh/acme/sym     participant stores
```

Back up the complete tenant root. Do not run two XMesh processes against the same writable
root. Scale by adding independently governed team roots, not by adding writers to one store.

If you run XMesh under macOS `launchd`, a **plist edit is not picked up by
`launchctl kickstart -k`** — that restarts the process on the already-loaded job, so the new
process keeps the old environment. After changing environment in the plist, `launchctl bootout`
then `launchctl bootstrap` the job, and confirm with `launchctl print` against the running job.
Use `kickstart -k` only for a code change with an unchanged plist.

## LLM providers

XMesh supports two credential models. Choose one explicitly for a controlled deployment:

| Use case | Provider |
| --- | --- |
| Individual development with an existing CLI login | `cli:claude` or `cli:codex` |
| Customer or enterprise deployment | `api:anthropic` or `api:openai` |

On Windows, codex can answer as the provider but cannot be a mission's worker or critic: its sandbox there refuses
every command. Use claude for work and review.

Example enterprise configuration:

```bash
XMESH_LLM_PROVIDER=api:openai \
XMESH_LLM_KEY_FILE=/run/secrets/llm-key \
XMESH_LLM_BASE_URL=https://ai-gateway.acme.internal/v1 \
XMESH_LLM_MODEL=gpt-5.6-luna \
xmesh
```

Prefer `XMESH_LLM_KEY_FILE` to an environment variable. The file is read for each call, so
the credential can be rotated without restarting the runtime. A trailing `/v1` on the base URL
is accepted and normalized — `https://gw.example` and `https://gw.example/v1` name the same
endpoint, never `/v1/v1`. When `XMESH_LLM_BASE_URL` is set, redirects to another origin are
refused before the prompt or credential is sent.

The declared enterprise-provider path is wired from version 0.6.1 onward. A declared but
unusable provider—or no declared provider and no detectable local Claude CLI—makes the
deployment not ready and refuses worker spawns instead of silently substituting a fake
response. Native development may still detect an authenticated local Claude CLI when no
provider is declared.

## Models — the lane table

Work runs on **lanes**: an adapter (`claude` or `codex` protocol), a transport (`cli` on a
subscription, `api` on a metered key), an endpoint, a model, the *name* of a credential, a pool,
and the capabilities the operator declares. Read them with `mesh_mission {action: "view", view: "capabilities"}` (or
`GET /api/v1/mission?view=capabilities`); edit them in Station → Models or through
`PUT /api/v1/_/lanes/:name`. Every xagent reads the table at hand-over and decides for itself
what a task needs. The table never carries a secret — the host that runs the work holds the
value — so it can live where you choose: this host, a shared file, or any `GET`/`PUT { rev, lanes }` URL
(`XMESH_LANES_SOURCE`; `sym-relay` serves one per channel). Details: [docs/models.md](docs/models.md).

## Docker

The published image is on Docker Hub and needs no credentials, no repository access, and no
build:

```bash
docker volume create xmesh-acme
docker run -d --name xmesh -p 8787:8787 \
  -v xmesh-acme:/state \
  symbotdocker/xmesh:0.11.0
```

`linux/amd64` and `linux/arm64`. Pin the version you intend to evaluate; `latest` exists and
moves. The image installs the same **pinned npm runtime artifact** you would get from
`npm i -g @sym-bot/xmesh@0.11.0` — not identical bytes, because the build prunes the browser
ONNX bundle and the native binaries for other platforms, neither of which can execute here.

Measured on a fresh volume with no environment at all, it answers in about two seconds:

```json
{"ok":true,"ready":false,"grounded":false}
```

**`ready:false` is correct there and is not a fault.** No provider is declared, so no mind
resolves and worker spawns are refused rather than silently echoed; `GET /api/v1/_/readiness`
says so in as many words. Such a container still serves the whole API, the CLI and the MCP
bridge — it can be inspected, configured and integrated — it just cannot yet *think*. Since
0.9.39 the image carries its worker mind (a pinned codex CLI, proved present at build); declare
the provider and hand it a key and `ready` becomes true. The image starts as root only to take
ownership of the volume paths and provably drops to an unprivileged user (uid 1000) before any
identity check, login, or server code runs; all state stays on the volume you mount.

Give it the provider, a key and a credential when you want it to do work rather than answer:

```bash
docker run -d --name xmesh -p 8787:8787 \
  -v xmesh-acme:/state \
  -e XMESH_LLM_PROVIDER=api:openai \
  -e OPENAI_API_KEY=<your key> \
  -e XMESH_PASSCODE=<credential> -e XMESH_REQUIRE_CREDENTIAL=1 \
  symbotdocker/xmesh:0.11.0
```

The entrypoint performs the supported stdin API-key login into an ephemeral `CODEX_HOME`,
verifies it, hands the same key to the runtime's own `api:openai` completion as a `0600` file
(`XMESH_LLM_KEY_FILE`, read per call — unless you named `XMESH_LLM_KEY_FILE` or
`XMESH_LLM_API_KEY` yourself), and unsets it before the server starts — the key never appears
on a command line and never sits in the long-lived process environment. On a persistent deployment, name
`XMESH_TEAM_ROOT` beneath your mounted volume and set `XMESH_VOLUME_MOUNT` to the mount: the
team root then owns all state, and the volume carries a deployment-identity marker so a later
pod naming a different team root refuses rather than quietly becoming a different deployment.

Building it yourself, from a source checkout, is the same artifact by a longer route:

```bash
docker build --build-arg XMESH_VERSION=0.11.0 -t xmesh:0.11.0 .
```

The build installs a **pinned** version from npm rather than copying the working tree — copying
would ship whatever was uncommitted. It also **fails** if the neural embedding path cannot load,
which is not decoration: the image published before this one was built on Alpine, and
`onnxruntime-node` ships glibc-linked bindings that cannot load under musl. It served correctly
and could not embed a single vector, silently, because the runtime falls back to lexical recall
by design. The base is now Debian slim and the build proves the path rather than assuming it.

One volume is one tenant. Do not attach two XMesh containers to the same volume.

A server-only container can be healthy but not ready: XMesh grounds work in participant
stores on its own filesystem. An empty `sym` tree gives it nothing to reason with. Check the
`ready` value, not only the HTTP status code. A container also has its own filesystem and
`/tmp`, so it cannot see peers or other deployments on its host unless you deliberately provide
the required state and networking.

The Kubernetes sample at [`deploy/kubernetes/xmesh.yaml`](deploy/kubernetes/xmesh.yaml) is a
**pre-1.0 evaluation sample, API-only**: one stateful replica and one persistent volume per
tenant, server image alone. Without a co-located agent it will not ground missions — it
answers every endpoint from an empty canvas. The released image digest, the agent sidecar,
and a real-cluster proof are 1.0 gates; production deployment starts with a Team Mesh licence.

## Budget management

XMesh never meters you for money — pricing is a flat team licence (below). Budget management
is the opposite direction: **limits you set on what XMesh may spend from your own provider
account**, all local, all changeable at any moment by your own validator, all auditable in
your own state files. Two layers and two cost levers:

**1. The deployment spend contract — a default you can replace.** A fresh deployment runs under
`workload`/40 with `chosen: false`; a person may replace it with one of three modes
(`POST /api/v1/_/spend/choice`, validator+). Only `unbounded` requires an explicit human
acceptance, and it is recorded with their name:

| mode | you set | what it enforces |
|---|---|---|
| `workload` | `dailyUnits` (default 40) | a hard cap on commissioned work units per UTC day — provider-independent; 40 sits above the heaviest measured real day (31) and far below a runaway loop |
| `usd` | `dailyUsd` | a daily dollar line, enforced as a hard cap where the provider reports cost; the workload cap backstops where it does not (CLI subscriptions report none) |
| `unbounded` | — | explicitly accepted unlimited provider spend — recorded in the ledger; health reports `protection: none` |

Over the line, every request that would commission work answers the same structured `402`
naming the state and the remedy. A refusal admits nothing: no request is announced, no worker
staffed, no unit consumed. State lives in your tenant (`spend-choice.json`, `spend-day.json`,
`spend-ledger.jsonl`); read it at `/api/v1/_/health` (protection class, public) and
`/api/v1/_/readiness` / `/api/v1/_/spend` (counts and remaining, authenticated).

**2. Per-room daily USD lines — optional, finer.** Any room can carry its own budget:
`xmesh budget <room> <usd/day>`, which writes `dailyBudgetUsd` into the room's policy
(`PUT /api/v1/_/rooms/<name>/policy`, validator+), or set `XMESH_ROOM_BUDGET_USD` to give every
room without its own line the same default. This layer counts **known** dollars (asks and fills
whose provider reported cost) per UTC day. Past the line, work or a question in that room answers
`402` with the numbers, and a validator may pass it anyway with an explicit `overBudget: true` in
its spec, which the mission keeps. `dailyBudgetUsd: 0` is a valid posture: every piece of work and
every question in that room needs a validator. Read any room's line and spend with
`xmesh budget <room>` (`GET /api/v1/_/rooms/<name>/budget`).

**3. Cost levers that are not budgets.** Verification runs a critic on every mission —
that is the product's own proof, and it roughly doubles per-mission provider usage;
cost-sensitive deployments opt out with `XMESH_MISSION_CRITIC=0`, and health then says so
(`verification: off (explicit opt-out)`) rather than letting unverified work look verified.
Concurrency is bounded by the mind governor (half your cores, minus one), and a single
mission is bounded in time by its 60-minute deadline — both cap how fast anything can burn,
neither is a spend limit.

None of this reports anywhere: no activation, no usage phone-home. The ledger is yours.

## Licensing

- **Developer Runtime:** free for local development, evaluation, and non-production use.
- **Team Mesh:** one flat annual licence per production team root, with unlimited human users
  and agents.
- **No metering:** pricing is not based on people, seats, agents, devices, IP addresses,
  sessions, tokens, or usage.

Licences are verified locally and entirely offline. XMesh does not need a usage-metering
service or activation call to operate.

**Starting a paid pilot (the offer):** mail **contact@sym.bot** with the subject
`Team Mesh pilot`, naming the team root you want to take to production. A pilot begins as a
private engagement: your production team root licensed for a year, unlimited humans and
agents in that mesh, direct support from the team that builds XMesh, and your launch findings
worked as first-class Board items. Pre-1.0 every production engagement is set up this way —
there is no self-serve checkout yet, and this page will say so when that changes.

**Licence v2 has shipped** (since 0.7.x) and implements the production-team contract above: a
licence names the team roots it covers, and a root's identity is derived from its canonical
path — so a licence validates only in the root it was issued for. In any other root it simply
does not validate, and that deployment runs as `EVALUATION (unlicensed)` — visibly, not
silently. The legacy `seats` cap survives **only** for existing v1 licences, honoured through
their expiry. It is not the pricing model and has not been since v2.

**Decision record (2026-08-25, pre-1.0 boundary):** the Developer Runtime is free for local
development and non-production evaluation; production use of a team mesh requires a Team Mesh
licence, arranged as a private pilot/contract. Pre-1.0 this boundary is **contractual** — the
runtime marks unlicensed roots as evaluation and does not technically refuse them. The
fail-closed enforcement gate is a named 1.0 item and will be documented when it can actually
refuse.

**The free allowance is a deployment, not a seat:** a home-directory install has no team root at
all and is the free Developer Runtime. Unlicensed named team roots remain available for
evaluation and are visibly marked `EVALUATION (unlicensed)` in operator status. A licence is
required when an independently governed team mesh moves into production; it still covers
unlimited humans and agents in that mesh.

See [xmesh.bot](https://xmesh.bot) for the Enterprise Pilot and Team Mesh paths.

## Current boundaries

- The public npm artifact contains the Developer Runtime CLI and API, not Station.
- The `simulation` kind is part of the contract but not served by this runtime yet; it answers
  `501 kind_unavailable`, and the capabilities say why.
- Existing agent integration is shipped through `@sym-bot/mesh-channel`; a general
  source-system connector SDK and catalogue are not yet a supported product surface.
- XMesh integrates cognition about business work. Transactional writes remain the job of
  the source systems and their existing controls.
- A server with no local participants can listen successfully while having no grounded
  capability. Use readiness, not presence, as the operational test.
- Cross-organization federation endpoints are shipped and licence-gated; they return HTTP 402
  without a production Team Mesh licence. Deployment topology, identity, and source-system
  controls remain the customer's responsibility.

## Develop

This is a private source repository. The published npm package contains compiled proprietary
software.

```bash
npm install
npm run dev       # the API on :8787, from source
npm test          # server suite; no paid APIs required
npm run typecheck
npm run build     # compiled runtime artifact
```

Releasing (`node scripts/release.mjs X.Y.Z`, after writing the CHANGELOG entry): the script gates,
bumps, tags, pushes and publishes in that order. After it, **know how each deployment is served**:
a tenant that runs from this checkout (`bin/xmesh.js` in its plist) deploys on restart — the
checkout is the deploy; a tenant that runs the global package (`…/bin/xmesh`) stays on the old
version until `npm i -g @sym-bot/xmesh@X.Y.Z` and a restart. One of three tenants on the founder's
host sat two minors behind for a week because nobody asked which kind it was.
`GET /api/v1/_/version` answers for each: `{version, tree, startedAt, generation, teamId}` —
`tree` is null for a package.

Useful references:

- [`docs/api/mission-v1.md`](docs/api/mission-v1.md) — the mission API contract.
- [`docs/agentic-mesh-reference.md`](docs/agentic-mesh-reference.md) — mechanism and status of
  the agentic mesh.
- [`docs/integration-reference.md`](docs/integration-reference.md) — shipped integration
  surfaces and claims that must remain future tense.
- [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) — system architecture.
- [`deploy/kubernetes/xmesh.yaml`](deploy/kubernetes/xmesh.yaml) — single-writer deployment
  shape (pre-1.0 evaluation sample, API-only).

## Support and security

Defects, questions, and security reports: **contact@sym.bot** with the subject
`XMesh defect report` (or `XMesh security report` — those are read first and not
discussed publicly before a fix ships). Product page: [xmesh.bot](https://xmesh.bot).
There is no public issue tracker yet; mail is the supported channel and every report
gets a reply.

## Notices

**Security note, 0.11.0 (unreleased).** Through 0.10.10 the runtime ran git as itself in repositories a doer can write
(a review target's, a deliverable's), and that repository's config could make git run the doer's programs inside the
runtime; a mission's `CHECK: run` commands also ran as the runtime, with its whole environment. 0.11.0 reads a doer's
repository from outside it: the runtime finds the repository and its commit itself, git runs in a private repository
of the runtime's with every config file switched off, and the operating system (macOS `sandbox-exec`, Linux Landlock)
bounds what that git, and every `CHECK: run` command, can read, write and start. Each command runs in a scratch copy of
its workspace, with no network and none of the runtime's environment. On a host with no enforcing sandbox the runtime
refuses rather than falls back: git facts are "not stated" and run checks are "not run", which is "not verified", never
a pass or a failure. On Linux the sandbox is a launcher of the runtime's own (Landlock ABI 3, Linux 6.2 or later): it
makes each rule only on the directory the runtime checked, opened with no symbolic link resolved, so a doer cannot race
a link or another directory into a rule path. A check's "no network" is enforced on macOS by the profile and on Linux by
a seccomp filter denying `socket(2)` for every address family, io_uring, and any `socketpair` but a unix stream pair,
since Landlock bounds only TCP. A check runs programs only from its scratch copy and the toolchain. What the
sandboxes do not cover, stated: a hardlinked object (a path-based boundary cannot see a hardlink, and refusing an
object with more than one link would break ordinary local clones, which hardlink by default — what a doer can hardlink
into its workspace it can already read through its own CLI's sandbox, so that sandbox is the boundary for its reads);
on macOS, a directory a doer renames into the place of one the runtime checked (one it can already write); on Linux
before 6.12, a signal a check sends to another process of the runtime's user; a child a check starts that leaves its
process group, closes every descriptor, clears its environment and changes out of its directory, which can outlive the
reap while staying inside the check's sandbox; and the per-check copy of the workspace (cloned on macOS, a real copy on
Linux, capped at 1 GiB and 200,000 files). The image's git is built from source, and Git 3.0 makes its Rust parts
mandatory, so the next git upgrade in the image needs cargo in the build stage. Details in the changelog.

**Security note, 0.9.45 (2026-09-04).** In every published version through 0.9.44 a mind spawned by
the worker inherited the daemon's whole environment — passcode, relay token, licence, vendor keys —
and any child of the mind that reads an environment could read it. 0.9.45 spawns every mind with an
explicitly built environment: the runtime's secrets, every other lane's credential and the file
paths that point at them are removed, and only the family its own CLI authenticates with is kept. Rotate any model API key that
sat in the daemon's environment on a host where missions ran, upgrade, then rotate the passcode and
relay token. Found by a mesh review of our own shipped source; no evidence of exploitation was
looked for and none is claimed. Details in the changelog.

**Erratum, 31 August 2026 — 0.9.31 release notes.** The notes bundled with 0.9.31 are headed
"A release can no longer half-ship." That sentence is false and the release it shipped in is what
disproved it: 0.9.31 reached npm while the container registry still served 0.9.30 as `latest`, and
the Kubernetes manifest published by that release pinned a `0.9.31` image that did not yet exist.
The images were promoted by hand about twenty-five minutes later and verified layer-for-layer
against the artifacts the gate scanned, which is the state described above. What the credential
preflight actually guarantees is narrower: publishing authentication is proven before any
irreversible step. The retraction is recorded here because the notes ship inside the package and
cannot be amended in place.

## License

Copyright © 2026 SYMBOT LTD, trading as SYM.BOT. All rights reserved. See [LICENSE](LICENSE)
and the [SYM.BOT Software License Agreement](https://sym.bot/license).
