xcb --json generate gives an application one model response per call from a
coding-agent subscription. xcb handles provider sign-in, the provider's
sandbox, and the provider process, and holds the account while the call runs.
The application sends a prompt of up to 1 MiB and receives untrusted text; its
own code decides which files, network requests, or messages that text can lead
to. TextButler is an example: it uses
separate classification and reply prompts and keeps contact memory, recipient
selection, review, and message delivery in its own code.
The application route is separate from xcb run. It creates no saved session,
reads no conversation history, and turns on no tools, hooks, plugins, judge,
continuation, or switching to another account. Private run records keep the
account, process, model, and timing without storing application prompts or
replies. Provider sign-in stays in xcb; applications never pass credentials.
Once you sign in an account, apps can use it with no extra command. The first
generate for an account and model checks it automatically: xcb sends one
fixed harmless prompt through the same no-tools path and checks the reply. Later
calls skip the check until xcb, the provider build, the app settings, or the
account's sign-in changes. An installation reports supported: false when no
provider build on this computer can serve apps, for example when xcb can't
confirm the provider's sandbox here. Do not substitute xcb run when
application generation is unavailable.
xcb --json generate --capabilitiesThis reads local account/model metadata without refreshing a provider, connecting
an account or making an inference call. It does not initialize a missing state
directory. Existing installations open their database read-only without initialization or
migration; SQLite may maintain its normal reader coordination sidecars.
Use only accounts with available: true. connected reports that local
credentials exist and look valid; it doesn't promise the provider still accepts
them. runtimeAdmitted reports whether xcb supports the provider build,
separately from the application checks. supported reports whether this build
has any provider that passed the application checks; a supported build can
still have no available account.
{
"version": 1,
"supported": false,
"zeroTools": true,
"zeroHooks": true,
"ephemeral": true,
"limits": {
"maxInputBytes": 1048576,
"maxOutputBytes": 262144,
"minTimeoutMs": 1000,
"maxTimeoutMs": 300000
},
"accounts": []
}Account rows contain id, label, provider, enabled, busy, connected,
runtimeAdmitted, available, reason, admission and models. Models
contain the exact public key, label, observedAtMs and admission. Keys
match xcb models and accounts match xcb accounts.
admission says how far a model has been checked for this exact xcb, provider
build, settings and sign-in:
| Value | Meaning |
|---|---|
pending |
Not checked yet. It is usable: the next generate checks it first, so that call takes longer (up to 60 seconds more). |
admitted |
The automatic check passed. |
qualified |
A strict manual qualification covers it (see below). |
The account-level admission is the best value among its models, or null
whenever the account is unavailable (reason says why). On an unavailable
account, a model's admission is null too unless a strict qualification
covers it, so pending never appears beside a reason. Models come from the
account's most recent provider catalog observation, however old it is: a
pending model needs no recent refresh, and once admitted or qualified a model
stays listed without further catalog refreshes. A model the provider has since
withdrawn fails its own request. models_unavailable means xcb has never
observed this account's catalog. Sign-in (xcb accounts login, accounts token, accounts import-codex and xcb setup) loads the catalog as its last
step; if that load failed, xcb accounts refresh ACCOUNT_ID repeats it in a
few seconds with a metadata probe, not a model turn. --capabilities itself
never refreshes a provider.
A reason is application_disabled (the owner turned app access off),
account_disabled, authentication_required, account_busy, not_connected,
runtime_unavailable, sandbox_unproven (xcb couldn't confirm the provider's
sandbox on this computer), admission_failed (the automatic check failed for
every listed model), models_unavailable (no catalog observed yet; see
above), or null when ready. busy is true
while the account has any unfinished run; account_busy means its runs reached
the configured max_runs_per_account limit, so the account cannot take another
task right now. Version 0.19 and earlier also reported
application_not_qualified; newer versions don't, so treat unknown reasons as
unavailable.
A manually qualified account additionally carries qualification with runtimeVersion,
runtimeDigest, evidenceDigest and expiresAt, which is always null:
qualification has no time limit. runtimeDigest identifies the exact xcb
executable. The separately reviewed evidence binds its provider pin, isolation
controls and live application tests. An application must reject missing or
mismatched evidence; neither
account sign-in nor caller JSON can issue it.
Start the xcb executable directly, write one UTF-8 JSON document to stdin, close stdin, and read stdout:
{"version":1,"account":"a_selected_account","model":"claude/observed-model/observed-effort","prompt":"Return the requested application response.","timeoutMs":60000,"maxOutputBytes":65536}Use xcb --json generate. All six fields are required and additional fields
are rejected. The entire input is limited to 1 MiB, including JSON framing.
timeoutMs is 1,000–300,000 and maxOutputBytes is 1–262,144. Prompts must be
nonempty UTF-8 without NUL. Account selection and the full observed model key
are exact; no implicit default or fallback is used.
Success is one JSON object and exit code zero:
{"version":1,"status":"completed","requestId":"application_generated_id","account":"a_selected_account","model":"claude/observed-model/observed-effort","text":"application response","outcome":{"terminal":"completed","joined":true,"effects":"none"}}xcb reports success only after the provider's processes, protocol connection,
and network bridge have exited, any refreshed credentials are saved, and the
account is released. effects: none means no application tools or actions ran;
xcb's own sign-in and account bookkeeping still happen. Validate text against your application's
own schema before using it. The provider is not claimed to enforce arbitrary JSON
schemas.
Failures use a nonzero exit code and a fixed-shape object with version: 1,
status: failed, code, and requestId when known. Codes are invalid_request,
unavailable, busy, deadline, cancelled, provider_error, output_limit,
and custody_unproven (xcb couldn't confirm the provider stopped, so it keeps
the account held). joined: true and effects: none appear only when xcb
confirmed them. Failure responses never include generated text,
provider payloads, stderr, credentials or private paths.
For an execution failure, the host may keep one small private diagnostic per account. Inspect it using the exact account and application request ID:
xcb --json application-diagnostic --account ACCOUNT_ID --request application_REQUEST_IDThis command is read-only: it does not initialize state, refresh an account,
read credentials or launch a provider. An absent, older, replaced or unreadable
record returns unavailable; it does not reconstruct details from past runs.
The existing version-one generate and qualification failure objects are
unchanged.
The diagnostic contains request/run/account identities, provider, timestamp,
a closed execution stage and category, and optional closed RPC operation,
numeric RPC code and protocol-check reason. For example, a resource
refusal can retain receive, quota_or_resource_limit, session_prompt and
-32011; a model-selection mismatch retains initialize, protocol and
model_changed. Unknown protocol checks become other. No original error
string, prompt, reply, provider payload, stderr, credentials or path is stored.
Writing a diagnostic replaces only that account's previous one, with a 4 KiB limit and owner-only file permissions. It is best effort: a diagnostic I/O failure never changes the execution result or weakens cleanup. Preparation, initialization, prompt start, response decoding and rejected output can produce records. Pre-admission refusals, cancellation, deadlines, and output-size limits need not produce one. A diagnostic doesn't prove that the process stopped, the account was released, the checks passed, or anything about cost or entitlement; use the command's outcome fields and the normal application checks.
Send SIGINT or SIGTERM and wait for the command to finish its cleanup. After the deadline, xcb keeps waiting for the provider's processes to exit and saves credentials before releasing the account, so cleanup can outlast the deadline. Killing xcb or seeing its main process exit doesn't prove that a provider has stopped. When the outcome is uncertain, xcb keeps the account held and blocks new work on it; don't delete its records or blindly retry.
Applications own their own durable request records, privacy controls, output validation and external effects. TextButler's recipient-bound grants, final takeover checks and send journal remain necessary even when xcb has successfully generated a response. xcb never sends messages for the application.
The first generate for an account and model runs the check before your
request:
- xcb confirms the provider's sandbox works on this computer. On macOS it runs
a short local test of the provider's sandbox profile; on Linux it uses the
receipt from
xcb doctor --qualify-sandbox. This needs no account and runs once per xcb build and provider. If it fails, apps can't use that provider here (sandbox_unproven), and xcb tries again 15 minutes later. - xcb holds the account and sends one fixed harmless prompt through the same
no-tools path
generateuses, then checks the reply exactly. The provider's processes must exit and credentials must be saved before the result counts. - xcb records the result, then serves your request. Your
timeoutMsstarts after the check, which has its own 60-second limit.
Only one check runs per account at a time. Other calls for that account wait
for it, then return busy if it's still running. If the model answers with the
wrong text, or more than the check allows, the model stays unavailable
(admission_failed, and generate returns unavailable) for 15 minutes or
until something it covers changes. A provider error (such as a rate limit), a
deadline, cancellation or local failure records nothing, so the next call checks
again. Each xcb build keeps its own results, so two xcb executables sharing one
state folder don't undo each other's checks.
A check covers one account and model for the exact xcb executable, provider
build, platform, application policy and configuration, and the account's
sign-in. When any of these changes, for example after an xcb or provider
update, the next generate checks again automatically. The record holds those
identities, the result and a time. It never holds a prompt, the check's reply,
or your text.
Apps can use every signed-in account until you turn access off:
xcb application disable # every account
xcb application disable --account ID # one account
xcb application enable [--account ID]
xcb application statusWhile access is off, --capabilities reports application_disabled and
generate returns unavailable without starting a provider. This also stops
accounts that were already checked or qualified. If xcb can't read the setting,
it treats access as off.
The strict manual qualification remains available as a stronger record for an
exact deployment. It is optional: generate doesn't require it. It takes a
private directory of sandbox and source/test evidence collected on the host,
then runs the same fixed challenge:
xcb --json qualify-application --account <account-id> --model <full-model-key> --evidence /absolute/private/evidence-directoryThe evidence must match the current executable, provider, platform and effective
application settings. The command accepts no caller prompt, tools or availability
override. It saves a qualification only after the fixed response is correct,
the provider's processes and connection have exited, credentials are saved, and
it holds the account exclusively. Each run covers one model and replaces that
account's previous coverage; it doesn't add to other models' qualifications.
A qualified model reports admission: "qualified" and needs no automatic check.
A qualification has no time limit. It ends when anything it covers changes: the xcb executable, the provider build, the platform, the application policy or configuration, or the account's sign-in after an explicit credential replacement. Collection itself must finish within 24 hours of its first observation, and the model must have been seen in the last 24 hours when qualifying. The renewal helper remains available for requalifying a Claude account/model on macOS after such a change.
Earlier versions required the strict qualification before any app call. It re-proved, for every account and model, facts about the xcb build: that the workspace tests passed, that a frozen source build matched the running binary, and, for Codex and Devin, a separately produced provider-boundary receipt. Those are now proved once, where they belong:
- Build facts are proved with the build. A release binary comes from a
maincommit whose required checks ran the workspace tests, and carries a build-provenance attestation bound to its digest. A build from source carries only what its builder checked; xcb doesn't verify that attestation at run time, so the runtime controls below are what protect every build. Provider builds run only when xcb's source or its reviewed catalog names their exact digest, and that admission already checks tools, configuration isolation and file access. - Host facts are proved automatically. The sandbox check above runs without credentials, once per xcb build and provider. If it can't pass, apps can't use that provider; nothing falls back to an unsandboxed run.
- Account facts are proved automatically. The fixed challenge confirms the account, model and provider answer through the exact no-tools path, and is repeated whenever anything it covers changes.
What protects you on every call is unchanged and enforced at run time, not by a
record: no tools, hooks or plugins; the provider's sandbox and isolated
configuration; credentials that stay in xcb; exact provider-build checks before
launch; exclusive account custody with custody_unproven holding the account
when xcb can't confirm the provider stopped; and failures that never include
payloads. The owner switch above turns access off at any time.
Discovery emits models only when they are qualified, admitted, or pending for that account. The complete response is limited to 128 accounts, 64 models per account, 1,024 models total, and 2 MiB. Qualified and admitted models count first; pending models fill the remaining room in catalog order, and any beyond it are left out until earlier ones are admitted. Otherwise an oversized inventory makes discovery fail rather than be silently truncated. These cardinalities also keep the closed version-one schema below 131,072 JSON tokens.
Trusted qualification tooling can read the executable's exact binding without launching a provider or changing local state:
xcb --json qualify-application --inspect --account ACCOUNT_ID --model claude/sonnet/lowInspection returns the xcb and provider versions and SHA-256 digests, OS, architecture, effective policy/configuration digests, and selected account/model. It doesn't pass the checks or extend a qualification.