Skip to main content
The CLI and the web UI are both clients of this API, so anything either of them does, you can do over HTTP.

Endpoint summary

Fifty-two operations across 48 paths. The set is generated from the running application, so it is complete for the default all role; other roles serve a subset.

Conventions

Base URL

Every path above is relative to the origin XORCISE is serving on, plus /api. Do not hard-code a port: the REST plane starts at 3001 and walks up to 50 ports to find a free one, so a second instance, or a busy port, moves it. xorcise up prints the URL it took, xorcise status prints it on the REST API row, and xorcise ui prints the same origin with /ui appended. The examples on this page take the base URL from xorcise ui and strip the /ui suffix:

Authentication

The operator API has no authentication. No API key, no session, no token, on any of the agent, mission, run, result, configuration, system, or filesystem endpoints. In the default local deployment topology XORCISE binds loopback plus, on Linux, the Docker bridge gateway — so every local process and every container on that bridge can read your runs and traces, change your judge model, start runs, and delete results. Set XORCISE_HOST=0.0.0.0 and the network can do the same. Reachability is the access control. Security and isolation explains why the bind is what it is and what to do about it. The one exception is the eight run-control endpoints your agent calls. Those authenticate per run with a bearer token, and they authorize nothing else — a run’s token opens that run and no other.

The run-control token

POST /api/runs mints a 32-character hexadecimal token and returns it as run_control_key in the 201 body. That is the only place it appears. GET /api/runs does not carry it, and there is no endpoint that re-reads it, so capture it when you create the run.
Two failures, both 401:
The first means the Authorization header was absent or was not a Bearer credential. The second means the token does not belong to that run.

Run ids

The REST API matches run ids exactly and takes the full 32 characters. xorcise run status 4c62254b works because the CLI expands a unique prefix before it calls the API; GET /api/runs/4c62254b/result returns 404.

Errors

Every error carries FastAPI’s default shape, a single string under detail:
Those strings are written to be read by a person and are what the web UI displays verbatim, so surface them rather than replacing them with your own message. A 422 is the exception and carries a list instead, one entry per invalid field:
Every endpoint that takes a body or a typed parameter can return 422. It is omitted from the per-endpoint status tables below except where a specific value, such as an unsupported report format, is rejected that way.

The 202 grading window

Grading runs after a run ends, not during the request that ends it, so there is a window in which a run is finished but has no score. Four endpoints share one state ladder: GET /result, GET /report, and GET /stats all return 202 this way, and POST /regrade returns 202 on success. Poll on 202 — it is a normal transient state, not a failure. Polling also re-drives grading, de-duplicated, so a grade lost to a restart heals on the next poll instead of hanging at grading forever.

Agents

GET /api/agents

Synopsis
Description. Lists every registered agent. An agent is a record you declare, not a process XORCISE launches, so this endpoint reflects only what you have told XORCISE. Parameters. None. Request. No body. Response. 200 with an array of agent records. Each carries id, name, created_at, version, and the optional declared fields endpoint, kind, model, and otel. version starts at 1 and increments on every update. otel is stored and read by nothing — the OTLP endpoint your agent actually uses comes from GET /api/runs/{run_id}/launch-profile. Example

POST /api/agents

Synopsis
Description. Registers an agent under a unique name. kind is the one field that changes behaviour: it selects the replay adapter, the telemetry provider, and the launch provider together. The built-in values are openhands, claude-code, and codex; any other value, including none, falls back to the generic adapter. Parameters. None. Request. name is required. endpoint, kind, model, and otel are optional strings.
Response. Example

PUT /api/agents/

Synopsis
Description. Replaces an agent’s declaration and increments its version. Sending a different name renames the agent, keeping its id, its version history, and its runs. Results already recorded keep the version they were scored under, which is what lets you compare an agent against its earlier self. Parameters. Request. The same shape as POST /api/agents. name is required and may differ from the path parameter.
Response. Example

DELETE /api/agents/

Synopsis
Description. Removes an agent together with every version, run, and result belonging to it. This cascade is deliberate: a result is meaningless without the agent record that names what produced it. Deleting a mission behaves differently and keeps its runs. Parameters. Request. No body. Response. Example

GET /api/agents//history

Synopsis
Description. Returns every result recorded for an agent, oldest first, so you can watch a score move as the agent changes. intel_disclosed is filled at read time from what the agent actually requested during each run. Parameters. Request. No body. Response. 200 with an array of history entries carrying run_id, agent_id, created_at, overall, deterministic, judge, partial, partial_trigger, trace_ref, and a conditions object. 404 when no agent has that name. Example

Missions and the library

GET /api/missions

Synopsis
Description. Returns one merged list of the missions installed on this machine and the missions the XORCISE library offers, distinguished by source. The library needs no account, key, or sign-in; when it is disconnected or unreachable you get the installed set only. Parameters. None. Request. No body. Response. 200 with an array of catalog entries: source (your_own or library), mission_id, name, summary, proficiency, specialty, type (lab or static), skills, technologies, installed, and image. A static mission has no environment and therefore no image. Example

GET /api/missions//manifest

Synopsis
Description. Returns the complete mission.json for a mission — metadata, environment, attachments, artifacts, checks, rubric, intel, and terrain. XORCISE prefers the installed copy and falls back to the library’s. The rubric and the checks are visible here, so treat this endpoint as spoiler territory for anyone about to attempt the mission. Mission manifest documents every field. Parameters. Request. No body. Response. 200 with the manifest. 404 when the mission is neither installed nor in the library. Example

DELETE /api/missions/

Synopsis
Description. Uninstalls a mission. Runs and results that reference it survive, and so does the fused image in Docker, so a re-pull or re-ingest is cheap. This is the opposite of the agent cascade. Parameters. Request. No body. Response. Example

POST /api/missions/ingest

Synopsis
Description. Starts ingesting a mission bundle from a directory on the machine running XORCISE, not from the machine making the request. Ingest validates the manifest, builds the fused image, and installs the result, which takes minutes, so it returns a job id immediately and does the work in the background. Validation happens only here — there is no separate validate endpoint. Parameters. None. Request. bundle_dir is required and is an absolute path on the XORCISE host.
Response. 202 with {"job_id": "<job_id>"}. Poll GET /api/missions/ingest/{job_id} for the outcome. A malformed body is 422; a bad manifest is not rejected here but surfaces as an error job status. Example

GET /api/missions/ingest/

Synopsis
Description. Reports an ingest job’s status and streams its build log. since is a line cursor: pass the number of lines you have already consumed and you get only what is new, which is how the CLI and the web UI tail a build without re-reading it. Parameters. Request. No body. Response. 200 with job_id, status (building, installed, or error), logs, and, once installed, slug and image. On failure detail carries the reason, and a manifest rejection reads as invalid mission.json — most often an unknown key, since every manifest model forbids extras. 404 when the job id is unknown. Example

POST /api/missions//pull-jobs

Synopsis
Description. Starts a background pull of a library mission onto this machine. Calling it while a pull for the same mission is already running joins that job rather than starting a second one, so it is safe to retry. This is the pull path both the CLI and the web UI use. Parameters. Request. No body. Response. 202 with {"job_id": "<job_id>"}. Poll GET /api/missions/pull-jobs/{job_id}. Example

GET /api/missions/pull-jobs/

Synopsis
Description. Reports a pull job’s progress. percent and eta_seconds are null until Docker knows the total size, and neither is monotonic, because the daemon discovers layers as it goes. A pull that shows no byte movement is usually not stuck: cached layers and registry negotiation report status without transferring anything, which is what phase is for. Parameters. Request. No body. Response. 200 with job_id, mission_id, status (pulling, installed, error, or cancelled), phase (resolving, pulling_image, downloading_bundle, installing, or done), bytes_current, bytes_total, percent, eta_seconds, detail, cancel_requested, and, once installed, the catalog entry. 404 when the job id is unknown. Example

GET /api/missions/pull-jobs

Synopsis
Description. Finds the pull job currently running for a mission, so a client that reloads mid-pull can rejoin the progress it was already watching instead of starting a second pull. Parameters. Request. No body. Response. 200 with the same job object as GET /api/missions/pull-jobs/{job_id}, or null when nothing is pulling. Omitting mission_id is 422. Example

POST /api/missions/pull-jobs//cancel

Synopsis
Description. Requests cancellation of a pull. The cancel is cooperative and is checked on every progress event, so it lands quickly and does abort the transfer rather than letting it finish quietly. Calling it twice is harmless, and cancelling a job that already finished returns that job unchanged. Parameters. Request. No body. Response. 200 with the job object, cancel_requested set to true and status moving to cancelled. 404 when the job id is unknown. Example

POST /api/missions//pull

Synopsis
Description. Pulls a library mission and blocks until the pull finishes. It works, but it is superseded: no shipped client calls it, a large image holds the connection open for minutes, and it reports no progress. Prefer the job pair above. Treat this endpoint as internal. Parameters. Request. No body. Response. Example

GET /api/catalog/status

Synopsis
Description. Reports whether the mission library is reachable from this machine. disconnected means you turned the library off; error means it is on but did not answer. Parameters. None. Request. No body. Response. 200 with state (connected, error, or disconnected), last_sync, and message. Example

Runs

GET /api/runs

Synopsis
Description. Lists runs, newest first. There is no endpoint for a single run: list and filter client-side, or read the run’s result, environment, or events directly. Only two states are persisted, created and terminal, so a run that is currently being worked on reads as created. Parameters. None. Request. No body. Response. 200 with an array of run records carrying run_id, agent_id, mission, name, state, created_at, budget_seconds, terminal_trigger, completed_at, model, sandbox_ref, agent_version, mission_version, source_agent, intel_policy, and last_telemetry_at. The run_control_key is never included. Example

POST /api/runs

Synopsis
Description. Creates a run and deploys its environment: it reserves a private subnet, starts the mission, joins it to the run’s tailnet, renders the connect prompt, and mints the run-control token. For a lab mission this takes seconds and can fail for real infrastructure reasons, which is why it has more failure codes than any other endpoint. A static mission deploys nothing. Parameters. None. Request. agent and mission are required and are the agent’s name and the mission’s id. budget_seconds bounds the run in wall-clock seconds. name overrides the generated run name. intel_policy is all or empty for every authored intel item, none for no intel, or a comma-separated list of intel ids such as i1,i3; it defaults to all.
Response. Example

DELETE /api/runs/

Synopsis
Description. Deletes one run and its recorded result. A run that has not finished cannot be deleted, because its environment is still deployed — terminate it first. Parameters. Request. No body. Response. Example

POST /api/runs//terminate

Synopsis
Description. Stops a run early on your instruction, seals the evidence collected so far, tears the environment down, and schedules grading in the background. The result is flagged partial with a partial_trigger of operator, which is how a deliberately-stopped run is told apart from one that scored badly. Parameters. Request. No body. Response. Grading has not finished when this returns. Poll GET /api/runs/{run_id}/result through the 202 window. Example

GET /api/runs//environment

Synopsis
Description. Reports the live state of the run’s environment. This is what drives the status chip on the live run page, and it is the endpoint to poll after creating a run to know when the mission is actually up. Parameters. Request. No body. Response. 200 with run_id, state, ready, and detail. state is none for a static mission with no environment, then starting, ready, failed, or released once the run is over and the environment is torn down. 404 when the run id is unknown. Example

GET /api/runs//prompt

Synopsis
Description. Returns the connect prompt verbatim — the mission text your agent reads, carrying the objective with target IPs substituted, the tailnet join recipe, the run-control endpoints, and the correlation marker. launch_mode rebases the run-control base URL in that text to where your agent actually runs, and getting it wrong is the most common reason an agent cannot check in. Parameters. An unrecognised launch_mode is clamped to container rather than rejected, then narrowed to what the agent’s harness supports. Request. No body. Response. 200 with {"run_id": "...", "prompt": "..."}. 404 when the run id is unknown. Example

GET /api/runs//launch-profile

Synopsis
Description. Returns the telemetry environment for this run and a copy-paste command to start the agent with it. This is the authoritative OTLP endpoint — the otel field on the agent record is not read. correlation reports how strongly traces will bind to this run: resource-attr for a harness with a telemetry provider, prompt-sentinel otherwise, which relies on the marker in the connect prompt surviving. Parameters. Request. No body. Response. 200 with run_id, env, correlation, notes, fallback, launch_mode, launch_modes, command, shell_block, and tips. env is empty when no OTLP collector is configured. 404 when the run id is unknown. Example

GET /api/runs//events

Synopsis
Description. Returns the normalized replay stream — what the agent did, after the harness’s raw telemetry has been run through its replay adapter. XORCISE ingests both traces and logs, and they advance on separate cursors, because a harness such as Codex carries its entire narrative in logs. Poll by echoing the next_cursor from the previous response. Parameters. Request. No body. Response. 200 with run_id, source_agent, adapter_name, adapter_version, events, counts, warnings, fallback, next_since, and next_cursor. fallback is true when the generic adapter handled the stream because the harness had no adapter. An unknown run id returns an empty stream rather than 404. Example

GET /api/runs//events//raw

Synopsis
Description. Returns the raw OTLP span or log record that one normalized event was derived from. Use it to check what a replay adapter did with a record, or to see attributes the adapter dropped. The raw record is canonical; the normalized event is a view over it. Parameters. Request. No body. Response. 200 with the source record as it arrived. 404 when the event id is not in that run. Example

GET /api/runs//traces

Synopsis
Description. Returns the raw OTLP records collected for a run, in arrival order, with no adapter applied. since is exclusive, so passing the last seq you saw gives you only what arrived after it. This is the endpoint behind xorcise run traces, and it is the right one for confirming that telemetry is reaching XORCISE at all. Parameters. Request. No body. Response. 200 with {"run_id": "...", "records": [{"seq": 0, "payload": {...}}]}. An empty records array on a run whose agent is working usually means correlation failed rather than that nothing happened — check correlation on the launch profile. Example

GET /api/runs//terrain2

Synopsis
Description. Returns the resolved terrain map for a run: the nodes the mission author declared, the updates that have fired, and the attribution linking each update to the evidence that triggered it. Terrain conditions are natural-language statements scored by a model, so this endpoint’s contents depend on the terrain model being configured. Parameters. Request. No body. Response. 200 with the resolved terrain — nodes with their groups and states, edges, updates, and an attribution status. 404 when the run id is unknown. A mission that declares no terrain returns an empty map, not an error. Example

GET /api/runs//artifacts

Synopsis
Description. Returns everything the agent submitted during the run, with the payloads intact, in submission order. This is the operator’s view — it needs no run-control token, unlike the endpoint the agent submits through. The flag, if the agent found one, is the artifact named flag. Parameters. Request. No body. Response. 200 with an array of {name, kind, seq, payload}. 404 when the run id is unknown. Example

Results

GET /api/runs//result

Synopsis
Description. Returns the recorded score and the conditions it was produced under. The score is half deterministic checks and half model judgement, each weighted 0.5, so a run graded with no judge configured tops out at 50% and that is not a failure. judge_status is the diagnostic to read first when the judge half is zero. Parameters. Request. No body. Response. grade.judge_status is ok, model-not-configured, or unavailable, with the reason in judge_detail. grade also declares hard_fails, key_evidence, and major_deductions; no code path fills them and they are always empty. How grading works explains the arithmetic. Example

GET /api/runs//report

Synopsis
Description. Returns the run’s full report as a single self-contained document — the same scores, check table, rubric, evidence, artifacts, and conditions as the result, rendered for sharing. The HTML carries its own styling and loads no external asset, so it survives being emailed. Parameters. Request. No body. Response. Example

GET /api/runs//stats

Synopsis
Description. Returns the token, count, and timing snapshot recorded when the run was graded. A run graded before the snapshot existed is folded live from its events instead, read-only. Every token figure is 0 on a run that emitted no telemetry, which is the normal reading rather than a fault. Parameters. Request. No body. Response. Example

POST /api/runs//regrade

Synopsis
Description. Re-grades a finished run’s already-sealed evidence against your current settings, without re-running the agent. This is the fix for a run whose judge half failed for a configuration reason — the transcript exceeded the judge’s token budget, or the key was rejected. Raise the cap or fix the key, call this, and the judge scores the same preserved evidence. Re-grading is cheap next to a re-run, and it is the only way to recover a misconfigured judge on a run you cannot reproduce. There is no CLI equivalent; the web UI exposes it as Re-evaluate on the Results page. Parameters. Request. No body. Response. The new score arrives through the same 202 poll as the first grade, so a client needs no extra state. Calls are de-duplicated: pressing the button twice grades once. Example

Run control

These are the endpoints your agent calls during a run. Every one requires Authorization: Bearer <run_control_key> and returns 401 without it. Two shared mappings apply throughout: once the run is over, any call returns 409, and a mission that cannot be read returns 404. Connect any agent walks through the contract these endpoints form.

GET /api/runs//mission

Synopsis
Description. Returns the agent’s brief: the mission id, the objective with target IPs already substituted, and the names of the attachments available. It deliberately does not include the rubric or the checks. Your agent can re-read this at any time while the run is open. Parameters. Request. No body. Response. Example

POST /api/runs//artifacts

Synopsis
Description. Submits one named finding. This is how the agent reports what it recovered, and the flag is the artifact named flag — there is no separate flag endpoint. Content is inline text or JSON; binary upload is out of scope. Submitting a name twice records both, in order. The name matters: deterministic checks look the artifact up by the name the agent used, so a misspelled name fails the check silently rather than raising an error here. Parameters. Request. name and content are both required strings.
Response. Example

GET /api/runs//intel

Synopsis
Description. Requests the next intel item the run’s policy allows. Each call discloses one more, in the order the mission authored them, and the count is recorded on the result as intel_disclosed, so disclosed intel is visible in the conditions a score is compared under. remaining tells the agent how many intel items are left after this call. When the policy allows no intel, or all of it is spent, intel is null and remaining is 0. Parameters. Request. No body. Response. Example

POST /api/runs//complete

Synopsis
Description. Ends the run. XORCISE seals the evidence, tears the environment down, and schedules grading in the background. Your agent should call this when it is finished, whether or not it solved the mission — a run left open scores nothing until its budget expires. Telemetry arriving after this call is dropped once a five-second drain closes, so flush your exporter before calling it. Parameters. Request. No body. Response. Example

GET /api/runs//connect

Synopsis
Description. Returns the credentials your agent needs to join the run’s private network, as JSON. login_server is already rewritten to an address the agent can reach, and ca_cert carries the local certificate authority when one is needed and is an empty string otherwise. Most agents use the script form below instead of assembling the join themselves. Parameters. Request. No body. Response. 200 with login_server, join_key, and ca_cert. 401 without a valid bearer. Example

GET /api/runs//join.sh

Synopsis
Description. Returns the same join bundle as a runnable shell script, which is the join path the connect prompt tells your agent to use. It needs no root and configures the tunnel in userspace, printing the SOCKS5 address the agent then routes target traffic through. The script bakes a self-reaper that tears the tunnel down at the run’s budget plus 600 seconds, or after 86,400 seconds when the run is unbudgeted. Parameters. Request. No body. Response. 200 with text/x-shellscript. 401 without a valid bearer. Example

GET /api/runs//tailscale.tgz

Synopsis
Description. Serves the pinned static tailnet client the join script uses, so an agent in a minimal container does not need a package manager or its own network access to get one. The join script fetches this for you; call it directly only when you are assembling the join by hand. Parameters. Request. No body. Response. Example

GET /api/runs//attachments/

Synopsis
Description. Downloads a mission attachment in two calls. Call it with the bearer and no query parameters and you get a signed URL back; call that URL with the X-Run-Key header and you get the bytes. The split keeps file bytes off the bearer-authenticated call so a download can be streamed or handed to another process without carrying the run token in a shell history. Parameters. Request. No body. Response. The mint response carries name, url, expires_at, media_type, and sha256. Example
Then fetch the bytes:

Harnesses

GET /api/harnesses

Synopsis
Description. Lists the built-in harnesses that ship both a replay adapter and a launch provider — the values worth registering as an agent’s kind. Each descriptor carries a registration-time preview of how that harness is launched. The preview is a template, not a runnable command: run credentials, endpoints, and the mission text only exist once a run is created, so GET /api/runs/{run_id}/launch-profile remains authoritative for a real launch. Parameters. None. Request. No body. Response. 200 with an array of descriptors carrying kind, display_name, description, model_hints, capabilities (the same profile the capabilities endpoint serves), and launchlaunch_modes (host, container), command_template, model_flag, model_flag_anchor, tips, and mission_preamble. Example

GET /api/harnesses/capabilities

Synopsis
Description. Returns every registered replay adapter’s declared telemetry capability profile, sorted by adapter name — the honest matrix of which event kinds a harness actually exports. notes carry the user-facing gap sentences for partially-supported kinds, rendered verbatim in the UI and the judge disclosure. verified is false only for the generic fallback adapter, whose profile is not audited against a real harness. Parameters. None. Request. No body. Response. 200 with an array of profiles carrying adapter_name, adapter_version, verified, kinds (a mapping over every event kind to supported, partial, or unsupported), message_roles, and notes. Example

System and configuration

GET /api/health

Synopsis
Description. Liveness only. It answers as soon as the REST plane is serving and says nothing about Docker, Headscale, or the database — use the system endpoint for those. Parameters. None. Request. No body. Response. 200 with {"status": "ok", "service": "rest"}. Example

GET /api/system

Synopsis
Description. Reports what the running instance sees about itself: which role it booted, which planes are healthy, where its data lives, and whether its database schema matches the build. xorcise status probes from your machine; this asks the instance, and the two disagreeing is itself the diagnosis. Parameters. None. Request. No body. Response. 200 with role, topology (local or distributed), home, db_url, db_schema (head, behind, fresh, or unknown), planes, catalog, and remotes. On the default all role, planes carries four rows — rest, docker, headscale, and otlp — each with name, ok, detail, location, role, label, and state. A db_schema of behind means xorcise db upgrade is due. Example

GET /api/config

Synopsis
Description. Returns the effective configuration with secrets masked. Model keys are never returned — key_hint shows the last four characters so you can tell which key is loaded. judge.configured is a presence check only and does not prove the key works; the test endpoints do that. Parameters. None. Request. No body. Response. 200 with judge, terrain, catalog, network, and default_budget_seconds. terrain.uses_judge_default is true when no terrain override is set, which means terrain attribution calls the judge model. Configuration keys documents every field. Example

PUT /api/config/model

Synopsis
Description. Sets the judge model and its token caps. Every field is optional and only what you send is changed; an empty string clears a field. The two caps are the controls behind judge failures on long runs — span_max_tokens truncates each span, and transcript_max_tokens caps the whole prompt before it is sent. Writes land in ~/.xorcise/.env as XORCISE_* variables, never in config.toml, so a value set here survives a restart but does not appear in your config file. Parameters. None. Request. All fields optional: model_name, base_url, key, transcript_max_tokens, span_max_tokens, tokenizer, timeout_seconds.
Response. 200 with the updated masked configuration, the same shape as GET /api/config. 422 when a value is out of range — the caps must be non-negative and timeout_seconds must be greater than zero. Example

POST /api/config/model/test

Synopsis
Description. Calls the saved judge model once and reports what happened. This is the only endpoint that spends tokens on your provider account, and it is the difference between a key that is present and a key that works. Parameters. None. Request. No body. Response. 200 with ok, status (ok, not_configured, or error), model_name, and message. A failed call is still 200 — read ok, not the status code. Example

PUT /api/config/terrain-model

Synopsis
Description. Sets a separate model for terrain attribution, or clears it so terrain falls back to the judge model. Terrain attribution calls a model on every run that declares terrain, so pointing it at a cheaper model than the judge is the usual reason to set it. Parameters. None. Request. All fields optional: model_name, base_url, key, transcript_max_tokens. Send empty strings to clear the override.
Response. 200 with the updated masked configuration. terrain.uses_judge_default becomes false once an override is set. 422 when transcript_max_tokens is not greater than zero. Example

POST /api/config/terrain-model/test

Synopsis
Description. Calls the effective terrain model once — the override when one is set, the judge model otherwise — and reports what happened. Like the judge test, it spends tokens. Parameters. None. Request. No body. Response. 200 with the same shape as the judge test: ok, status, model_name, message. Example

PUT /api/config/catalog

Synopsis
Description. Turns the mission library on or off. Disconnecting hides library missions from browse and leaves your installed missions untouched. No credential is involved in either direction — the library needs no account or key. Parameters. None. Request. connected is required.
Response. 200 with the updated masked configuration. 422 when connected is missing or is not a boolean. Example

PUT /api/config/network

Synopsis
Description. Sets the addresses used when XORCISE runs across more than one machine. This backs the distributed topology, which is experimental: the CLI labels it so, the web UI shows these values read-only, and values written here apply at the next start rather than immediately. Parameters. None. Request. Both fields optional: headscale_url, advertise_host.
Response. 200 with the updated masked configuration. Example

GET /api/fs/list

Synopsis
Description. Lists one directory on the machine running XORCISE. It exists so the web UI’s bundle picker can browse to an ingest directory, and it is read-only. Because the API has no authentication, this endpoint lets anyone who can reach the port enumerate directories on that host — one more reason to treat the port as sensitive. Security and isolation covers the boundary. Parameters. Request. No body. Response. 200 with path, parent, and entries, each entry carrying name, path, and is_dir. 400 when the path does not exist or is not a directory. Example
  • CLI reference — the same operations as commands, with exit codes and the web UI equivalent for each task.
  • Security and isolation — what the missing authentication means in practice, and how to place a boundary around it.