Skip to main content
The xorcise command drives the same local instance as the web UI. Most commands are thin REST clients and need an instance running (xorcise up); the last column of the summary says which.

Find a command

Every command is also listed in full below, in the order the CLI groups them.

Command summary

xorcise agent delete, xorcise mission rm, and xorcise run rm are accepted aliases of agent rm, mission delete, and run delete.

Conventions

Global options

Running xorcise with no arguments, or a group with no subcommand such as xorcise agent, prints help and exits 0.

Names, ids, and prefixes

Run ids accept a unique prefix, so the 8-character id from xorcise run list works everywhere a run id is asked for. Agent arguments take the agent name. Mission arguments take the mission id or its display name — quote a name that contains spaces.

Output streams

Data and success messages go to stdout. Every error, the xorcise ui warning, and the xorcise mission pull progress bar go to stderr, so a script can separate them. A closed downstream pipe, as in xorcise run list | head, is not an error.

Confirmation prompts

Destructive commands prompt only on an interactive terminal. Both --yes and a non-interactive stdin skip the prompt, so nothing hangs in CI. The single exception is xorcise down --purge, which refuses with exit 2 rather than proceeding unconfirmed. xorcise mission prune has no prompt. Without --yes it only lists what it would remove. With --yes it checks again and removes what is unused at that moment, which can differ from a listing you ran earlier.

Exit codes

Exit 3 is not a failure. run status (without --json), run report, run terminate --wait, run regrade --wait, and mission pull all return it while the work continues on the server, so a CI gate that treats every non-zero code as failure will report a healthy in-progress run as broken. Branch on 3 explicitly.
Any unexpected exception is printed as a single line and exits 1; set XORCISE_DEBUG=1 for the traceback.

JSON output

Twenty-two commands accept --json: status, doctor, leaderboard, agent list, agent register, agent history, mission list, mission show, mission prune, run list, run create, run status, run traces, system, config show, config test, config test-terrain, catalog status, catalog connect, catalog disconnect, role show, and role list. JSON is written straight to stdout, never through the table renderer, so it is never wrapped, styled, or truncated. run status --json is always parseable, including before the result exists: the envelope carries status: "created", status: "xorcising" or status: "grading" for an unfinished run, and status: "not_graded" for a run that will never have a grade. With --json the command always exits 0, so branch on the status field; without --json it exits 3 for an unfinished or grading run and 0 for not_graded. Three commands emit machine-readable output that is not JSON. run prompt emits the prompt body verbatim, run launch-profile emits dotenv lines, and run events export writes JSONL.

CLI ↔ Web UI map

Start and stop

xorcise up

Starts XORCISE in the background and prints the interface URL. On a first run it creates ~/.xorcise, scaffolds config.toml, migrates a fresh database, provisions the local Headscale control plane, then polls the REST plane until it answers. Already running is success: it prints already running — UI at <url> and exits 0. If the database already holds data and is behind the current build it refuses to boot and asks you to stop any running XORCISE with xorcise down, run xorcise db upgrade, then start it again. On a start-up timeout it exits 1 and points at ~/.xorcise/serve.log, which is where boot failures are written. The local Headscale control plane runs headscale/headscale:v0.29.5, pinned by digest. If that image is not on the host and cannot be pulled, up exits 1 and tells you to docker pull it on a host that can, then docker save it and docker load it on this one. up also refuses, with exit 1, to replace a running Headscale that is newer than the pinned release, because its database cannot move back to an older one. When deployment_topology is distributed and host is a loopback address, up refuses with exit 1 before it writes anything, and tells you to set host in ~/.xorcise/config.toml to the address agents use. Once the server answers, up waits up to five seconds for the start-up reconcile and says what it did with the runs that were live when XORCISE last stopped: how many it picked back up, closed out as crashed (not graded), or cleaned up as unfinished deploys. An unfinished deploy whose teardown failed is listed by id, closed as deploy failed (not graded), and retried automatically. A whole reconcile step that failed prints boot reconcile failed with the error; it runs again on the next start. A clean start prints none of this, and a reconcile still running after five seconds says so and points at xorcise system. Whether XORCISE is already running is not decided by the pid file alone. When ~/.xorcise/xorcise.pid is missing, holds no valid pid, or names a process that has exited, up asks /api/system on each REST port an instance of this home could be using: the configured port, the port recorded in runtime-ports.json, and --port if you passed one. If an instance of this home answers, up adopts it instead of starting a second server on the same database. It rewrites the pid file and the port record from what the instance reports, prints already running (pid record repaired) — UI at <url>, and exits 0. An older server that does not report its pid, one from before v0.2.0, gets only its port record back, and up says the pid file could not be restored. An instance of another home on that port, or another program that answers with anything but a server error, counts as a busy port, and up moves past it. A port that accepts the connection but gives no answer within 8 seconds, drops the connection, or answers with a server error cannot be identified. up then refuses with exit 1 and names the port, rather than risk a second server on the same database. If it is a hung XORCISE, stop it with xorcise down or by hand; if it is another program, change the configured port with XORCISE_REST_PORT or rest_port in config.toml. --port alone does not get past it, because the configured port is still checked.

xorcise ui

Prints the interface URL on stdout, reading the port the running instance actually took rather than assuming the default. When nothing answers it still prints the URL and writes a warning to stderr. No options.

xorcise status

Probes three things from this machine — the REST plane, the OTLP plane, and the local Docker daemon — and derives an Interface row from the REST result. Exits 1 if any of them is down.
Output

xorcise doctor

Checks the host and reports the fix for each failure. The Environment section covers Python, the Docker CLI, the Docker daemon, the Docker Compose v2 plugin, free disk space, openssl, /dev/net/tun, the data directory, the mission base, the host platform, and nested containers. The Services section covers the plane ports and, when an instance is expected, the Headscale control plane. Each check is a blocker or a warning; warnings never fail the verdict, so the command exits 1 only when a blocker fails. Three of those checks decide whether missions can run at all: The nested-containers probe boots a throwaway privileged container, and pulls its image on a cold host, so it can take a minute with no output. doctor prints a line saying so before it starts — wait rather than interrupting it. It runs only under doctor, never under xorcise up, because a host that cannot nest can still serve the UI and run static missions. A probe that fails to run at all is reported as undetermined and is a warning, not a blocker: “the probe broke” is a different claim from “this host cannot nest”. Run creation reads the verdicts the running server holds, not doctor’s. So while XORCISE is running, doctor asks the server to probe again through POST /api/system/nested-support/refresh and prints the server’s verdicts: one nested containers line for your host’s own platform, and a nested containers (<platform>) line for each other platform a run has asked about, such as linux/amd64 on Apple Silicon. That clears a refusal the server cached before you fixed the host, without a restart. A foreign platform this host cannot nest, or a probe that could not decide, is a warning with its fix. The server’s probe can take several minutes on a cold host, and doctor waits up to 300 seconds for it. With XORCISE stopped, doctor probes in its own process instead. When an instance is expected, the control plane gets two checks, both blockers, and neither runs in stub mode. The first runs headscale version inside the headscale_container, the way run creation reaches Headscale, and prints run network ready (Headscale). The second, control plane address, sends a request to headscale_url, the address every run’s subnet router logs in to. Any HTTP reply passes, and the certificate is not verified. The two checks can disagree: xorcise up builds that URL from the host address it detects at the time, so if the address changes later, the container stays healthy while no router can reach it. A failure reads routers dial <url>, which is not answering. When the address up would detect today is different, the failure says this host's address is now <ip>, not <recorded ip>, and either way the fix it gives is xorcise down && xorcise up, which detects the address again and provisions the control plane on it. A headscale_url you pointed at a control plane you run yourself is probed the way up verifies it at boot instead, with no address comparison. See Troubleshooting.

xorcise down

Stops XORCISE and reaps any orphaned per-run mission containers, removing each by id together with its volume, reporting only what it removed, and carrying on past one that will not go. With no flags it clears transient runtime state and keeps your configuration and data. --keep-data with --purge is a usage error, exit 2. So is --purge on a non-interactive stdin without --yes. down sends SIGTERM to the process in ~/.xorcise/xorcise.pid, waits up to 8 seconds for it to exit, then sends SIGKILL and waits 2 seconds more. It then asks /api/system on the REST ports an instance of this home could be using, the configured port and the one recorded in runtime-ports.json, as xorcise up does, and stops any instance of this home still answering there by the pid it reports. That catches a server whose pid file was lost or overwritten, and prints stopped an orphaned instance of this home (pid <pid>) that was still serving port <port>. Last, it checks that the REST and OTLP ports are free. A port something else still holds is named in a note: line and is not a failure. down exits 1 rather than report a stop it could not make, and does so before it reaps containers or tears down Headscale:
  • A process it has to stop cannot be signalled from your user, or is still alive after SIGKILL. For the process in the pid file, the pid file is left in place and the error names the pid to kill as the user that owns it. If no instance of this home on its ports reports that pid, the pid was reused by an unrelated process: down drops the stale record with a note and carries on.
  • An instance of this home answers on one of its ports but does not report a usable pid, as a server from before v0.2.0 does. The error gives the ss -tlnp command that finds it.
  • A port accepts the connection but cannot be identified, as described under xorcise up.
Stop the process, then run xorcise down again.
xorcise down --purge deletes your judge-model configuration, every registered agent, every installed mission, and every recorded run and result. There is no undo. Run it before pip uninstall xorcise, though, or the Headscale container and its volume are left behind.

Instance and results

xorcise system

Asks the running instance what it sees about itself: its planes and their addresses, Docker, Headscale, the mission library, and registered remotes. Contrast xorcise status, which probes from your machine. Two lines appear only when there is something to act on. releases: counts finished runs that still hold their container or tailnet nodes because a teardown failed, with when the oldest ended and that run’s last error; XORCISE retries them automatically, usually waiting on Docker or Headscale to come back. The start-up reconcile’s outcome is shown when it recovered runs or failed, as on xorcise up, and with --verbose even on a clean start.

xorcise leaderboard

Ranks every agent by the median overall score of its finished runs, best first. An agent with no scored run has no median and comes last; ties go to the agent with more scored runs, then to the name. The table is the server’s own summary (GET /api/results/summary?by=agent), the same one the web UI’s Results page and dashboard read, so the CLI cannot rank the same runs differently. It needs a server of the same release. The median, Mean and Best are over the runs graded with a judge, a partly judged run counting at its lower bound. A run that timed out is in all three, at the score it earned: not finishing within the budget is the agent’s failure. Every other run is counted in its own column and never in the median: Runs still grading are in Runs only. A run that ended before its agent started, cancelled or never_started, is not counted anywhere, not even in Runs. With no finished runs it prints no finished runs yet — nothing to rank and exits 0.
Output
Scores print as whole percentages, rounded half up, exactly as the web UI shows them. Each footnote appears only when some agent has a run of that kind; a run whose grading failed adds:
With --json, each row is the agent’s summary as GET /api/results/summary returns it: agent_id, agent_name, runs, completed, scored, the counts timed_out, partial_judge, assisted, no_judge, stopped, grading_failed, infra_failed and grading, the scores median, q1, q3, mean, best, mean_deterministic, mean_judge and judge_gap, judge_models, and last_run. Each row also carries mission, mission_version and current_version, which are null for by=agent. Scores are fractions between 0 and 1, or null when the agent has no scored run. Each row also keeps the keys it carried before the median, computed by the rule above:

agent

xorcise agent list

Lists registered agents with their harness, version, and disclosed model. A generic agent shows as Generic, and a label of your own is title-cased.

xorcise agent register

Registers an agent as a named record at version 1. Connection details are optional — XORCISE never launches your agent, so the record exists to identify and version it. A duplicate name is caught before the request and exits 1 suggesting xorcise agent update. --kind is not cosmetic: it selects the replay adapter that turns traces into events, the telemetry provider that decides which variables run launch-cmd emits and how a run is correlated, and the launch provider that builds the harness command. An unrecognised --kind prints a warning that names the real harnesses and the closest one, then asks you to confirm:
Answering no exits 1 and saves nothing. Without a terminal and without --yes, the command exits 2 and saves nothing. The CLI checks --kind against the server’s harness list, and saves without asking if it cannot read that list. With --json, the saved agent carries the same sentence in warnings.

xorcise agent update

Re-declares an agent at a new version, keeping the same agent id and all of its runs. Only the fields you pass change; the rest are carried over. Passing no field to change is a usage error, exit 2.

xorcise agent rename

Renames an agent, keeping its id, version history, and runs. Both positionals are required. Use this rather than registering a second agent, which would start a fresh history. No options.

xorcise agent history

Lists that agent’s results oldest to newest, with the overall, deterministic, and judge scores, the agent and mission versions each was produced under, and the disclosed model. Partial results are marked. The model column is only the one you declared with --model, and reads not disclosed without it; the model the agent’s harness reported for each run is on xorcise run status.

xorcise agent rm

Removes the agent, its version history, and its runs with everything recorded for them — what run delete removes, for every run the agent has. Prompts on an interactive terminal. It is refused while any of the agent’s runs is still running or being graded. Each finished run whose environment is still up is torn down first, so the command waits up to 600 seconds for the server; if a teardown fails, the removal is refused unless --force. A refusal prints the server’s reason and the next step — xorcise run terminate <id> for a run that has not finished, or re-running with --force when that would help — and exits 1.

mission

xorcise mission list

Lists your own missions and, when the library is connected, the free XORCISE library. Installed missions sort first. A filter that matches nothing is an empty result, exit 0. The columns are Source, Id, Name, Difficulty, State, and Size. State is one of three values. Available means not installed. Installed means installed and current. Update available means installed, but the library now serves a different artifact for that mission — either the creator shipped a new mission version or the fleet was re-fused onto a newer base. xorcise mission update is the one action that clears it. Size is what the pull will download, and only ever for a mission you have not installed. It is a ceiling rather than a prediction: missions share base layers, so a pull that reuses layers already on disk transfers less. An installed row reads as a dash, because its bytes are already on disk, and a mission whose size the library does not carry reads as unknown rather than 0 B. --installed with --available is a usage error, exit 2. The accepted set is the built-in vocabulary plus every proficiency the live library actually carries, so it grows with the library. Words from the retired scale, such as intermediate and hard, are not in the built-in vocabulary: unless the live library carries one, it is a usage error, exit 2.

xorcise mission show

Shows one mission — your installed copy if you have it, otherwise the library entry. Accepts an id or a display name. The human view covers the environment kind, difficulty, specialty, the objective given to the agent, the description written for you, skills and technologies, and counts of artifacts, rubric criteria, checks, intel items, attachments, and attack-path nodes. It ends with the next command to run. A mission you have not installed also shows a Download line — the same ceiling mission list quotes, with the image and attachment split beside it when the total genuinely decomposes into both. An installed mission shows no size at all.

xorcise mission pull

Installs a library mission so it can run, downloading its container image and bundle. It always asks the registry for the image, even when one is already local, such as an image you loaded yourself with docker load, so an install fails if the registry cannot be reached. Docker downloads only the layers it does not already have. On a terminal it shows a live progress bar on stderr; elsewhere it prints one line per phase: resolving mission…, downloading container image…, downloading mission bundle…, installing…, done…. Already installed is a no-op, exit 0, unless you pass --refresh. Ctrl-C cancels the pull on the server and exits 130. Past a 30-minute wait the CLI exits 3 and the job keeps going. --refresh is a repair, for an installed mission whose local image is damaged. For a newer release, use xorcise mission update. A plain pull skips every layer already on disk, so it cannot replace a damaged one. --refresh removes the installed image first, never forced, then downloads the library’s current image and reinstalls. Layers the image shares with other images stay on disk, so damage in a shared layer survives the repair. It does not reuse the image your install recorded, so if the library has moved on, the repair also lands on its current release. It repairs library missions only and never replaces a mission you added yourself. On a mission that is not installed, it is a plain pull. Before it removes anything, --refresh checks the following, and stops with nothing removed if any check fails:
  • No other tag names the same image. Removing one of two tags would free no layers.
  • The registry answers for the image it will download, so an offline repair keeps the image you have.
  • No container, running or stopped, uses the image.
It does not check for a run that has been created but has no container yet. A repair at that moment can remove the image the run is about to start, and that run then fails to deploy. If the download fails or is cancelled after the removal, the mission stays installed without an image until a repair succeeds, and the message says so. Run --refresh again. If a pull of the same mission is already running, --refresh follows that pull instead of starting a repair, and warns that no refresh happened. Run it again once that pull finishes.
The library’s contents change, so run xorcise mission list first and pull an id you actually see rather than assuming a particular mission is available.

Which architecture gets pulled

The pull chooses the mission’s execution platform before any byte moves, so a mission this host cannot run is refused up front rather than after a multi-gigabyte download. In order:
  1. XORCISE_DOCKER_PLATFORM, if you set it. An explicit override wins unconditionally and pins every pull and run to it.
  2. Your host’s own platform, when the mission published and validated an image for it. An Apple Silicon or other ARM64 host runs a native linux/arm64 image.
  3. linux/amd64 under your host’s emulation layer, when the mission has no native image for you. The server logs Native ARM64 image unavailable for this mission. Running the AMD64 mission using compatibility/emulation mode. as a warning; the CLI does not print it. The installed mission’s emulated field in GET /api/missions and the emulated badge on its web UI card say it ran that way.
  4. Otherwise the pull is refused, naming what the mission does support: this mission supports <platforms>, and this host (<yours>) has no way to execute any of them.
An amd64 image under emulation still has to run nested, inside the run’s own container, and run creation checks that at linux/amd64 before it starts the run. An Apple Silicon Mac does it through Docker Desktop’s Rosetta, when Rosetta is on. An arm64 Linux host fails the check, with or without a qemu emulator installed, so the run is refused with this mission's image is linux/amd64, but this host executes linux/arm64 natively and cannot run linux/amd64 containers nested. xorcise mission pull does not run this check, and neither does the download that xorcise run create or the web UI’s Download & Start Run starts for a mission that is not installed, so the image downloads and installs before the run is refused. Only POST /api/runs, when it pulls the mission itself, checks before the download. The platform that actually landed is verified after the pull, recorded in the install, and carried into the run’s evidence and its report. xorcise doctor reports your host’s platform under the host platform check. A mission served by a library that predates this contract publishes no platform list. It behaves exactly as it did before, with Docker picking the platform.

xorcise mission update

Updates an installed library mission to the release the catalog currently serves, re-pulling it in place. Accepts an id or a display name. No options. Use it when xorcise mission list shows Update available for a mission. That one command covers both reasons the artifact can move — the creator shipping a new mission version, and the fleet being re-fused onto a newer base — because the user-facing operation is the same re-pull either way. The update is atomic: the install is replaced only once the new artifact is in place, so an interrupted update leaves the mission you already had. Layers shared with the previous release are already on disk, so an update usually moves far fewer bytes than the first pull. The previous release’s image stays on disk, where xorcise mission prune lists it as superseded. An install that already matches the catalog is an untouched no-op:
Output
Updating a mission that is not installed exits 1 and points at xorcise mission pull.
This replaces the delete-then-pull workaround. Do not xorcise mission delete a mission in order to get a newer copy of it — mission update does it in one step and without a window where the mission is not installed.

xorcise mission ingest

Coming soon. Adding your own mission from a local bundle is not available in this release. The command is a placeholder: every form of it prints the notice below, installs nothing, and exits 0. To get missions today, browse the free XORCISE library with xorcise mission list and install one with xorcise mission pull — no account, key, or sign-in.
Output
The BUNDLE_DIR positional is optional and is not read, so xorcise mission ingest, xorcise mission ingest ./sqli-login, and a path that does not exist all print the same notice. There are no options and no running instance is required. When the feature ships, a bundle will be a local directory containing a mission.json manifest — a directory, not an archive — which ingest will validate before building the mission’s image. Validation will belong to ingest and only to ingest; there is no separate validate command. The manifest format is documented now so you can prepare bundles in advance: see Mission manifest.

xorcise mission delete

Uninstalls an installed mission. Recorded runs and results are kept. Without --purge the mission’s image stays in your local Docker store, so re-installing is cheap, and xorcise mission prune removes it later. Deleting a mission that is not installed exits 1. With --purge the command also removes the mission’s image, under the same rules as mission prune. The image stays, with a note that says why, when another install, an unfinished run, or a container still uses it, or when it is not a XORCISE mission image. A static mission has no image, so there is nothing to remove. On an instance that does not manage Docker images, the control role or stub mode, --purge still uninstalls, removes nothing, and prints no image removed: this server does not manage Docker images …. If the uninstall succeeds but the image removal fails, the command prints uninstalled, but could not remove image <image>: <reason> and exits 1. The mission stays uninstalled.

xorcise mission prune

Frees disk space by removing the mission images nothing uses any more: the images of missions you uninstalled, and the previous images an update left behind. Without --yes it removes nothing. It lists each image with its size and a reason, prints would remove N images, reclaiming up to <size>, and exits 0. The reason is not installed when the image’s mission is no longer installed, and superseded when the image’s mission is still installed under a different image. When nothing qualifies, it prints no unused mission images to remove and exits 0. With --yes it checks again and removes every image that qualifies at that moment, which can include one a listing you ran earlier did not show. Removal is never forced. If Docker refuses to remove an image, for example because it is in use, prune names that image and the reason, removes the rest, and exits 1. Prune never removes these images:
  • The image of an installed mission.
  • Every image of an installed mission whose recorded image prune cannot find in the local store, in case the record spells it differently.
  • The image of a run that has not finished.
  • The image of any container, running or stopped.
  • The images of a mission with a pull job running, which is how mission pull, run create and the Web UI pull. A pull that runs without a job is not covered; see POST /api/missions/prune.
If an install record cannot be read, prune removes nothing and exits 1 with an error that names the file. Without that record it cannot tell which image the install needs. On an instance that does not manage Docker images, the control role or stub mode, prune also removes nothing and exits 1 with an error that says so, rather than report a clean disk. Prune touches only XORCISE mission images, and leaves every other Docker image alone. An image counts as a mission image when it was built from the XORCISE mission base, so it carries the base’s labels, and every one of its tags names a mission repository: xorcise/mission-<id> for a mission built locally, or <registry>/xorcise/[<env>/]mis-<id> for a library pull. An untagged image counts only when its ai.xorcise.mission.id label and every one of its registry digests name the same mission, and it has at least one of them. An untagged image that an earlier release fused locally has neither, so prune never claims it. Base images, and any image you re-tagged under your own name, are never removed. Sizes are an upper bound. Images built on the same base share layers, and a shared layer stays on disk while any image still uses it. Like run create, prune needs the instance to reach the Docker daemon. The --json report has six fields. applied is true with --yes. candidates lists each image prune found, with image_id, tags, slug, size_bytes, and reason. removed lists the ids of the images it removed. failed lists each image it could not remove, with image_id, tags, and error. skipped is always empty here, because the CLI does not confirm a list of image ids. reclaimable_bytes is the upper bound on the space the candidates hold.

run

xorcise run list

Lists runs newest first with a shortened run id, the humanised result, the agent, the harness the run was created under, the mission, the score, and how long ago the run was created, under Created. The short ids feed run status, run report, and run traces. The result is the server’s status label for the run, the same word the web UI shows: Created until its agent starts, Xorcising until the run ends, then Completed, Timed out, Stopped, Cancelled, Never started, Deploy failed, or Crashed. A run that ended cancelled, never_started, deploy_failed or crashed is never graded, so its score reads —. A score whose judge half did not run is marked with *: 0.50* is a run that passed every check with no judge. Whenever a marked score is on screen, a legend under the table says so: * the judge half did not run for this score — see xorcise run status <id>. The raw array from --json carries no scores, so read grade.judge_status from xorcise run status --json instead.

xorcise run create

Creates a run pairing one registered agent with one installed mission. Both inputs are resolved before the request, so a typo answers with the closest name and a mission you have not installed answers with the xorcise mission pull command rather than a 404.
Output

xorcise run status

Shows the graded result: the overall score and its deterministic and judge halves, the per-check breakdown, artifacts, trace counts, and the disclosed conditions the run was graded under — the agent’s model, the judge model, the budget, and the sandbox. A run graded on incomplete data is flagged partial. The model: line shows the model you declared with --model, marked (disclosed), or the model the agent’s harness reported in its telemetry, marked (reported by the harness). When you declared one and the telemetry named something else, it shows both, as in model: claude-opus-4 (disclosed); telemetry named gpt-5.5. With neither, it reads model: not disclosed. Conditions has the full rule, and --json carries the reported names as models_reported. When the judge half did not run, because judge_status is model-not-configured or unavailable, a JUDGE DEGRADED line under the scores names the status, followed by the reason from judge_detail when there is one. The line only discloses: the judge half still counts as 0.00 in overall. run terminate and run regrade print the same line when they wait for the result.
Output
When the judge scored only part of the rubric, a PARTIAL JUDGE line under the scores gives the share of rubric weight that was scored and notes that the shown ranges include unscored criteria. On a sealed run an evidence seal: line follows the scores. It carries the short digest and reads verified when the graded evidence is unchanged since sealing, or MISMATCH when it has changed. run status re-checks the seal against the evidence each time. Where the seal was not re-checked, as in an exported result.json, the line reads recorded. A seal that could not be hashed or read says so, and a run with no seal prints no line. Without --json, it exits 3 while the run is unfinished or still grading, so it polls cleanly. An unfinished run prints run <id> has not started — no result yet while it is Created, and run <id> is still xorcising — no result yet once its agent has started. A run that ended cancelled, never_started, deploy_failed or crashed has no result and never will. The command prints not graded, the run’s status, the reason, and any evidence recorded when the run ended, then exits 0, since this is final rather than in progress. --json emits the server’s not-graded envelope unchanged.
Output
When the run is finished but its environment is still up because a teardown failed, it prints environment not released after the result, with the attempt count and the last error. It stays quiet while the teardown is only waiting for grading to finish.
For a graded run it also prints a telemetry: line: the replay renderer, and how many spans and log records arrived and how many of each carry content. Each telemetry warning follows on a warning: line, for example when no span carried content and the judge graded from the artifacts alone. --json adds the same summary as a telemetry object.

xorcise run terminate

Stops an unfinished run early. The run is sealed immediately and graded on what happened so far; the result is marked partial with an operator trigger. A run still Created, whose agent has not started, is cancelled instead. The confirmation says (its agent has not started, so it will not be graded), the run ends cancelled, and the command prints Cancelled and the reason at once, without waiting for a grade. With --wait, the CLI polls for up to four minutes and then exits 3 if grading has not landed. Grading continues either way.

xorcise run regrade

Re-grades a finished run’s sealed evidence against the current settings. The agent is not re-run: the deterministic checks and the LLM judge re-evaluate the evidence already recorded for the run. Use it after fixing grading configuration — a judge token budget the transcript overflowed, or a rejected model key. The recorded result is replaced. With --wait, the CLI polls for up to four minutes and then exits 3 if grading has not landed. Grading continues either way. A run that ended cancelled, never_started, deploy_failed or crashed was never graded and cannot be re-graded: the server refuses, and the command prints its reason and exits 1.

xorcise run delete

Deletes a run and everything recorded for it: its result, everything the agent submitted, the raw traces and logs, the evidence seal, the event stream, the observed facts and terrain map, the run record, and the run’s ~/.xorcise/runs/<run_id>/ directory. An active run cannot be deleted — terminate it first. A run still being graded is refused too. A finished run whose environment is still up is torn down first, so the command waits up to 120 seconds for the server; if the teardown fails, the delete is refused unless --force. A refusal prints the server’s reason and the next step — xorcise run terminate <id>, or re-running with --force when that would help — and exits 1.

xorcise run export

Writes a set of finished runs into one directory tree, so exporting a mission’s, an agent’s or a date range’s worth of runs is one command rather than a loop over run ids. The filters combine; with none, every finished run is exported. Each selected run becomes <out>/<id8>/, holding four files: --mission and --agent resolve the way run create does, so a typo fails with the closest name and exits 1 instead of reading as an empty selection. An empty --mission, --agent or --since, such as an unset shell variable, is a usage error rather than “no filter”.
Output
The per-run lines and every warning go to stderr; only the exported N run(s) summary goes to stdout. What lands in the tree, and what does not:
  • Finished runs only. An unfinished run is never selected. Unlike run traces --export and run events export, which take a partial snapshot mid-run, a bundle is always the sealed record.
  • A run still grading is left out and named on stderr; re-run the export once grading finishes. For a finished run with no grade and none in progress, such as one whose grade was lost to a restart, fetching its report starts grading it. That calls the judge model if one is configured, so an export is not a purely passive download.
  • A run that is never graded — one that ended cancelled, never_started, deploy_failed or crashed — has no report in its directory. result.json holds the server’s not_graded answer and its reason, beside the traces and events that show what happened, and the run is named on stderr.
  • The seal in result.json is recorded, not verified. Checking it re-hashes the run’s whole evidence, which a batch would pay once per run, so every result.json carries evidence_verified: null and evidence_status: "recorded". That means the export did not ask, not that the check failed. The bundled report carries a verdict taken when it was fetched, and xorcise run status checks again.
A run whose own document answers an error, a 404 or 500 on its report, result, trace or events, is skipped and named on stderr, and the export carries on. Anything that is not one document’s answer stops the export at that run: an unreachable instance; a 401, 403, 429, 502, 503 or 504; or no response inside the 300-second per-document timeout. A timeout may be one slow run, but the CLI cannot tell it from a slow service, so it is not retried run by run. What was already written stays on disk, and stderr says how many runs were not attempted. Every document a run needs is fetched before anything is written, and a run exported for the first time moves into place in one rename, so it appears whole or not at all. A process killed mid-write can leave a dot-prefixed .xorcise-export-* staging directory in --out, and nothing removes it. Re-exporting into the same directory overwrites each run’s own files in place and leaves anything else in its directory alone, so it is not a clean slate, and a failure part-way through re-exporting one run can leave a mix of its old and new files. Export into a fresh directory when the tree must contain only this export. Two runs whose ids share their first eight characters are written under their full 32-character ids instead.

xorcise run report

Writes one self-contained document containing the run metadata, scores, check table, judge rubric and per-criterion verdicts, artifacts, telemetry summary, and disclosed conditions. The Overview table gives Created, when the run was created, and Started, when its agent started followed by what showed it, such as · first OTel export, or — for a run whose agent never started. It sets Duration, the wall clock the run held from creation, beside Telemetry window, the stretch the agent’s own telemetry covers, marked (reported by the harness). On a run whose agent died early they differ by most of the budget. When the harness’s clock contradicts the server’s, the window’s cell says why it was withheld instead of giving a number; Budgets has the rules. The Conditions table’s Agent model row reads the same as the model: line of run status. Exits 3 if the run is unfinished or still grading. A run that ended cancelled, never_started, deploy_failed or crashed has no report: the command prints not graded with the reason, writes no file, and exits 1.

xorcise run traces

Fetches the OpenTelemetry records XORCISE collected for a run. The human view prints one line per record with its sequence number and span name.

Exporting the raw OTLP stream

--export writes the run’s whole raw OpenTelemetry stream — spans first, then logs, one OTLP/JSON envelope per line. The file carries no XORCISE framing: it is the Collector’s own otlpjson format, so it feeds straight into an OpenTelemetry Collector and on to Jaeger, Tempo, or anything else that reads OTLP.
Output
Export works mid-run. The evidence stores are append-only with whole-batch commits, so a partial export is always a valid prefix of the final stream, never a torn one. An unfinished run is labelled as such:
Output
The check happens before the download, so a run that seals mid-flight can only be over-labelled partial. A snapshot is never under-labelled complete. Two combinations are usage errors, exit 2: --export with --json, because one writes a file and the other prints an envelope; and --export with --since, because an export always takes a whole-run snapshot. For the normalized per-event stream instead, see xorcise run events export.

xorcise run prompt

Prints the run’s connect prompt exactly as the agent should receive it, with real newlines and no wrapping, so it survives redirection. No options. The prompt carries the run’s token inline, because an agent you start from it has no launch block to set XORCISE_RUN_BEARER. Treat a saved copy as a credential, and create it readable by you alone:

xorcise run launch-profile

Prints the telemetry environment XORCISE configured for this run as dotenv KEY=VALUE lines. Export these before the harness starts — a harness reads them at start-up only. The output is empty when the run’s harness has no telemetry provider. No options.

xorcise run launch-cmd

Prints a copy-paste startup block for a host-run harness: the telemetry export lines, an export of the run’s token as XORCISE_RUN_BEARER, then the harness command. The prompt embedded in the command refers to that variable rather than carrying the token, so the token stays off the harness’s command line. A custom command that starts the agent in a container must forward the variable, for example docker run -e XORCISE_RUN_BEARER. When the run’s harness has no launch command it says so and exits 0. Launch mode is the most common cause of a run that records no traces. Use host when you start the harness in your own shell.

xorcise run events export

Writes the run’s normalized event stream to JSONL: a header line carrying the event count, then one event per line with clean bodies. It reads the running instance over GET /api/runs/{run_id}/events.jsonl, matching run report and run traces --export. This is a debugging and inspection tool, not a grading input. Works mid-run as a partial snapshot. The server still writes its own copy under ~/.xorcise/runs/<run_id>/ when a run seals; that copy is unrelated to where this command writes. For the raw, unnormalized OTLP stream instead, see xorcise run traces --export.

config

xorcise config show

Shows the judge model, the terrain model and whether it inherits the judge, the mission library endpoint, the network addresses, and the default run budget. The API key is only ever shown as a four-character hint.

xorcise config set-model

Sets the bring-your-own judge model. Only the fields you pass change, and the change applies to the running instance immediately — no restart. Passing no field is a usage error, exit 2. A text field containing a line break is refused by the server (422) and nothing is written; the same holds for set-terrain-model and set-network. Without a judge model the judge half of every score is unavailable, which caps the overall score at 50%. --key-stdin refuses, with exit 2 and before any request is sent, in three cases. Passing it together with --key is an error rather than a precedence rule. An empty read — an unset $KEY, Enter at the prompt, or a closed stdin — leaves the stored key unchanged instead of clearing it; --key '' is the deliberate clear. A key that spans more than one line is refused, because it would be written into .env broken across lines. These checks are made by the CLI; Where secrets live covers what the REST endpoint does not check.

xorcise config test

Calls the saved judge model for real, which is the only way to prove the key and base URL work. Exits non-zero when the model is unconfigured or unreachable, so it can gate a script. The call uses the same order of message roles as a grading call — system, user, assistant, user — with a few words in each message, so an endpoint whose chat template would refuse a grading call’s role order refuses the test too. A pass proves less than a grade would: the test only checks that the model answers, so it says nothing about whether a long run’s transcript fits the model’s context window or whether the model returns the JSON the judge expects. Bound the judge prompt covers the first.

xorcise config set-terrain-model

Overrides the model used for terrain attribution, field by field. Every field you leave unset falls back to the judge model, so an empty override means terrain uses the judge. Passing '' clears a field. Passing no field is a usage error, exit 2.

xorcise config test-terrain

Calls the effective terrain model — the override if one is set, otherwise the judge model — and reports which one it used.

xorcise config set-network

Experimental. Sets the two multi-machine addresses. Multi-machine deployment is unfinished: pointing a local-only instance at a remote control plane misconfigures it, so leave both unset unless you are deliberately testing that path. This is the only place these are set — the web UI shows them read-only. The values are read at boot, so a change needs xorcise down then xorcise up. Passing '' unsets a field, and passing no field is a usage error, exit 2.

catalog

xorcise catalog status

Shows the saved library setting alongside a live reachability probe, so a saved preference is never presented as a working connection. Exits 1 when the library is enabled but unreachable. The library needs no account, key, or sign-in.

xorcise catalog connect

Enables the online mission library, then reports whether the endpoint actually answers. Idempotent: already connected prints already enabled and exits 0.

xorcise catalog disconnect

Disables the online library so xorcise mission list shows only local missions. Installed missions keep working. Idempotent.

Advanced

xorcise serve

Runs XORCISE in the foreground. This is the process xorcise up starts for you; use up for a normal install. Sibling commands such as status and ui discover a relocated port only for instances started by up. Once its ports are bound, serve prints one listening on … line per plane to stderr, naming every address that plane listens on. Under xorcise up, stderr is ~/.xorcise/serve.log. A plane that fails to start prints error: server exited — <reason> and serve exits 1. Like up, serve refuses with exit 1 when deployment_topology is distributed and host is a loopback address, and tells you to set host in config.toml. Ctrl-C, or the SIGTERM that xorcise down sends, shuts it down gracefully: it prints shutting down, closes the database, and exits 0. Unlike up, serve does not check whether an instance of this home is already running. Started beside one, it moves to the next free port and serves a second instance over the same database.

xorcise db upgrade

Applies pending database migrations. It is always deliberate and never runs at boot: xorcise up migrates a fresh database, but refuses to migrate one that already holds data. A history mismatch — a database stamped by a different build — is reported as such and exits 1, with the recovery path being a backup of the database or a clean start via xorcise down --purge. It also refuses, with exit 1, while a server of this home is running. It looks for one the way xorcise up does: by the pid file, then by asking the REST ports. Migrating rebuilds tables the running server has open, so the error names the server and the order to do things in:
Output
A REST port that accepts the connection but cannot be identified also refuses, with Not migrating while that is unknown, because a server there cannot be ruled out.

xorcise role show

Experimental. Reports the service role this shell would boot, taken from XORCISE_ROLE and defaulting to all. This is not necessarily the role a running instance is serving — ask xorcise system for that.

xorcise role list

Experimental. Lists the five roles with a maturity column.
Output
Only all is a complete install. The others boot, but multi-machine deployment is unfinished: control has no runner, so it answers 503 to run creation and to mission pulls, updates and ingests, and cannot release a run’s environment, and runner and headscale serve only a health endpoint.

Environment variables

Configuration resolves highest first: command-line flags, then XORCISE_* environment variables, then ~/.xorcise/.env, then ~/.xorcise/config.toml. Every setting is reachable as XORCISE_<FIELD>, so anything xorcise config show lists can be set from the environment. These are the ones the CLI itself reads. xorcise run launch-profile and xorcise run launch-cmd emit OTEL_* variables for your harness to consume, and xorcise run launch-cmd also emits XORCISE_RUN_BEARER, the run’s token, for your agent. The CLI does not read them.

Configuration keys

Every setting, its environment variable, and its default.

Troubleshooting

Symptom to fix, starting from doctor and status.