Skip to main content
This page is for contributors working from a clone of the XORCISE repository. The evaluation loop itself — up → register → mission → run → prompt — is the same one every user walks, and Your first run explains each step; this page covers only what is different from source: the install command, the fused base image, stub mode, and the failure modes a development checkout produces. General failures are on Troubleshooting.

Prerequisites

  1. Docker Engine running, and the checkout installed from its lockfile:
    That is the command the engine’s CONTRIBUTING.md gives and CI runs, and --locked fails rather than re-resolving when uv.lock is out of date. Do not use uv pip install -e .: uv’s pip interface never reads uv.lock, so a clean checkout gets whatever is newest on PyPI instead of what CI tests, and can fail tests on a tree you have not touched. uv sync installs into the checkout’s .venv, so activate it (source .venv/bin/activate) or prefix the commands below with uv run. No extra is needed to run missions: the Docker SDK is in the base dependencies, and the runner and all extras are empty, kept only so older install commands still resolve. A plain pip install xorcise covers the default single-host setup. A source checkout also needs Node.js 22 and npm, because xorcise up builds the web UI from frontend/ rather than using a prebuilt one. It installs the frontend dependencies and rebuilds the static export when it is missing, broken or older than the frontend source, and if npm is missing or the build fails it warns and carries on without a current UI. XORCISE_SKIP_FRONTEND_BUILD=1 skips the step. frontend/.nvmrc names the version, and frontend/package.json declares >=22 <23. Clone with full history. The version is read from git tags when the checkout is installed: the newest v* release tag, never the mission-base-v* tags of the base image. Between releases xorcise --version prints a development version that counts commits past that tag, such as 0.2.1.dev3+g1a2b3c4. A git clone --depth 1 checkout has no tags to read and reports a placeholder 0.1.dev… version instead.
  2. The fused base image xorcise/mission-base — the local fused-image build produces it automatically if it is absent or carries the wrong base generation label (from the bundled context under containers/mission-base). Build it ahead of time only if you want to control when that happens:
    The base installs Tailscale, so the build needs egress to tailscale.com.
  3. A mission — pull one from the mission library: xorcise mission list, then xorcise mission pull <id>. The library is free, and pulling needs no account and no key.
Installing your own mission bundle is coming soon. In this release xorcise mission ingest is a stub — whatever arguments you pass it, it prints a coming-soon notice and exits without installing anything — and the Ingest a bundle button on the Missions page opens a product preview rather than a directory picker. Missions come from the library instead: xorcise mission list and xorcise mission pull <id>, or that same Missions page. The bundle format still holds if you want to prepare one ahead of time — a directory with mission.json + docker-compose.yml + service Dockerfiles, as described in Author a mission.
xorcise up provisions the local Headscale control plane itself (and xorcise down tears it down); an external control plane can be pointed at via the headscale_url setting instead.
To walk the flow without Docker, use xorcise up --stub (or XORCISE_USE_STUBS=1) — the loop then runs against stubs and no container actually starts. xorcise up checks for Docker before it starts anything and exits with the fix when the daemon is not reachable — it never silently falls back to stubs.

The loop

What “real” looks like

  • Installing a mission puts a real fused image on the host and writes the install record — no registry needed at run time. The local build engine behind that (the POST /missions/ingest API, which the coming-soon CLI and console surfaces will drive) builds xorcise/mission-base if it is not already present with the right generation label, then builds the inner stack into a fused xorcise/mission-<slug>:local image. The tag is local for every mission, not a version, so a rebuild replaces the image in place.
  • run create starts a real container (docker ps shows it), and the network fence mints a one-time pre-auth key and applies the per-run ACL — the agent can reach exactly its one mission.
  • run prompt carries a live tailnet join recipe, the authenticated run-control REST URL + per-run key, and the target(s) — everything the agent needs to join and work the mission.

If something fails

  • xorcise up stops on a Docker check — start Docker, or use --stub for a Docker-less walkthrough. Docker daemon is not reachable — start Docker is a different message: it is the 503 that run create and mission pull answer when the daemon is down. The server itself does not fail at boot over it; its boot-time readiness check only logs a warning.
  • The base image build fails — it needs egress to tailscale.com. On a restricted network, pre-build xorcise/mission-base from a host with egress (or an offline mirror) so the build finds it already present.
  • mission pull fails — check the catalog connection (xorcise catalog status). Pulling is how missions arrive in this release, so it needs network access to the mission library; a fully disconnected host cannot install one.
  • A CLI command says it cannot reach the server, but the server is up — the server may have relocated to a free port; always use the URL xorcise up printed (or run xorcise ui), and don’t hardcode :3001 in scripts.
  • mission pull succeeds but mission list / run create don’t see it — the server and your shell are using different homes. Don’t set XORCISE_HOME (both default to ~/.xorcise), and kill any stale server before xorcise up.
  • xorcise doctor checks the Python version, Docker, the Docker daemon, Compose v2, free disk space, openssl, /dev/net/tun and the data directory. It also reports the mission base release the catalog promotes, the platform missions execute on, whether the host can run containers nested inside a run container, the REST and OTLP ports, and the Headscale control plane.
Everything else — Docker socket permissions, database migrations, telemetry correlation, grading — fails the same way from source as from an installed package: see Troubleshooting.