Reference
Files and HTTP API
Besides orca.toml, morca reads and writes an HTTP API
(every UI page is also JSON), logs and machine metrics under ORCA_LOGS,
an orca schema in Postgres, and its environment.
The HTTP API
orca ui serves every page as HTML, and as JSON to
Accept: application/json or ?format=json; paths under
/api/ are always JSON. Reading needs no login. An action needs a session
cookie from a browser or an API token from a script (Authorization), and
is audited as its user's. A worker's token works only on /api/worker/,
and a user's token never does. The full route table is the docstring of
routes.mojo, and
ui.md describes each page and its JSON
fields.
Pages and their JSON
| Path | What it answers |
|---|---|
/ | the pipelines with their coverage, schedules, next and last runs |
/pipelines/<name> | a pipeline's tabs (?tab=overview, runs or operate): steps, partitions, runs, fill, waiting runs and its registration (config) |
/api/pipelines/<name> | the pipeline page's JSON, whatever is asked for |
/pipelines/<name>/progress | the Progress card as a fragment: runs going, fill, how far a sensor is behind, coverage |
/pipelines/<name>/calendar | a daily or monthly pipeline's days or months of ?month= with their state, as JSON |
/pipelines/<name>/keys/<key> | one key of a keyed pipeline: its steps in every run it was in |
/runs | runs newest first, filtered by ?pipeline=, ?status=, ?trigger=, ?from=, ?to=, ?key=, paged with ?before= and ?after= |
/runs/<id> | a run's cards, steps, machines and the snapshots each step read and wrote |
/runs/<id>/attempts/<n> | an attempt's log and error; /log is the log as a fragment the page refreshes while it runs |
/runs/<id>/timeline.json | the run's Dista timeline, in Chrome's trace format |
/runs/<id>/driver.log | the run's driver output, as the scheduler kept it |
/runs/<id>/cluster/<name> | an EC2 run instance's head or worker log, for the run's own instances only |
/runs/<id>/metrics | the run's machines: charts and peaks from its samples |
/api/gantt | the pipelines and their runs over ?window=1h, 6h, 24h or 7d, for the home page's Timeline |
/datasets, /datasets/<name> | datasets with their writers, readers and snapshots, and one dataset's place in the graph |
/datasets/<name>/snapshots/<id> | a snapshot's lineage: back (what it was made from) and forward (what was made from it) |
/rebuilds/<id> | a rebuild's pipelines by level, with their runs |
/ops/alerts, /api/alerts | every alert sent, newest first, with its delivery and whether it's still open |
/ops/control, /api/system | the control host's CPU, memory, disk, network, containers' memory and memory pressure |
/ops/pools, /api/pools | every machine runs are placed on: EC2 instances, Kubernetes pods and workers; from 0.24.0 also autoscaled |
/ops/pools/<kind>/<id> | one machine: its facts, an instance's samples or the runs a worker claimed |
/api/fleet, /api/workers | Fleet's summary, hosts and charts, and each worker token's machine with its heartbeat and runs |
/watchdog, /api/watchdog | the watchdog's rules with their values, limits and snoozes, and its open trips |
/metrics | Prometheus gauges: the control host, runs running and queued, hosts, memory pressure and recent errors |
Pages that refresh have a fragment twin (/runs/table,
/home/pipelines, /ops/pools/live,
/ops/control/live and others) that the page polls. The old
/fleet, /ops/fleet, /ops/workers and
/system redirect to Pools and Control.
A run's cluster (from 0.24.0)
/runs/<id>'s JSON gains cluster: the workers a run's
Dista cluster spread over, when a pipeline placed on a worker pool has
workers = N. The first row is the worker that started the run, holding
its head; each later one a member that joined. It is an empty list for a run on one
machine. The run page shows the same as its Cluster card.
"cluster": [
{"role": "head", "start": 1, "worker": "orca-pool-lab-3", "address": "10.77.0.3:41237",
"grant": {"cpus": 8, "memory_mb": 15360}, "joined": "2026-11-11T09:12:04Z",
"ended": null, "why": null},
{"role": "member", "start": 1, "worker": "orca-pool-lab-4", "address": null,
"grant": {"cpus": 8, "memory_mb": 15360}, "joined": "2026-11-11T09:12:31Z",
"ended": "2026-11-11T09:40:10Z", "why": "its run ended"}
] Autoscaled pools (from 0.24.0)
/ops/pools' JSON and /api/pools gain
autoscaled: each [pools.<name>] morca grows, with its
types and waits (null for none), locations, limits, live machines, the
type it is trying and what its last try came to. Pool machines are named
orca-pool-<pool>-<n>.
"autoscaled": [
{"name": "lab", "provider": "hetzner",
"machines": [{"type": "cax31", "wait": 600}, {"type": "cpx32", "wait": null}],
"locations": ["hel1", "nbg1"], "max_machines": 3, "idle_seconds": 900,
"live": 2, "trying": null, "status": null}
] Actions
Each is a POST of a form, or of JSON from orca --remote. It
answers 303 See Other to the page of what it made (a script gets that
page's JSON with 200), or 409 saying why it wasn't done.
| POST | What it does |
|---|---|
/pipelines/<name>/runs | start a run over partitions |
/pipelines/<name>/backfill | queue a run per partition that has none; rerun=1 for done ones too |
/pipelines/<name>/cancel | cancel its queued, running and retrying runs over partitions |
/pipelines/<name>/pause, /unpause | stop the scheduler starting its runs, or let it again |
/runs/<id>/resume | queue a failed, cancelled or stalled run again; allow_new_code=1 confirms new code |
/runs/<id>/rerun | make a new run from step on, with the steps before it copied |
/runs/<id>/cancel | cancel a run before its next attempt |
/runs/bulk | cancel, retry or backfill the runs picked from a list, or all its filters match |
/datasets/<name>/rebuild | rebuild every pipeline downstream over partitions |
/watchdog/<rule>/snooze | silence a watchdog rule for duration (2h, 30m, 1d; off ends it) |
/watchdog/<trip>/ack, /watchdog/all/ack | acknowledge a watchdog trip, or all of them |
Logging in
| Path | What it does |
|---|---|
/login | GET is the form; POST name, password and next opens a session in the orca_session cookie |
/logout | ends the session and clears the cookie |
/api/login | JSON name, password, label and days gives an API token, for orca login |
/api/logout | revokes the API token it's sent with |
The worker API
What orca worker and the runs it starts call: each a POST of
JSON with Authorization: Bearer <worker token>. The token must be a
live worker token (else 401), the body a JSON object (400), and a pool it names the
token's (403). With ORCA_WORKER_ADDR set, the UI also serves these, and
only these, on that address from a thread of their own, so a slow page can't hold up
claims and heartbeats. See Remote workers and
api/worker.mojo.
| POST | What it does |
|---|---|
/api/worker/claim | the worker's heartbeat; with slots above 0 it also asks for a queued run of its pool, answered with the run's spec or null |
/api/worker/release | hands back a run it was given and won't start, which is queued again and held saying why |
/api/worker/exited | a run's process ended, with its samples |
/api/worker/cluster | reports the dista head's address and token of a run this worker started as a cluster |
/api/worker/left | a cluster member's process ended |
/api/worker/boot | a pool machine booting gets the image, the login to pull it and the bucket's variables |
/api/worker/store/<call> | a run's store calls (its steps, attempts, reads, commits and heartbeat), in place of Postgres |
Logs on disk and in object storage
With ORCA_LOGS set to a directory or s3://bucket/prefix,
everything a run leaves is under <ORCA_LOGS>/<pipeline>/<run>/.
Without it, logs go to standard output only. An object can't be appended to, so a log
bound for object storage is written under ORCA_LOG_STAGE and uploaded
when it's complete. Secrets named in ORCA_SECRETS are masked as
*** in every log morca writes, and each attempt's row records where its log
ended up.
| Path under ORCA_LOGS | What it holds |
|---|---|
<pipeline>/<run>/<step>/<partition>/<attempt>.log | an attempt's start, end, error and ctx.log lines with UTC timestamps; the partition is _run for a gather step, and a key's / is %2F |
<pipeline>/<run>/timeline.json | a run with Dista tasks: the runtime's timeline in Chrome's trace format, written when the run ends |
<pipeline>/<run>/driver.log | the run's driver output, kept by the scheduler, or by the driver pod's log on Kubernetes |
<pipeline>/<run>/cluster/<role>-<instance>.log | an EC2 run's head or worker log, uploaded every ORCA_CLUSTER_LOG_SECONDS while it runs |
<pipeline>/<run>/metrics/local.jsonl | the machine's samples for a run on the local runner |
<pipeline>/<run>/metrics/<role>-<instance>.jsonl | each EC2 run instance's samples |
<pipeline>/<run>/metrics/<pod>.jsonl | each Kubernetes pod's samples, taken out of its log |
<pipeline>/<run>/metrics/worker-<name>.jsonl | a remote worker's samples beside each run it starts |
A control-placed run's driver output is staged under
ORCA_LOG_STAGE/control/ before it's appended to
driver.log. On a remote worker, the logs of a cluster's head and
members stay in the worker's --dir.
Metrics lines
<binary> sample runs beside every process doing a run's work and
appends a JSON line every ORCA_SAMPLE_SECONDS (5). Readers ignore keys
they don't know, since fields may be added. The run page's Machines section draws
these files.
{"v": 1, "t": 1762250000.123, "dt": 5.0, "host": "ip-10-0-1-5", "cores": 4,
"cpu": {"user": 61.2, "system": 4.1, "iowait": 0.3, "steal": 0.0, "busiest": 99.0},
"mem": {"total": 8053063680, "used": 3221225472, "available": 4831838208,
"cache": 2147483648, "swap_total": 0, "swap_used": 0, "rss": 2952790016},
"disk": [{"path": "/tmp", "total": 53687091200, "used": 9663676416, "free": 44023414784}],
"io": {"read": 1048576, "write": 52428800}, "net": {"in": 1310720, "out": 655360},
"self_cpu": 0.42} | Field | Meaning |
|---|---|
v | the format's version, 1; it changes only if a field's meaning does |
t, dt | the Unix time, and the seconds since the previous line |
host, cores | the machine's name and its core count |
cpu | user, system, iowait and steal as percentages of all cores' time over dt, and busiest, the busiest core's |
mem | bytes: total, used, available, cache (page cache), swap_total, swap_used, and rss of the run's own processes |
disk | per file system the run writes to: path, total, used and free bytes |
io, net | disk read and write, and network in and out, in bytes per second over dt; io is null on macOS |
containers | on the control host only: each Docker container's name and memory |
self_cpu | the sampler's own CPU seconds so far |
The sampler's options and platform differences are in the docstring of sample.mojo.
The Postgres schema
With ORCA_DATABASE_URL set, every orca binary creates and migrates the
orca schema at start-up. Each change of a run's state is one
transaction. morca 0.23 has 54 migrations, and 0.24.0 adds a 55th
(orca.pipelines.fill_max_runs_auto). A sqlite: database
holds the core run tables only.
| Table (orca.*) | What it holds |
|---|---|
pipelines | each registered pipeline: its registry hash, describe JSON, config and overrides, env, max_runs, keys_per_run and learned request |
pipeline_versions | each code version a pipeline was registered with: hash, commit, dirty flag and image |
runs | each run: pipeline, partitions, trigger, status, owner and heartbeat, request and grant, spec and cluster head |
steps | each step of each partition of a run, with its input, output, status and error |
attempts | each attempt of a step: host, code, start and end, error, log location and peaks |
reads, commits | the snapshot of each dataset an attempt read or committed, which lineage walks |
fires | one row per (pipeline, time) the schedule fired, with its run, so nothing fires twice |
sensed | each upstream snapshot a sensor saw, with the run it queued |
keys | a keyed pipeline's keys, as last read from its dataset's snapshot |
rebuilds, rebuild_pipelines | each rebuild of a dataset, and its pipelines by level with their runs |
users, sessions, api_tokens | UI users, their browser sessions, and the API tokens orca login issues |
audit | who did what from the UI or the remote CLI |
instances | each EC2 instance a run used: role, type, market, launch and end, and why it ended |
fleet_samples, fleet_hourly | machine samples per host, and hourly totals per role for Ops › Pools' charts |
system_minutes, system_containers | the control host's minute-by-minute health, and each container's memory |
watchdog_rules, watchdog_trips | each rule's last value, threshold and snooze, and each trip with the pipelines it paused |
workers | each worker token: pool, label, expiry or revocation, and the machine's last heartbeat, CPUs, memory and runs |
notifications, alerts | ntfy delivery state per alert subject, and every alert sent, for Ops › Alerts |
pressure | each host's latest memory pressure reading and whether it holds runs |
schedulers | each scheduler process: host, image, role (leading, waiting, draining) and heartbeat, for overlapping deploys |
pools, pool_machines | the orca.toml pools with what the autoscaler is trying, and each machine it made |
stack_identity | this stack's id, the label that marks its pool machines |
run_members | the workers that joined each start of a run spread over a pool, with their grants |
schema_version | the migrations applied, and the fewest an older binary must know to run on them |
Additive migrations
A binary older than the database runs on it if every migration it doesn't know is
additive, which is what makes deploy.sh --rollback safe. So a migration
may add tables, indexes and nullable foreign keys, and columns that are nullable or
have a default. It may not rename, drop or retype a column or table, tighten a
constraint, or write a value an older binary would act on. A migration that must is
listed as raising the floor: orca.schema_version records it, and a binary
that doesn't know it refuses to start. Today only migrations 1 and 24 raise it.
Queries name the columns they read and write, so new columns never break old binaries.
The rules are in migrations.mojo.
Environment variables
Settings for the whole stack are environment variables, in
/etc/orca.env on a deployed stack; settings for one pipeline belong in
orca.toml's env. Families with many members
(ORCA_WATCHDOG_*, ORCA_NOTIFY_*, ORCA_EC2_*,
ORCA_POOL_*) are listed in full in
watchdog.md,
alerts.md,
Scheduling and running and
orca.toml.
The scheduler
| Variable | What it sets | Default |
|---|---|---|
ORCA_DATABASE_URL | the Postgres database, or sqlite:<path> for one machine | unset: in memory |
ORCA_RUNNER | where the scheduler starts runs: local, kubernetes or ec2 | local |
ORCA_LOGS, ORCA_LOG_STAGE | where logs and metrics go (a directory or s3://), and where object-store logs are staged | unset, $TMPDIR/orca-logs |
ORCA_WAREHOUSE | the root of the filesystem catalog, a directory or s3:// | unset |
ORCA_STALE_SECONDS, ORCA_START_SECONDS | how old a heartbeat is before a run counts as dead, and the grace before its first beat | 30; start: 30, 300 on EC2 and Kubernetes |
ORCA_RETRY_SECONDS, ORCA_SENSE_SECONDS | the first wait before a retry (doubled each start), and how often sensors are asked | 60, 60 |
ORCA_CONFIG, ORCA_PIPELINES_DIR | the orca.toml orca register reads, and where it finds the binaries | ./orca.toml, pipelines/ |
ORCA_COMMIT, ORCA_SOURCE, ORCA_DIRTY | the commit an image was built from, and its source, recorded with a registration | unset |
ORCA_CONTROL_MAX_RUNS, _MEMORY_MB, _NICE, _WORKERS | the limits of control-placed runs | 2, 1024, 10, 1 |
ORCA_HOST_CPUS, ORCA_HOST_MEMORY_MB | what this host can grant, over what morca reads | the host's |
ORCA_RESERVE_CPUS, ORCA_RESERVE_MEMORY_MB | kept back from grants | 1, 1536 (a worker: 0, 512) |
ORCA_ADMIT_MEMORY_PRESSURE, ORCA_ADMIT_PRESSURE_GRACE_SECONDS | the PSI limit over which no run starts, and how long a hold lasts with no runs going | 10, 600 |
ORCA_CGROUPS | 0 turns off a cgroup per run | on where possible |
ORCA_RUN_MEMORY_MB, _MARGIN, _GROWTH, _HELD_SECONDS | a run's memory limit without a request, the kill margin, the growth after a kill, and how long it may be held at its limit | host's, 25, 1.5, 120 |
ORCA_RUN_ENV | the scheduler's variables, by name, copied to every run | unset |
ORCA_VCPU_BUDGET, ORCA_RUN_DEADLINE | the vCPUs live runs' machines may use together, and the seconds after which they're removed | no limit, 86400 |
ORCA_EC2_* | head and worker types, AMI, subnet, security group, profile, database URL, preamble, mode and the warm pool's ORCA_EC2_POOL_SECONDS | c7g.large, c7g.xlarge, 150 |
ORCA_SCALE_UP_AFTER, ORCA_SCALE_IDLE, ORCA_SCALE_STEP | how an EC2 run with workers = MIN..MAX adds and drains workers | 5, 60, 4 |
ORCA_RUNS_NAMESPACE | the Kubernetes namespace runs' pods go in | orca-runs |
ORCA_SAMPLE_SECONDS, ORCA_SAMPLE_UPLOAD_SECONDS | the sampler's interval (0 turns it off), and how often an object-store metrics file is uploaded | 5, 30 |
ORCA_CLUSTER_LOG_SECONDS | how often an EC2 run instance uploads its log | 30 |
ORCA_BINARY_SECONDS | how long a sensor's binary may take to answer | 300 |
ORCA_EXPIRE_HOURS | how often every dataset is expired by its own policy; 0 turns it off | 24 |
ORCA_OVERLAP, ORCA_DRAIN_SECONDS | on lets a new scheduler wait to lead while the old one drains, for at most the drain time | off, 3600 |
ORCA_WATCHDOG_SECONDS, ORCA_WATCHDOG_* | how often the watchdog checks, and each rule's threshold | 60 |
ORCA_NOTIFY_URL, ORCA_NOTIFY_* | the ntfy topic alerts go to, its token, and when each alert fires | unset: no alerts |
ORCA_SECRET_FETCH | a command that prints a secret by id, in place of Secrets Manager | unset |
ORCA_POOL_*, ORCA_HCLOUD_* | the pool autoscaler's settings and Hetzner's API | see orca.toml |
ORCA_NOW, ORCA_ZONEINFO | a fixed clock for tests, and where time zones are read from | the clock, /usr/share/zoneinfo |
The UI
| Variable | What it sets | Default |
|---|---|---|
ORCA_UI_ADDR | host:port orca ui listens on | 127.0.0.1:8270 |
ORCA_WORKER_ADDR | a second host:port serving only the worker API, from its own thread | unset |
ORCA_UI_LOGIN | required makes actions need a login before any user exists | on once a user exists |
ORCA_UI_SESSION_HOURS, ORCA_API_TOKEN_DAYS | how long a login lasts, and an API token | 12, 30 |
ORCA_UI_SECURE_COOKIES, ORCA_UI_TRUST_PROXY | 1 marks the cookie Secure, and 1 trusts X-Forwarded-For | unset |
ORCA_UI_URL | the UI's public address, for links in alerts and for pool machines | unset |
ORCA_UI_LINKS | extra nav links after Docs, as label=url entries separated by commas | unset |
ORCA_UI_DRAIN_SECONDS | how long the UI finishes requests after SIGTERM | 8 |
ORCA_DOCS_URL | where the UI's Docs links point | mlake.dev's docs |
ORCA_FIXED_COST | on the local runner, what the Cost tile shows | unset |
ORCA_WORKER_TOKEN_DAYS | the longest a worker token lasts | 365 |
Workers and the remote CLI
A worker also reads ORCA_WAREHOUSE, ORCA_LOGS,
ORCA_SECRET_FETCH, the reserve and pressure variables, and refuses to
start with ORCA_DATABASE_URL set.
| Variable | What it sets | Default |
|---|---|---|
ORCA_REMOTE (or ORCA_URL) | the UI a worker, or orca --remote, talks to | unset |
ORCA_WORKER_TOKEN | the worker's token, from orca worker-token create | unset |
ORCA_WORKER_ADVERTISE | the address the pool's machines reach this worker at, which a run's cluster needs | unset: no clusters |
ORCA_IMAGE | the worker's version, which must match a pipeline registered with --image | unset |
ORCA_CREDENTIALS | where orca login keeps its API tokens | ~/.config/orca/credentials |
What morca sets for a run
A pipeline's env can't set these; orca does, for the run's process.
| Variable | What it holds |
|---|---|
ORCA_RUN | the run's id, for a run a worker or an EC2 instance starts |
ORCA_GRANT_CPUS, ORCA_GRANT_MEMORY_MB | the run's grant for this start |
ORCA_MEMORY_PRESSURE_FILE | the PSI file its host's pressure is read from |
ORCA_SECRETS | the names of the variables holding the run's secrets, which logs mask |
ORCA_WORKERS | the local Dista workers the run forks (default 4) |
ORCA_STATE_URL, ORCA_STATE_TOKEN | on a worker, the UI's store API and token the run records itself through, in place of a database |
ORCA_WORKER_PID | the worker that started the run, whose exit stops it |
ORCA_DB_RETRY_SECONDS | how long a run's database or store calls retry (default ORCA_STALE_SECONDS) |
Dista variables morca sets
The runners start a run's Dista head and workers from the pipeline's own binary, telling each what it is with these.
| Variable | What it holds |
|---|---|
DISTA_ROLE | head or worker: the binary runs as that Dista process |
DISTA_HEAD | host:port of the head a Dista worker joins |
DISTA_PORT | the port a head listens on: 6390 on Kubernetes, a free port on a worker |
DISTA_ADVERTISE | the address a Dista worker is reached at by its head |
DISTA_TOKEN | a run's cluster token, without which Dista refuses a connection; set on a worker pool's clusters |
DISTA_WORKER_SLOTS | slots per Dista worker; "auto" in env becomes the granted CPUs over the run's workers, else 0, one per core |
DISTA_CONNECT_TIMEOUT | set to 10 for a cluster member, so it gives up on a head that's gone |