# Models — the lane table

xMesh runs work on **lanes**. A lane is where a mind's traffic goes and whose account pays: an
adapter, a transport, optionally an endpoint and a model, the *name* of a credential, a billing
pool, and the capabilities its operator declares. Every xagent reads the table at hand-over and
decides for itself what the task needs; the runtime never infers a capability from a model name
and never routes a task to a lane on the operator's behalf.

## A lane

```jsonc
{
  "qwen":  { "protocol": "openai",    "transport": "api", "baseUrl": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
             "keyFile": "/run/secrets/dashscope", "model": "qwen-max", "pool": "alibaba",
             "capabilities": ["text", "code"] },
  "glm":   { "protocol": "anthropic", "transport": "cli", "baseUrl": "https://api.z.ai/api/anthropic",
             "keyFile": "/run/secrets/zai", "pool": "zai-sub" },
  "flux":  { "protocol": "openai",    "transport": "api", "baseUrl": "https://x.example/v1",
             "keyFile": "/run/secrets/flux", "model": "flux-1.1", "pool": "flux", "capabilities": ["t2i"] }
}
```

| field | meaning |
|---|---|
| `protocol` | the API the endpoint speaks: `openai` or `anthropic`, whoever hosts it — DashScope's compatible mode, Z.ai, Ollama and most gateways speak one of these. Any model behind either shape works. |
| `mind` | the adapter that speaks that protocol: `claude` (anthropic) or `codex` (openai). Implied by `protocol`; give it only for a CLI lane where the CLI matters, and it must agree. |
| `transport` | `cli` spawns the vendor's CLI on the person's subscription; `api` calls the HTTP endpoint on a metered key. |
| `baseUrl`, `model` | the endpoint and the model name; absent means the adapter's default. |
| `keyFile` | the path of a file holding the credential on the host, read when the mind is spawned. Never the value, and never in the daemon's environment. |
| `credentialEnv` | deprecated: the **name** of an environment variable holding the credential. Still honoured for older deployments. |
| `pool` | the billing relationship. Lanes on one account share one pool, so a quota or credit wall on one pauses the others instead of hammering the same account. |
| `capabilities` | what the operator declares the lane can do, from `text`, `code`, `vision`, `audio`, `long-context`, `t2i`. Unknown names are dropped by name, never guessed. |

**Declared is not callable.** A lane's capabilities are what its operator declared. What a doer can
reach is narrower: on a CLI lane the doer is the adapter, so it has the adapter's own capabilities
and nothing more; on an API lane nothing can be called by a doer yet. The table, `lanes.md` and the
stand-down all use the callable set, so a host never accepts work it cannot perform. A capability
declared but not callable is a blocker for the requester that names the lane and says why.

The built-in adapters `claude` and `codex` are lanes too: `claude` declares `text, code, vision`,
`codex` declares `text, code`. Neither generates an image, so a fresh install has no `t2i` lane,
which is the truth.

## Who carries the secret

The host that runs the work. The table names a credential; the value lives only on the hosts that
hold it, in the daemon's environment or a file it can read. A subscription CLI lane carries no key
at all — the CLI's own login does. Station shows each lane as *usable* or *no credential* for the
host it is talking to. This is why the table can be shared freely and why only hosts holding the
credential volunteer for work that needs the lane.

A mind sees only the credential family its own CLI authenticates with. Every other lane's
credential and the runtime's own secrets are scrubbed from the environment a mind is spawned in.

## Two layers: CLI lanes are local, API lanes are shared

A CLI lane is a person's own login on their own machine. It means nothing on another host, so it
lives in this host's local table and is never pushed anywhere. An API lane is a metered endpoint a
team shares; it lives in the shared source when one is configured and is mirrored locally as
`lanes.shared.json`. Local wins on a name collision, and every row says which layer it came from
(`adapter`, `builtin`, `env`, `shared`, `store`). A source that tries to hand a host a CLI lane
is refused by name, and the relay refuses to store one.

## Where the table lives — you choose

| `XMESH_LANES_SOURCE` | the table is | writes go |
|---|---|---|
| unset | this host's `<state dir>/lanes.json` | to that file |
| a file path | a shared file: a synced folder, a network drive, a git checkout | to that file, `{ rev, updatedAt, updatedBy, lanes }` |
| an `http(s)` URL | any endpoint answering `GET` → `{ rev, lanes }` and `PUT { lanes }` → `{ rev }`, bearer `XMESH_LANES_SOURCE_TOKEN` | `PUT` to that URL |

Whatever the source, this deployment **mirrors** it into `lanes.shared.json` at boot and every
30 s, so workers and doers keep reading local files. The mirror is the durable copy: if the source
is absent (a 404, a missing file) and the mirror is not empty, the mirror is written there. If the
source is unreachable, the mirror stays in use and Station says so.

`sym-relay` 0.5.0 serves one such document per channel at `/v1/docs/lanes`, read and written with
the channel token a team already holds, API lanes only — one hosted option among any you run
yourself.

`XMESH_LANES` (a JSON map in the environment) still works as a boot-time layer beneath the saved
table; the saved table wins on a name collision.

## Editing

Station → **Models** lists every lane with its source (`adapter`, `builtin`, `env`, `store`),
whether its credential is present on this host, and lets a validator add, edit or remove saved
lanes. The API is `GET /api/lanes`, `PUT /api/lanes/:name`, `DELETE /api/lanes/:name`; the MCP
bridge exposes `mesh_lanes`. A write lands in the mirror first, then in the shared source; the
response says whether the shared table took it.

## What an xagent does with it

At every hand-over the worker writes `lanes.md` into the doer's workspace: the table, which
lanes are usable now, and the declared vocabulary. The brief tells the doer: decide yourself
whether the task needs a capability your lane lacks; if a usable lane declares it, say which; if
nothing declares it, stop with

```
BLOCKED: no lane declares <capability> | owner: requester | closes: add a lane with "capabilities":["<capability>"] in Station's Models panel or XMESH_LANES and set its credential
```

That line is a corrective request into the mission room (MMP error-handling extension): the fork
carries `needs: <capability>`, every receiver reads it against its own table, hosts that cannot
serve it stand down, a host that can takes it. If none can, the mission stays blocked with the
requester named as owner and the closes text saying what to set up. After the operator adds the
lane and presses Resume, the doer reads `resume-context.md` — why it stopped, what cleared it,
what was already done and left on disk — and continues from there.
