Skip to content
Docs menu

runs

Training runs — submitted here, executed by an executor: a self-hosted worker (M7-RL) or Modal (modal, not built yet)

14 operations · 48 schemas

GET /v1/runs

List training runs

listRuns · scope key:read

Runs visible to the caller (created by them or owned by an org they belong to), newest first. A self-hosted runner discovers its work here with status=queued&executor=worker (M7-RL).

Parameters

listRuns parameters
NameInTypeDescription
statusqueryJobStatus
executorqueryRunExecutor

Filter by executor (worker | modal).

ownerqueryHandle

Filter by billing owner handle (username or org slug).

kindqueryRunKind

UI2 — filter by kind: external (imported), orchestration (a plan's run), cloud (executed; narrow further with executor).

robotquerystring

length ≤ 128

UI2 — filter by the robot the recipe names (owner/slug, exact), or the literal none for runs whose recipe names no robot.

gatedqueryboolean

UI2 — true: only runs that carry a scorecard (gate non-null); false: only runs without one.

parent_run_idqueryId

UI2 — the direct forks of one run (its lineage children).

facetsqueryboolean

default false

UI2 — true adds facets to the page: how the caller's whole visible set (this request's filters NOT applied) splits by kind, executor, status, robot and gate, so a filter rail can print a count beside every choice. Default false; the CLI never pays for it.

limitqueryinteger

default 20 · ≥ 1 · ≤ 100

Page size.

cursorquerystring

length ≤ 512

Opaque cursor from the previous page's next_cursor.

Responses

listRuns responses
StatusDescriptionBody
200

Page of runs.

RunPage
401

Missing or invalid credentials.

Problemapplication/problem+json

POST /v1/runs

Create a training run

createRun · scope key:train

Queues a training run from a recipe. The API validates the recipe envelope and the dataset ref URI format (lucen://, hf://, github://); external ref resolution happens in the executor. owner defaults to the caller; pass an org slug to bill an org. parent_run_id records lineage (fork / retrain attribution) and is what the coach's resolved-config diff runs against.

Executor (M7-RL). recipe.executor picks the backend; absent means worker. A worker run is recorded as queued and stays there until a self-hosted runner (lucen worker run, holding a train key for this owner) claims it — the hub does not track connected runners, so a run with no runner simply waits. A modal run (W13) is recorded queued and the hub then spawns the template's deployed Modal function on the tier's GPU (modal_call_id / dispatched_at on the run); the container claims and reports like a runner. On a deployment without MODAL_APP_NAME the submit answers 503 naming it rather than recording a run that would never start; a spawn that fails after the commit leaves the run failed with the reason, never queued forever.

Idempotency. Send an Idempotency-Key header to make retries safe: per credential, the same key with the same body within 24 h returns the run created the first time (200, same id); the same key with a different body is 422.

GPU-hour quota (M7-RL, L0). The GPU hours an account may reserve on priced executors (modal) are capped: the sum of max_hours (service default 4) over the priced runs the user created in the last 24 h that are not canceled — through ANY credential, a session, any key, an agent session's keys — plus this run, must stay within the deployment's RUN_DAILY_GPU_HOUR_QUOTA (default 24 h), else 429 with Retry-After, X-GPU-Quota-Limit / -Used and a problem body naming the numbers. An API key's own daily_gpu_hour_quota, when set, narrows that further for the runs the key created. worker runs are unpriced and never count.

Planned templates (M7-RL ③). A template that the hardware planner knows but no executor implements yet (pi0_lora, groot_lora, openvla_lora) is 503 naming the missing template, on every executor, and nothing is recorded — POST /v1/runs/recommend still answers for it. Ask recommend before create: it picks the GPU tier from the model family, the method and the dataset's shape.

Isaac Lab on Modal (C1). ppo_isaac with executor: modal runs the owner's Isaac Lab task on a hosted L40S (the default when recipe.gpu is null) or L4 / A10G. NVIDIA's Isaac Sim licence covers a company training its own robots, not a service training other people's, so the hosted template is 403 for any owner not in the deployment's ISAAC_HOSTED_OWNERS (default lucen), naming the reason; a worker run of ppo_isaac (the owner's own box) is never restricted. Isaac Sim renders through RTX, so gpu: a100 | h100 is 422 for it. recipe.parent_checkpoint_step starts a child from a parent's published checkpoint (422 if the parent does not list that step). A modal run that Modal preempts is re-claimed by its own call and resumes from its last published checkpoint (claimRun).

Hosted execution (L0). There is no payment system, so executor: modal runs only for owners in the deployment's HOSTED_OWNERS (default lucen; * = everyone): anyone else is 403 with errors[].type: forbidden.hosted_owner, a sentence naming the setting and the self-hosted way, and nothing recorded or dispatched. A worker run is never restricted, but a recipe.gate whose rollouts would be hosted is asked the same question. On a deployment with no Modal app the 503 answers instead.

Parameters

createRun parameters
NameInTypeDescription
Idempotency-Keyheaderstring

length 1–255

Client-chosen key that makes a create retry-safe. Scoped to the credential (API key, or session user): within 24 h the same key with the same body returns the original resource with 200; the same key with a different body is 422. Keys are opaque to the hub; a UUID is a fine choice.

Request body

application/json · required · RunCreate

createRun request body
FieldTypeDescription
reciperequiredRecipe
ownerHandle | null

Billing owner handle; defaults to the caller's username.

parent_run_idstring | null

Lineage — the run this one forks/retrains from.

Responses

createRun responses
StatusDescriptionBody
200

Replay — this Idempotency-Key already created a run with this body; that run is returned unchanged.

Run
201

Run queued.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json
429

The account's (or the key's own) daily GPU-hour quota would be exceeded (priced executors only). Retry-After says when hours free up; nothing was recorded.

Problemapplication/problem+json
503

The requested executor cannot run on this deployment (modal with no MODAL_APP_NAME, or a template no deployed function carries), or the template is registered for planning only (pi0_lora, groot_lora, openvla_lora — M7-RL ③). Nothing was recorded.

Problemapplication/problem+json

POST /v1/runs/import

Import a training run trained off-platform

importRun · scope key:write

Records a run that trained somewhere else (kind: external) so the Training Coach can read it and so it appears on /runs beside cloud runs. The three artifacts — resolved config, metrics summary, scorecard — are sent inline and stored as objects; parent_run_id records lineage, and is what makes the coach's resolved-config diff (its first piece of attribution evidence) possible. Importing spends no GPU dollars, so this is key:write, not key:train. Imported runs have no live logs: logs_key is always null.

Request body

application/json · required · RunImport

importRun request body
FieldTypeDescription
artifactsrequiredRunImportArtifacts
ownerHandle | null

Owner handle; defaults to the caller's username.

parent_run_idId | null

Lineage. Set it whenever one exists — the coach's first piece of attribution evidence is this run's resolved config diffed against the parent's.

templatestring | null

length ≤ 64

What produced the run, free text. Defaults to external.

datasetarray of DatasetRef

items ≤ 16

Dataset refs the run trained on, if any. A simulator-only RL run legitimately has none.

robotstring | null

length ≤ 128

statusJobStatus
hyperparamsobject | null

Responses

importRun responses
StatusDescriptionBody
201

Run imported.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
413

An artifact is over the per-artifact size limit, or the owner's storage quota would be exceeded (L0: imports count toward it).

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json

POST /v1/runs/estimate

Price a training run before submitting it

estimateRun · scope key:train

The same body as createRun, validated the same way (owner, dataset refs, parent), and nothing is created: the answer is the most the run can cost — the tier's billed hourly rate × the wall-clock cap (recipe.max_hours, else the service default of 4 h), then capped by recipe.budget_usd when one is set. Rates are the ones GET /v1/hardware_tiers publishes. A worker run executes on the owner's own machine and costs the hub nothing, so its estimate is cost_usd_max: 0, priced: false — the same rule the coach applies to an unknown model id. A modal estimate is priced whether or not this deployment can run it: the price list exists on every deployment, the backend only where MODAL_APP_NAME is set. (M7-RL, W13) C1: the rate is the tier's plus host_rate_usd_per_hour (the CPU cores and memory ppo_isaac's function reserves), and a hosted ppo_isaac estimate for an owner outside ISAAC_HOSTED_OWNERS is the same 403 createRun answers — a price for something the owner may not buy would be a false answer.

Hosted execution (L0). A modal estimate for an owner outside HOSTED_OWNERS is the same 403 (forbidden.hosted_owner) createRun answers.

Request body

application/json · required · RunCreate

estimateRun request body
FieldTypeDescription
reciperequiredRecipe
ownerHandle | null

Billing owner handle; defaults to the caller's username.

parent_run_idstring | null

Lineage — the run this one forks/retrains from.

Responses

estimateRun responses
StatusDescriptionBody
200

The estimate.

RunEstimate
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json

POST /v1/runs/recommend

Recommend a GPU tier for a training run (deterministic planner)

recommendRun · scope key:read

The same body as createRun, validated the same way, and nothing is created: the answer is which hardware_tiers entry the run needs. Deterministic — no model is in this path. The hub looks the recipe's template up in its training_policies registry (model family, parameter count, method lora | full | rl, control class, per-sample activation cost, published requirements — each row carries the URL it was read from), reads the first lucen:// dataset ref's meta/info.json with the episode viewer's parser (cameras and their resolution, fps, episodes, frames, state and action width), and computes vram_estimate_gb = runtime floor + params × bytes × (lora ? 1.6 : 6) + samples × activations(cameras, resolution, action chunk), where samples is hyperparams.batch_size for imitation learning and hyperparams.num_envs for RL. A published floor (GR00T: "40 GB+ VRAM") raises the estimate when the arithmetic lands below it. The smallest tier whose VRAM covers the estimate plus 20 % headroom is tier; every tier that does is fits_on; est_hours comes from the registry's reference throughput scaled by the tier's relative BF16 tensor throughput, and cost_usd_est / cost_usd_max from the tier's billed rate (a worker recipe is unpriced, and the tier then names the GPU class the owner's own machine needs). Measured beats modelled: every succeeded reportRun that carried peak_vram_gb / samples_per_s updates a rolling value per (template, tier, batch size), and when one exists for this recipe's batch size it replaces the arithmetic — basis says which was used, and why[] shows the sums either way. A template registered for planning only (implemented: false) is answered here and refused by createRun with 503. An unknown template is 422 naming the registered ones; a dataset ref the caller cannot read, or that is not a LeRobotDataset, is a warning and the registry's defaults are used — never a 404 (a planner must not be an existence oracle). explanation is reserved for M16 and is always null today. (M7-RL ③)

Request body

application/json · required · RunCreate

recommendRun request body
FieldTypeDescription
reciperequiredRecipe
ownerHandle | null

Billing owner handle; defaults to the caller's username.

parent_run_idstring | null

Lineage — the run this one forks/retrains from.

Responses

recommendRun responses
StatusDescriptionBody
200

The recommendation, with its arithmetic.

RunRecommendation
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json

GET /v1/runs/{run_id}

Get a training run

getRun · scope key:read

Parameters

getRun parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Responses

getRun responses
StatusDescriptionBody
200

The run.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json

GET /v1/runs/{run_id}/logs

Stream run logs (SSE)

streamRunLogs · scope key:read

Server-Sent Events stream (text/event-stream). Each event is event: log with a JSON LogEvent payload ({ts, stream, line}). Replays existing logs from tail; with follow=true the stream stays open while the run is active, then closes with event: end.

What is on the wire (M7-RL). The log is the sequence of chunks a runner sent through reportRun, one object per report under logs_key; the stream emits one log event per line, ts being the chunk's arrival time (a runner batches ~5 s of output per report, so lines inside one chunk share a stamp) and stream being stdout for the runner's output and stderr for a notice the hub wrote into the log itself (a lease re-queue, a cancel request). The same stream also carries event: metric — one MetricEvent per metrics.jsonl line, all of them, unaffected by tail — so a client draws the loss curve from the same connection. event: end always closes the stream, with the run's status at that moment as its payload; with follow=true it comes once the run is terminal, otherwise right after the replay. An imported (external) run has no log and answers end at once. Same visibility as getRun: a run you may not see is 404.

Parameters

streamRunLogs parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

followqueryboolean

default false

Keep the stream open while the run is queued/running.

tailqueryinteger

≥ 0

Replay only the last N log lines (default = all). Metric events are never tailed.

strip_ansiqueryboolean

default false

L1 — remove ANSI escape sequences (colour, bold, cursor control) from every log line before it is sent. Off by default: the log is the trainer's bytes, and a terminal pane renders rsl_rl's bold header from them. An agent reading text sets it (the MCP get_run_logs tool does).

Responses

streamRunLogs responses
StatusDescriptionBody
200

SSE log stream.

stringtext/event-stream
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json

GET /v1/runs/{run_id}/logs/download

Download a run's whole log as text

downloadRunLogs · scope key:read

L1 — every log chunk of the run concatenated, in arrival order, as one text/plain; charset=utf-8 body (the Terminal tab's Download button, or curl … > run.log): exactly the bytes the runner reported and the hub's own [lucen <iso>] … notices, nothing added between chunks, in the order streamRunLogs replays them. ANSI SGR sequences (rsl_rl's bold ESC[1m header) are kept unless strip_ansi=true. Sent with Content-Disposition: attachment; filename="<run_id>.log" and streamed chunk by chunk, so a long run's log never sits in the API's memory. Same visibility as getRun (404 when invisible); a run with no log yet (queued, or imported) answers an empty body.

Parameters

downloadRunLogs parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

strip_ansiqueryboolean

default false

L1 — remove ANSI escape sequences (colour, bold, cursor control) from every log line before it is sent. Off by default: the log is the trainer's bytes, and a terminal pane renders rsl_rl's bold header from them. An agent reading text sets it (the MCP get_run_logs tool does).

Responses

downloadRunLogs responses
StatusDescriptionBody
200

The whole log.

stringtext/plain
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json

POST /v1/runs/{run_id}/cancel

Cancel a training run

cancelRun · scope key:train

Requests cancellation. Queued runs cancel immediately; running jobs transition to canceled once the worker acknowledges (billing stops within 60 s). Terminal runs return 409.

Mechanism (M7-RL). A queued run becomes canceled in this request (nothing ran, nothing is billed, no ledger row). A running worker run stays running — there is no process to kill on the hub — and gets cancel_requested_at set; the runner sees that field on its next reportRun response, kills the template and reports status: canceled, which is the terminal report that writes the ledger (cost 0 for a worker run). Until then the response is 202 with the run still running; asking again is idempotent. Anyone who can see the run may cancel it (the owner, an org member, or the runner's own credential). A modal run answers 501 until that backend exists. Writes a run.cancel audit row and a stderr notice into the run's log.

Parameters

cancelRun parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Responses

cancelRun responses
StatusDescriptionBody
202

Cancellation requested; current run snapshot returned.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json
409

State conflict (duplicate handle/slug, wrong repo kind, terminal job, ...).

Problemapplication/problem+json
501

The run's executor cannot cancel on this deployment (modal).

Problemapplication/problem+json

GET /v1/runs/{run_id}/config

A run's resolved config, flattened, and its diff against the parent

getRunConfig · scope key:read

UI2 — what the run page's Config tab and its "Config against the parent" panel read. The resolved config artifact (config_key) is flattened to leaf paths, and when the run declares a parent_run_id whose config the caller can also see, the two are diffed exactly as the coach's prompt builder diffs them (M14b's attribution evidence #1): changed columns listed, unchanged columns counted. Same visibility as getRun (404 when invisible).

L1 — grouped by RL structure, and readable while the run trains. A runner uploads config.yaml on its first progress report after the template wrote it (ppo_isaac: as soon as Isaac Lab dumped params/env.yaml + params/agent.yaml, before runner.learn), so this answers mid-run. groups[] sorts every key and every diff entry into Actions · Contract · Observations · Plant · Rewards · Network · Algorithm · Domain randomization · Curriculum · Terminations · Commands · Sim · Bookkeeping · Other by one rule table (docs/API.md "Config groups") — the same implementation Round.variables and the MCP get_run_config tool count with. When one side is the importer's manifest shape (contract.* / plant.* / lineage.*) and the other Isaac Lab's resolved env.* / agent.*, comparison is cross_shape: only the keys that map one to one (pairs[]) are compared, and the rest of each group is counted as parent_has_no_record / child_has_no_record — never as unchanged.

Parameters

getRunConfig parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Responses

getRunConfig responses
StatusDescriptionBody
200

The flattened config and the diff.

RunConfig
401

Missing or invalid credentials.

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json

GET /v1/runs/{run_id}/checkpoints

List a run's checkpoints

listRunCheckpoints · scope key:read

Checkpoints are ordinary repo files under checkpoints/step_{N}/ in the run's output model repo. This lists them grouped by step, read from that repo's own file rows — there is no separate checkpoint store, and a checkpoint downloads through presignDownload on the repo like any other file. A run with no output repo yet (queued, or running and not past its first checkpoint) answers an empty list with repo: null. (M7-RL) Listed while the run trains (C1): the runner publishes every checkpoints/step_{N}/ the template writes as soon as it is complete — for an ONNX-shaped template it holds exactly one *.onnx stamped with lucen_manifest beside the framework checkpoint (model_{N}.pt for rsl_rl) — and the progress report that first publishes sets output_model_repo, so this list grows during training. Steps appear in order and are never rewritten (content addressing dedupes unchanged bytes). AG1: published_at — when the step's last file landed.

Parameters

listRunCheckpoints parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Responses

listRunCheckpoints responses
StatusDescriptionBody
200

Checkpoints by step, oldest first.

CheckpointList
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json

POST /v1/runs/{run_id}/claim

Claim a queued worker run (self-hosted runner protocol)

claimRun · scope key:train

Compare-and-set queued → running on a run whose executor is worker. Exactly one caller wins; a run that is already claimed, is terminal, or is not a worker run answers 409. Records claimed_by, claimed_at, started_at and the first heartbeat_at, writes an audit row, and returns the run — its recipe, parent_run_id and owner are what a runner needs to assemble the template's inputs. key:train because a runner spends the owner's compute on the owner's behalf; the run must be visible to the credential (own, or an org it belongs to) and an invisible run is 404. The claim records the claiming credential: only that credential may reportRun afterwards (409 for anyone else). Lease: every report renews heartbeat_at; a running run whose heartbeat is older than the hub's lease (RUN_LEASE_SECONDS, default 300 s) is re-queued by a periodic sweep — back to queued, claim fields cleared, partial metrics dropped, a stderr notice in its log — so a dead runner never strands a run, and the dead runner's next report is 409. (M7-RL) Re-claim after preemption (C1). Modal restarts a preempted input in a new container under the same function call. A claim on a running modal run is accepted — not 409 — when modal_call_id equals the run's own and the credential is the one that claimed it; the new claimer's name replaces claimed_by, the heartbeat is renewed, started_at (and so billing) is unchanged, resume_step (a checkpoint step this run already published, or null) is where the template restarts, the lost attempt's metrics past it are dropped, a stderr notice lands in the log and run.reclaim is audited. Every other claim on a non-queued run is still 409.

Parameters

claimRun parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Request body

application/json · required · RunClaim

claimRun request body
FieldTypeDescription
workerrequiredstring

length 1–128

A name for the runner — its hostname, or anything the operator will recognise on the run page. Recorded as claimed_by.

modal_call_idstring | null

length ≤ 64

C1. The Modal function call the claiming container is running (modal.current_function_call_id()). Only meaningful on a re-claim: Modal preempts GPU containers and restarts the same input in a new one, which then claims a run that is already running. The hub accepts that claim when the run is a modal run, this id equals the run's modal_call_id (stored at dispatch) and the credential is the one that claimed it first; anything else is still 409.

resume_stepinteger | null

≥ 0

C1, re-claim only. The checkpoint step the restarted container will resume from — one of the steps listRunCheckpoints already lists for this run (422 otherwise), or null to restart from scratch. The hub drops the lost attempt's metrics.jsonl lines past that step (all of them when null), so the curve has one start, and writes a stderr notice into the log.

Responses

claimRun responses
StatusDescriptionBody
200

Claimed; the run as it now stands.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json
409

State conflict (duplicate handle/slug, wrong repo kind, terminal job, ...).

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json

POST /v1/runs/{run_id}/report

Report progress or a terminal state for a claimed run

reportRun · scope key:train

The runner's one write path after claimRun. The run must be running (409 otherwise). Every field is optional and additive: log_chunk (≤ 256 KiB of UTF-8) is stored as one object under runs/{run_id}/logs/ and logs_key becomes that prefix; metrics lines are appended to runs/{run_id}/metrics.jsonl and the last one is echoed on the run as latest_metrics; config_yaml (the resolved config) and scorecard_json land at config_key / scorecard_key exactly where importRun puts them, so the coach reads an executed run and an imported one the same way; output_model_repo names the model repo the runner published — it must be a model repo the caller can write, else 422; since C1 the runner sends it on the progress report that first publishes a checkpoint, not only on the terminal one. status may only move running → succeeded | failed, or running → canceled after the hub set cancel_requested_at (a canceled nobody asked for is 422); a terminal report sets finished_at, records cost_usd (0 for a worker run — the hub billed nothing), writes the run's ledger entry and an audit row. Every report refreshes heartbeat_at (the lease). The caller must be the credential that claimed the run — another key, or a runner whose run was re-queued after a missed lease, gets 409. Read cancel_requested_at on the returned run: when it is set, stop the template and report canceled. (M7-RL) Measurements (M7-RL ③): peak_vram_gb, samples_per_s and gpu_tier may ride on any report and are echoed on the run; on a succeeded terminal report they also update the hub's rolling measured value for (template, tier, batch size), which is what makes recommendRun prefer measured over modelled next time.

Parameters

reportRun parameters
NameInTypeDescription
run_idrequiredpathId

Training run id (run_...).

Request body

application/json · required · RunReport

reportRun request body
FieldTypeDescription
statusRunReportStatus | null

Omit (or running) for a progress report. succeeded / failed are terminal and may be sent once; canceled is terminal too and is accepted only after cancelRun set cancel_requested_at.

metricsarray of object

items ≤ 1000

Metrics lines to append to metrics.jsonl, oldest first.

log_chunkstring | null

length ≤ 262144

Raw stdout/stderr since the last report, ≤ 256 KiB of UTF-8.

errorstring | null

length ≤ 4000

Failure summary; expected with status: failed.

config_yamlstring | null

The template's RESOLVED config, stored at config_key. Send it once it exists — it is the coach's first attribution evidence. L1: the runner sends it on the first progress report after the template wrote config.yaml (so getRunConfig answers while the run trains), and again at the end only if the file changed; each one replaces the stored object.

scorecard_jsonobject | null

Acceptance scorecard, stored at scorecard_key.

output_model_repostring | null

length ≤ 130

owner/slug of the model repo the runner published.

peak_vram_gbnumber | null

≥ 0

Peak GPU memory the template used, in GB (M7-RL ③). Echoed on the run; on a succeeded terminal report it updates the rolling measured value recommendRun prefers over its own arithmetic.

samples_per_snumber | null

> 0

Training throughput, samples per second (M7-RL ③). Same handling as peak_vram_gb.

gpu_tierstring | null

one of l4 · a10g · l40s · a100 · h100

Which tier class the GPU that ran the template belongs to (M7-RL ③). Absent, the run's own gpu_type is used; absent too, the measurement is filed under other.

progressRunReportProgress | null

L1 — where the run stands, sent on progress reports; each report's object replaces the last and is echoed as Run.progress (with the ETA the hub computes at read time).

Responses

reportRun responses
StatusDescriptionBody
200

Recorded; the run as it now stands.

Run
401

Missing or invalid credentials.

Problemapplication/problem+json
403

Authenticated but not allowed (visibility, membership or scope).

Problemapplication/problem+json
404

Resource not found (or hidden from the caller).

Problemapplication/problem+json
409

State conflict (duplicate handle/slug, wrong repo kind, terminal job, ...).

Problemapplication/problem+json
413

A log chunk, metrics batch or artifact is over its size limit, or what the report would store puts the run owner past their storage quota (L0); nothing was stored.

Problemapplication/problem+json
422

Request failed validation.

Problemapplication/problem+json

GET /v1/hardware_tiers

GPU tiers and what an hour on each costs

listHardwareTiers · scope none

The tiers Recipe.gpu accepts, each with the hourly rate a modal run is billed at. Rates are what you pay, all-in — the hub's margin is already inside them, and estimateRun multiplies exactly these by the wall-clock cap. worker runs are never billed and no tier applies to them. Public, because a price list that needs a key to read is not a price list. (M7-RL) C1 adds l40s, and one template whose function reserves a large CPU host (ppo_isaac) pays that host on top of the tier — estimateRun names it as host_rate_usd_per_hour. L0: hosting says who this deployment runs modal work for at all (HOSTED_OWNERS).

Responses

listHardwareTiers responses
StatusDescriptionBody
200

The tier table.

HardwareTierList
401

Missing or invalid credentials.

Problemapplication/problem+json

Schemas (48)

The schemas these operations reach before any other tag’s do. A type that links elsewhere is rendered on that tag’s page.

RunExecutor

string

one of modal · worker

Where a run executes (M7-RL). worker — a self-hosted runner (lucen worker run) on the owner's own machine claims it; the hub orchestrates and bills nothing. modal — the hub's GPU backend (W13): the run is recorded queued, the hub spawns the template's deployed function on the tier's GPU, and the container claims, reports and publishes through the same routes a runner uses; billed elapsed seconds × tier rate. On a deployment without MODAL_APP_NAME (a laptop, CI) a modal submit answers 503 naming it and records nothing. From M15 the same two names are an inference endpoint's backend: worker is lucen serve on the owner's box (unpriced), modal is a hub-spawned container priced by the tier.

RunKind

string

one of cloud · external · orchestration

default "cloud"

cloud — submitted through POST /v1/runs and executed by an executor; Run.executor says which one (a self-hosted worker, or modal). external — trained off-platform and imported via POST /v1/runs/import; it carries artifacts and never live logs. Added in M14b; absent means cloud. orchestration (M16) — a planner run: no trainer, no GPU; its config_key holds the plan's inputs and its metrics_key the timeline, so parent_run_id lineage and the coach's config diff work on it like on any other run.

RunPage

object

RunPage fields
FieldTypeDescription
itemsrequiredarray of Run
next_cursorrequiredstring | null
facetsRunFacets | null

UI2 — present only when listRuns?facets=true.

RunCreate

object

RunCreate fields
FieldTypeDescription
reciperequiredRecipe
ownerHandle | null

Billing owner handle; defaults to the caller's username.

parent_run_idstring | null

Lineage — the run this one forks/retrains from.

Run

object

Run fields
FieldTypeDescription
idrequiredId
kindRunKind
statusrequiredJobStatus
reciperequiredRecipe
recipe_hashrequiredSha256
ownerrequiredRepoOwner
created_byUserPublic | null
gpu_typestring | null

Resolved GPU type the run is billed on.

cost_usdnumber | null

Accrued cost in USD (4 decimal places).

metrics_keyStorageKey | null

Storage key of the metrics: runs/{id}/metrics.jsonl for an executed run (appended by reportRun), runs/{id}/metrics.txt for an imported one.

logs_keyStorageKey | null

Storage prefix of the log chunks, runs/{id}/logs/ — one object per reportRun that carried a log_chunk, named so that lexical order is arrival order. Always null for an external run — it was not trained here, so there is nothing to stream.

config_keyStorageKey | null

Storage key of the resolved training config (M14b). The coach's attribution evidence #1 is the diff of this against the parent's.

scorecard_keyStorageKey | null

Storage key of the acceptance scorecard JSON (M14b).

gateRunGate | null

UI2 — the scorecard's verdict as two numbers, on every read: how many of its PASS/FAIL rows pass (the same reader the coach uses, gates declared or discovered). Null when the run has no scorecard — a plan's run never has one, an executed run gets one when a sim-gate rollout scores it, an imported run when it was imported with one. Stored when the scorecard is written, so a list page costs no object reads.

gatesarray of RunGateRow | null

UI2 — every PASS/FAIL row of the scorecard, getRun only (null on list pages); read from the scorecard object on each read.

output_model_repoRepoRef | null

Model repo produced on success.

parent_run_idstring | null

Lineage — the run this one forks/retrains from.

executorRunExecutor | null

Which backend runs it (M7-RL). Null for an external run.

claimed_bystring | null

length ≤ 128

The runner name given to claimRun (M7-RL).

claimed_atstring (date-time) | null (date-time)
heartbeat_atstring (date-time) | null (date-time)

Last time the runner claimed or reported (M7-RL).

cancel_requested_atstring (date-time) | null (date-time)

Set by cancelRun on a running run (M7-RL). The run is still running; the runner is expected to stop the template and report status: canceled. Null once the run is terminal by any other route, and always null for a run nobody asked to cancel.

latest_metricsobject | null

The last metrics line the runner reported (M7-RL).

peak_vram_gbnumber | null

Peak GPU memory the template used, in GB, as the runner last reported it (M7-RL ③). Null until a report carries one.

samples_per_snumber | null

Training throughput the runner last reported, samples per second (M7-RL ③). Null until a report carries one.

modal_call_idstring | null

length ≤ 64

The Modal function call executing a modal run (W13), set by the dispatch job after the run was recorded; cancelRun terminates it. Always null for a worker or imported run.

dispatched_atstring (date-time) | null (date-time)

When the hub spawned the modal call (W13) — the moment between queued and the container's claim. Null for a worker run.

errorstring | null

Failure summary when status is failed.

created_atrequiredstring (date-time)
started_atstring (date-time) | null (date-time)
finished_atstring (date-time) | null (date-time)
display_namestring

L1 — what to call the run before (and after) its output repo exists: the name of the campaign round that owns it, else recipe.hyperparams.run_name, else the output model repo's slug, else the template. display_name_source says which.

display_name_sourcestring

one of round · run_name · output_repo · template

progressRunProgress | null

L1 — iteration, total, iterations/s and ETA as the runner last reported them; null until a report carries progress (a queued run, an imported one, a template that declares nothing).

spendRunSpend | null

L1 — what the run has cost so far and what is left of its budget, computed on every read by the function that bills it at the end.

roundRunRound | null

L1 — the campaign round that owns this run (C3), when there is one the caller can see; null otherwise.

sparklineRunSparkline | null

AG1 — the reward curve decimated to at most 64 points, kept by the hub as the runner reports metrics; null until a metrics line carries a reward.

RunImport

object

RunImport fields
FieldTypeDescription
artifactsrequiredRunImportArtifacts
ownerHandle | null

Owner handle; defaults to the caller's username.

parent_run_idId | null

Lineage. Set it whenever one exists — the coach's first piece of attribution evidence is this run's resolved config diffed against the parent's.

templatestring | null

length ≤ 64

What produced the run, free text. Defaults to external.

datasetarray of DatasetRef

items ≤ 16

Dataset refs the run trained on, if any. A simulator-only RL run legitimately has none.

robotstring | null

length ≤ 128

statusJobStatus
hyperparamsobject | null

RunEstimate

object

The most a run can cost, computed before anything is created.

RunEstimate fields
FieldTypeDescription
executorrequiredRunExecutor
tierrequiredstring | null

Resolved GPU tier (recipe.gpu, else l4); null for a worker run, and (F1) for a modal template hosted on CPU (ppo_mujoco), whose rate is its host alone.

rate_usd_per_hourrequirednumber

Billed hourly rate (4 decimal places): the tier's, plus host_rate_usd_per_hour; 0 for a worker run. This is the rate the run is billed at, second by second.

host_rate_usd_per_hournumber

C1. The part of rate_usd_per_hour that pays for the CPU cores and memory the template's Modal function reserves beside the GPU (Modal bills them on top of the GPU): ppo_isaac 8 cores + 32 GiB; F1: ppo_mujoco 6 cores + 4 GiB with no GPU, so there it is the whole rate; 0 for every other template. Always 0 for a worker run.

max_hoursrequirednumber

recipe.max_hours, or the service default of 4.

cost_usd_maxrequirednumber

rate_usd_per_hour × max_hours, then min(budget_usd) when a budget is set.

budget_usdnumber | null

The recipe's hard cap, echoed.

capped_by_budgetboolean

True when budget_usd is what bounds cost_usd_max.

pricedrequiredboolean

False when the executor is not billed by the hub (worker), so a zero is "not charged", not "free GPU time" — the same rule the coach uses for an unknown model id.

RunRecommendation

object

RunRecommendation fields
FieldTypeDescription
templaterequiredstring
model_familyrequiredstring
control_classrequiredControlClass
methodrequiredTrainingMethod
implementedrequiredboolean

False for a planning-only row — createRun answers 503 for it.

executorrequiredRunExecutor
tierrequiredstring | null

The smallest tier that fits with headroom; null when none does.

requested_tierstring | null

recipe.gpu, echoed, so why[] can say whether it fits.

vram_estimate_gbrequirednumber

The estimate itself (modelled or measured), before headroom.

vram_required_gbrequirednumber

vram_estimate_gb × (1 + headroom), or the published floor when higher.

headroomrequirednumber

The safety margin applied (0.2 unless the deployment changed it).

fits_onrequiredarray of string

Every tier whose VRAM covers vram_required_gb, smallest first.

est_hoursrequirednumber | null

Expected wall clock on tier; null when the registry has no throughput reference and no run measured one.

rate_usd_per_hourrequirednumber

The tier's billed rate plus the template's host rate (C1, ppo_isaac); 0 for a worker recipe.

cost_usd_estrequirednumber | null

rate × est_hours; null when est_hours is.

cost_usd_maxrequirednumber

rate × max_hours (service default 4 h), capped by budget_usd — what estimateRun would answer for this tier.

pricedrequiredboolean
basisrequiredRecommendationBasis
measuredrequiredRecommendationMeasured | null
inputsrequiredRecommendationInputs
datasetrequiredRecommendationDataset | null
whyrequiredarray of string

The arithmetic, one step per line, with the source URL of every published number.

warningsrequiredarray of string
explanationrequiredstring | null

Reserved for M16 (a model's prose over this plan). Always null in v0.

RunConfig

object

UI2 — a run's resolved config, flattened, and its diff against the parent when there is one.

RunConfig fields
FieldTypeDescription
run_idrequiredId
parent_run_idrequiredstring | null
keysrequiredarray of RunConfigKey

Every leaf of this run's resolved config, sorted by path. Empty when the run has no readable config.

totalrequiredinteger

≥ 0

keys.length.

diffrequiredRunConfigDiff | null

Null when there is no parent, or one side has no parseable config — reason says which.

reasonrequiredstring | null

Why keys is empty or diff is null, in one sentence; null when nothing is missing.

shapestring

one of isaac · manifest · other · none

L1 — this run's config shape: isaac (Isaac Lab's resolved env.* / agent.*, ppo_isaac), manifest (a stamped contract — contract.* / plant.*, what the importer and the stub templates write), other, or none without a readable config.

parent_shapestring | null

one of isaac · manifest · other · none

The parent's config shape; null without a visible parent.

comparisonstring | null

one of same_shape · cross_shape

How the diff was computed (RunConfigDiff.comparison); null without a diff.

groupsarray of RunConfigGroup

L1 — the keys and the diff by RL structure, in the rule table's order, every group listed (a group with nothing in it on either side has total: 0), so a page can say what an imported manifest does not carry.

pairsarray of RunConfigPair

cross_shape only: the key pairs compared. [] otherwise.

declaredarray of RunConfigDeclared

The owning round's declared keys and what the diff says about each; [] off a round.

CheckpointList

object

CheckpointList fields
FieldTypeDescription
itemsrequiredarray of RunCheckpoint
reporequiredRepoRef | null

The output model repo the checkpoints live in; null when none exists yet.

not_fine_tunablestring | null

WA — why this run cannot be a fine-tune's parent, in one sentence: set when the run has ended (or was imported) and published no checkpoints/step_N/. The sentence is RoundPreview.not_fine_tunable's (K3), readable here without a campaign, so a page can say it before any form exists. Null while the run may still publish one, and whenever items is not empty.

RunClaim

object

RunClaim fields
FieldTypeDescription
workerrequiredstring

length 1–128

A name for the runner — its hostname, or anything the operator will recognise on the run page. Recorded as claimed_by.

modal_call_idstring | null

length ≤ 64

C1. The Modal function call the claiming container is running (modal.current_function_call_id()). Only meaningful on a re-claim: Modal preempts GPU containers and restarts the same input in a new one, which then claims a run that is already running. The hub accepts that claim when the run is a modal run, this id equals the run's modal_call_id (stored at dispatch) and the credential is the one that claimed it first; anything else is still 409.

resume_stepinteger | null

≥ 0

C1, re-claim only. The checkpoint step the restarted container will resume from — one of the steps listRunCheckpoints already lists for this run (422 otherwise), or null to restart from scratch. The hub drops the lost attempt's metrics.jsonl lines past that step (all of them when null), so the curve has one start, and writes a stderr notice into the log.

RunReport

object

One progress or terminal report from a runner. Every field is optional.

RunReport fields
FieldTypeDescription
statusRunReportStatus | null

Omit (or running) for a progress report. succeeded / failed are terminal and may be sent once; canceled is terminal too and is accepted only after cancelRun set cancel_requested_at.

metricsarray of object

items ≤ 1000

Metrics lines to append to metrics.jsonl, oldest first.

log_chunkstring | null

length ≤ 262144

Raw stdout/stderr since the last report, ≤ 256 KiB of UTF-8.

errorstring | null

length ≤ 4000

Failure summary; expected with status: failed.

config_yamlstring | null

The template's RESOLVED config, stored at config_key. Send it once it exists — it is the coach's first attribution evidence. L1: the runner sends it on the first progress report after the template wrote config.yaml (so getRunConfig answers while the run trains), and again at the end only if the file changed; each one replaces the stored object.

scorecard_jsonobject | null

Acceptance scorecard, stored at scorecard_key.

output_model_repostring | null

length ≤ 130

owner/slug of the model repo the runner published.

peak_vram_gbnumber | null

≥ 0

Peak GPU memory the template used, in GB (M7-RL ③). Echoed on the run; on a succeeded terminal report it updates the rolling measured value recommendRun prefers over its own arithmetic.

samples_per_snumber | null

> 0

Training throughput, samples per second (M7-RL ③). Same handling as peak_vram_gb.

gpu_tierstring | null

one of l4 · a10g · l40s · a100 · h100

Which tier class the GPU that ran the template belongs to (M7-RL ③). Absent, the run's own gpu_type is used; absent too, the measurement is filed under other.

progressRunReportProgress | null

L1 — where the run stands, sent on progress reports; each report's object replaces the last and is echoed as Run.progress (with the ETA the hub computes at read time).

HardwareTierList

object

HardwareTierList fields
FieldTypeDescription
itemsrequiredarray of HardwareTier
hostingHostedExecution

RunFacets

object

UI2 — how the caller's whole visible set splits (the request's own filters are NOT applied, so a rail's counts hold still while you click). Buckets with a count of zero are omitted, except gated, which always carries both true and false.

RunFacets fields
FieldTypeDescription
totalrequiredinteger

≥ 0

Every run the caller can see.

kindrequiredarray of RunFacetBucket

external and orchestration; executed runs are split by executor instead.

executorrequiredarray of RunFacetBucket
statusrequiredarray of RunFacetBucket
robotrequiredarray of RunFacetBucket

By recipe.robot; none for runs whose recipe names no robot.

gatedrequiredarray of RunFacetBucket

true (carries a scorecard) and false.

Recipe

object

Training recipe envelope. template names a training template; dataset lists dataset ref URIs; hyperparams passes through to the template untouched. executor and budget_usd were added in M7-RL, additively.

Recipe fields
FieldTypeDescription
templaterequiredstring

length 1–64

Which trainer runs. Free-form so an imported run can name whatever produced it (external, isaac-lab-ppo); the ids the platform knows are ppo_isaac (the Isaac Lab PPO stack behind the published Laika policies — on a self-hosted worker, or since C1 hosted on modal for owners in the deployment's ISAAC_HOSTED_OWNERS allow-list, NVIDIA's licence permitting the hub's own org and nobody else; on the RT-core tiers only), ppo_mujoco (M17b: a real, robot-agnostic PPO trainer on MuJoCo, built into the CLI — lucen-cli[rl] — and driven entirely by the robot card's interface; runs on a worker, or since F1 hosted on modal on a CPU-only container — no GPU is attached or billed, so gpu must be null there) and stub_ppo (a seconds-long stand-in that emits the four artifacts; CI and acceptance use it). M7-RL ③ adds the VLA family: smolvla_lora (LoRA fine-tune of SmolVLA on a LeRobot dataset repo, built into the CLI, control class skill), stub_skill (a seconds-long skill-class stand-in), and the planning-only rows pi0_lora, groot_lora, openvla_lora (recommendRun answers for them; createRun is 503). A runner serves the templates it was started with and leaves the rest queued.

datasetrequiredarray of DatasetRef

items 0–16

robotstring | null

length ≤ 128

The robot this run trains for, as the owner/slug of a robot repo the caller can see. M17b: validated on createRun, estimateRun and recommendRun — anything else is 422, and a private repo answers exactly as a missing one does, so run creation is not an existence oracle. The runner stages the repo's robot_card.json for the template, and for a template that simulates the robot (ppo_mujoco) its MJCF and every asset it names. importRun does not validate it: an imported run records something that already happened and may name whatever it named.

hyperparamsobject | null

Template-specific hyperparameters (opaque in v0).

gpustring | null

one of l4 · a10g · l40s · a100 · h100

GPU type; service default applies when null. l4 is the low-cost tier for small finetunes (ACT/SmolVLA on modest datasets); M7 owns the rate table. l40s (C1) is the 48 GB RT-core card Isaac Sim is hosted on: a modal ppo_isaac run with a null gpu is recorded on l40s, and a100 / h100 (no RT cores) are 422 for it. F1: a modal ppo_mujoco run runs on CPU and records no tier; naming a GPU for it is 422.

max_hoursnumber | null

> 0 · ≤ 48

Hard wall-clock cap; the run is stopped at the limit (a worker runner kills the template and reports failed). Null means the service default, 4 h, which is also what estimateRun prices.

executorRunExecutor | null

Which backend executes the run (M7-RL). Null means worker, the only executor that can run on this deployment today.

budget_usdnumber | null

> 0

Hard spend cap in USD (M7-RL). estimateRun caps cost_usd_max by it; a billed executor stops the run when accrued cost reaches it. For a worker run the hub bills nothing, so the cap is recorded on the recipe and never binds.

gateCheckpointGateSpec | null

Gate checkpoints in simulation while the run trains, and stop it on a tripwire (C2). Needs robot: 422 otherwise.

parent_checkpoint_stepinteger | null

≥ 0

C1. Start this run from the parent run's checkpoint at this step, not from scratch: the runner downloads the parent's checkpoints/step_{N}/ into the template's input/resume/ and writes input/resume.json ({"source": "parent", "run_id", "step", "files"}); a template that can resume (ppo_isaac, stub_ppo) continues from those weights. Requires parent_run_id, and the parent's output model repo must list checkpoints/step_{N}/ — anything else is 422 on createRun and estimateRun, so a typo is refused before a GPU starts. Null (the default) keeps M7-RL's behaviour: the child inherits the parent's contract and trains from scratch.

RunGate

object

UI2 — a scorecard's PASS/FAIL rows, counted.

RunGate fields
FieldTypeDescription
passedrequiredinteger

≥ 0

totalrequiredinteger

≥ 0

Zero when the scorecard states no PASS/FAIL row the reader recognises.

RunGateRow

object

UI2 — one PASS/FAIL row of a run's scorecard, as coach/gates.py reads it.

RunGateRow fields
FieldTypeDescription
namerequiredstring
statusrequiredGateVerdict
detailstring | null

The row's own measurement text (20/20 survival, tilt median 6.1 deg).

RunProgress

object

L1 — how far a run has got. The TEMPLATE declares where it starts and where it will stop (progress.json in its output directory — docs/API.md "The template contract"; ppo_isaac: the resume or parent step and that plus max_iterations); the RUNNER reads the newest iteration off metrics.jsonl (the template's step key — rsl_rl's iter) and measures iterations per second over the trailing minute of metrics lines, by their own clock (t) where they carry one; the HUB stores what the last report said and computes the ETA at read time.

RunProgress fields
FieldTypeDescription
iterationinteger | null

The newest iteration the metrics show, in the trainer's own numbering.

total_iterationsinteger | null

The number the trainer counts to, as it prints it — rsl_rl's Learning iteration 1412/2300 has 2300 (the start step plus max_iterations). Null when the template declared none.

final_iterationinteger | null

The last iteration the trainer will log: total_iterations − 1 for a zero-based trainer (rsl_rl), total_iterations for one that counts from 1 (stub_ppo). What fraction and the ETA count to.

start_iterationrequiredinteger

≥ 0

0 from scratch; the parent's checkpoint step on a resume from a parent (C1), the resumed step after a preemption.

iterations_per_snumber | null

≥ 0

Measured over the last minute of metrics lines; null until two lines exist.

fractionnumber | null

≥ 0 · ≤ 1

(iteration − start_iteration) / (final_iteration − start_iteration), clipped to [0, 1].

eta_snumber | null

≥ 0

Seconds left at this read: (final_iteration − iteration) / iterations_per_s, less the seconds since as_of. Null when the run is terminal or either number is missing.

eta_atstring (date-time) | null (date-time)

as_of plus the remaining iterations at the measured rate.

step_keystring | null

The metrics key the iteration was read from (iter, iteration, step, …).

as_ofrequiredstring (date-time)

When the hub received the report this was read from.

RunSpend

object

L1 — the run's cost so far, on every read. A priced (modal) run accrues elapsed since the claim × rate_usd_per_hour, where the rate is the tier's plus the template's CPU host (C1) — the same function (executors.modal.spend_usd) the terminal report bills with and the budget stop compares against, so the live number and the final bill cannot disagree. Once the run is terminal final is true and accrued_usd is cost_usd. An unpriced (worker, imported) run answers priced: false with the money fields null.

RunSpend fields
FieldTypeDescription
pricedrequiredboolean
accrued_usdnumber | null

USD to 4 decimal places.

rate_usd_per_hournumber | null

The billed rate, tier plus host (estimateRun's rate_usd_per_hour).

budget_usdnumber | null

recipe.budget_usd, echoed.

remaining_usdnumber | null

max(budget_usd − accrued_usd, 0); null without a budget.

finalrequiredboolean

True once the run is terminal and accrued_usd is the bill.

as_ofrequiredstring (date-time)

RunRound

object

L1 — which campaign round a run belongs to, for "round 4 of Omni walk" and the Shortlist link.

RunRound fields
FieldTypeDescription
campaign_idrequiredId
campaign_slugrequiredstring
campaign_namerequiredstring
campaign_ownerstring | null

The campaign owner's handle.

nrequiredinteger

The round's index in its campaign.

namerequiredstring

The round's name.

RunSparkline

object

AG1 — a run's reward curve decimated by the hub to at most 64 points, so a run card needs no stream.

RunSparkline fields
FieldTypeDescription
metricrequiredstring

The metrics key it follows (Train/mean_reward, else mean_reward, else reward).

pointsrequiredarray of array of number

items ≤ 64

[step, value] pairs, oldest first.

RunImportArtifacts

object

The three documents the coach reads, sent inline. Each is capped at COACH_MAX_ARTIFACT_BYTES (4 MiB by default) — a resolved config and a scorecard are kilobytes, and anything larger belongs in a repo.

RunImportArtifacts fields
FieldTypeDescription
config_yamlrequiredstring

length ≤ 4194304

The RESOLVED training config, not the source template.

metrics_summarystring

length ≤ 4194304

Metrics as text (CSV, JSONL tail, or a table).

scorecard_jsonobject | null

Acceptance results; PASS rows become standing constraints.

DatasetRef

string

length ≤ 512 · pattern ^(lucen|hf|github)://[A-Za-z0-9][A-Za-z0-9._-]*/[A-Za-z0-9][A-Za-z0-9._-]*(@[A-Za-z0-9._/-]+)?(/[A-Za-z0-9._/-]+)?$

Dataset reference URI. Schemes: lucen://{owner}/{repo} (this hub), hf://{namespace}/{name} (Hugging Face), github://{owner}/{repo} with optional @ref and /subpath. v0 validates the format only; workers resolve hf/github refs at training time.

TrainingMethod

string

one of lora · full · rl

lora — adapters on a frozen base; full — every weight; rl — a simulator drives a small policy.

RecommendationBasis

string

one of modelled · measured

modelled — the arithmetic in why[]; measured — a rolling value from succeeded runs of this template at this batch size replaced the arithmetic (measured says which run count and tier).

RecommendationMeasured

object

The measured value the planner used, when basis is measured.

RecommendationMeasured fields
FieldTypeDescription
tierrequiredstring

Tier the measurements came from (other when the runner named none).

batch_sizerequiredinteger

≥ 1

peak_vram_gbnumber | null

Highest peak reported so far.

samples_per_snumber | null

Running mean of the reported throughput.

n_runsrequiredinteger

≥ 1

RecommendationInputs

object

The numbers the arithmetic multiplied, after defaults were applied.

RecommendationInputs fields
FieldTypeDescription
batch_sizerequiredinteger

≥ 1

hyperparams.batch_size for imitation learning, hyperparams.num_envs for RL, else the registry default.

camerasrequiredinteger

≥ 0

image_pxrequiredinteger

≥ 0

The side of the square image the model actually sees (VLAs resize their inputs).

chunk_sizerequiredinteger

≥ 0

stepsrequiredinteger

≥ 0

RecommendationDataset

object

What the planner read out of the dataset's meta/info.json, when it could.

RecommendationDataset fields
FieldTypeDescription
refrequiredDatasetRef
camerasarray of object
keyrequiredstring
widthinteger | null
heightinteger | null
fpsnumber | null
episodesinteger | null
framesinteger | null
state_diminteger | null
action_diminteger | null
robot_typestring | null

RunConfigKey

object

RunConfigKey fields
FieldTypeDescription
pathrequiredstring

A flattened leaf path (env.events.push_robot.params.velocity_range[0]).

valuerequiredstring

The leaf's value, rendered as text and elided past 200 characters.

groupRunConfigGroupId

WA — on RunConfig.keys[]: the RL structure the key falls in, by the one rule table (groups[].id), so a client can list EVERY key of a group (a fine-tune form's editor) without a second copy of the table. Absent on groups[].own_keys, whose group is the one that holds them.

RunConfigDiff

object

UI2 — the coach's attribution evidence #1 (M14b, coach/config_diff.py), exposed to a page: the resolved config against the parent's, flat and path-addressed. Only the columns that moved are listed; the zero-variance columns are counted (unchanged_keys), never listed.

RunConfigDiff fields
FieldTypeDescription
entriesrequiredarray of RunConfigDiffEntry

items ≤ 120

changed_keysrequiredinteger

≥ 0

unchanged_keysrequiredinteger

≥ 0

truncatedrequiredinteger

≥ 0

Changed keys beyond the first 120, not listed.

comparisonstring

one of same_shape · cross_shape

L1. same_shape — both configs are compared path for path (the M14b diff, unchanged). cross_shape — one is the importer's manifest shape and the other Isaac Lab's resolved env.* / agent.*: entries and unchanged_keys cover only the mapped pairs (RunConfig.pairs), and every key with no counterpart is counted in parent_has_no_record / child_has_no_record, never as added, removed or unchanged.

parent_has_no_recordinteger

≥ 0

cross_shape: this run's keys the parent's config has no counterpart for.

child_has_no_recordinteger

≥ 0

cross_shape: the parent's keys this run's config has no counterpart for.

RunConfigGroup

object

L1 — one RL structure's slice of the config and of the diff. total counts this run's keys in the group; compared the keys that could be compared with the parent (the same path both sides, or a mapped pair on a cross_shape comparison), split into changed and unchanged; parent_has_no_record / child_has_no_record the rest — which is not "unchanged" and is never shown as such. A bookkeeping group is listed and counted but never counted as a change (counted: false).

RunConfigGroup fields
FieldTypeDescription
idrequiredRunConfigGroupId
namerequiredstring

How a page titles it: Actions, Algorithm · PPO, Plant · Scene, Sim · scale, …

countedrequiredboolean

False for bookkeeping, whose keys change on every run by construction.

totalrequiredinteger

≥ 0

parent_totalinteger | null

≥ 0

The parent's keys in this group; null when there is no diff.

comparedrequiredinteger

≥ 0

changedrequiredinteger

≥ 0

unchangedrequiredinteger

≥ 0

parent_has_no_recordrequiredinteger

≥ 0

child_has_no_recordrequiredinteger

≥ 0

entriesrequiredarray of RunConfigDiffEntry

The group's changed keys (same shape; added and removed keys included), in path order; [] without a diff.

unchanged_firststring | null

The first unchanged key by path, for a "first … last" summary line.

unchanged_laststring | null
own_keysarray of RunConfigKey

items ≤ 200

P1 — this run's own keys and values in the group that were NOT compared with the parent: the keys the parent has no record of (parent_has_no_record, a cross_shape comparison), or every key of the group when there is no comparison at all. In config order, at most 200 (own_truncated counts the rest); [] when every key was compared.

own_truncatedinteger

≥ 0

P1 — own_keys beyond the first 200, not listed.

summarystring | null

P1 — own_keys as one line a person reads, by the summary rule (docs/API.md "Config summaries"): names relative to the group, a reward term as its weight, an array of one value as name[0..11] = v, at most 12 items then +k more. Null when own_keys is empty.

unchanged_summarystring | null

P1 — the group's unchanged keys as one line, by the same rule; on a cross_shape comparison one item per pair rule, this run's key value ⇄ the parent's key (n) (e.g. scale 0.5 ⇄ action_scale_vec[0..11]). Null when nothing was unchanged.

RunConfigPair

object

L1 — one key pair a cross_shape comparison compared: the parent's manifest key and this run's Isaac Lab key that mean the same thing (action scale, clip and offset; observation terms; actuator gains, armature, friction and limits; control rate and decimation; lat_ff_gain). The list is the rule table in docs/API.md, derived from the founder's real resolved config.

RunConfigPair fields
FieldTypeDescription
grouprequiredRunConfigGroupId
parent_pathrequiredstring
child_pathrequiredstring
fromrequiredstring | null

The parent's value, rendered; null when the parent lacks the key.

torequiredstring | null

This run's value, rendered (after the pair's conversion, e.g. sim.dt × decimation → Hz).

changedrequiredboolean | null

Null when either side lacks the key (the pair could not be compared).

rulestring | null

How the two were made comparable, in one phrase.

RunConfigDeclared

object

L1 — one key of the owning round's declared change (C3), checked against the parent. moved — a counted key under it changed; not_moved — nothing under it changed; cannot_compare — the parent's config holds no record of it (a cross_shape comparison, or a key the parent never carried), so it neither passes nor fails.

RunConfigDeclared fields
FieldTypeDescription
keyrequiredstring
statusrequiredstring

one of moved · not_moved · cannot_compare

groupRunConfigGroupId | null

P1 — the RL structure the key falls in by the one rule table, so a declared key the parent never recorded still sits under its group (a command mode under commands).

summarystring | null

P1 — this run's value(s) under the key, one line by the summary rule (docs/API.md "Config summaries"); null when this run's config holds nothing under it.

RunCheckpoint

object

RunCheckpoint fields
FieldTypeDescription
steprequiredinteger

≥ 0

pathrequiredstring

The directory, e.g. checkpoints/step_1000/.

filesrequiredarray of RepoFileEntry
bytesrequiredinteger

≥ 0

published_atstring (date-time) | null (date-time)

AG1 — when the step's last file landed (the ONNX is pushed last, so this is when the checkpoint became complete).

RunReportStatus

string

one of running · succeeded · failed · canceled

The only transitions a runner may make — running → succeeded | failed, and running → canceled once the hub has set cancel_requested_at on the run (a canceled the hub did not ask for is 422).

RunReportProgress

object

L1 — what a runner knows about a run's progress: the template's declared range (progress.json) and what the metrics show. Every field is optional; send what is known.

RunReportProgress fields
FieldTypeDescription
iterationinteger | null

The newest iteration in metrics.jsonl.

total_iterationsinteger | null

≥ 0

final_iterationinteger | null

≥ 0

start_iterationinteger | null

≥ 0

iterations_per_snumber | null

≥ 0

step_keystring | null

length ≤ 64

HardwareTier

object

HardwareTier fields
FieldTypeDescription
tierrequiredstring

one of l4 · a10g · l40s · a100 · h100

rate_usd_per_hourrequirednumber

USD per GPU-hour as billed — the hub's margin is already included; there is no cheaper number. A template whose Modal function reserves a large CPU host beside the GPU (ppo_isaac: 8 cores, 32 GiB — C1) adds that host's billed rate on top; estimateRun shows it as host_rate_usd_per_hour. A template hosted on CPU alone (ppo_mujoco, F1) pays that host and no tier.

descriptionrequiredstring

HostedExecution

object

L0 — who this deployment runs executor: modal work for (runs, endpoints, rollouts, scans, gates, robot checks). There is no payment system, so it is a list of owners (HOSTED_OWNERS) plus, for every other account, a small monthly allowance for CPU work (K2, allowance); anything else is 403 (forbidden.hosted_owner) and runs on the owner's own machine. A client reads this to offer modal only where it would be accepted. Every hosted job acts as its owner: the hub mints a key for that one job (its owner's, scoped to the job's own operations, dead once the job ends) and hands it to the container (K2).

HostedExecution fields
FieldTypeDescription
allowanceHostedAllowance | null

K2 — the monthly hosted allowance every account outside owners gets for CPU work; null when it is off (HOSTED_ALLOWANCE_USD=0, L0's behaviour exactly) or nothing is hosted.

configuredrequiredboolean

A Modal app is configured (MODAL_APP_NAME). False: every modal request is 503 whoever asks.

everyonerequiredboolean

HOSTED_OWNERS=*: any owner may use modal.

ownersrequiredarray of string

The owner handles (users or orgs) the hub hosts for, lower-case; empty when everyone. A rollout also needs write access to its model.

RunFacetBucket

object

RunFacetBucket fields
FieldTypeDescription
valuerequiredstring

The filter value that selects this bucket (external, worker, succeeded, lucen/laika, none, true).

countrequiredinteger

≥ 0

CheckpointGateSpec

object

Gate checkpoints while the run trains (C2, the founder's watch_ckpt.py). While the run is running, the hub looks for new checkpoints/step_<N>/ directories in its output model repo (one .onnx each) about once a minute and queues a rollout of the first checkpoint past every every_steps boundary — battery (default watch), seeds (default 3), at delay (default 2) — on recipe.robot. tripwires are evaluated on the gated results; one that fires asks the run to stop (a notice in its log names the tripwire, the step and the rollout), exactly as the budget stop does.

CheckpointGateSpec fields
FieldTypeDescription
every_stepsrequiredinteger

≥ 1

Gate the first checkpoint at or past each multiple of this.

batterystring

one of watch · smoke · c_matrix · robust · recovery · full · stand

default "watch"

seedsinteger

default 3 · ≥ 1 · ≤ 20

delayinteger

default 2 · ≥ 0 · ≤ 10

Control steps of actuation delay (watch_ckpt.py --delay 2, the real link's upper bound).

executorRolloutExecutor | null

Null means the run's own executor.

tripwiresarray of GateTripwire

items ≤ 8

videoVideoMode | null

L1 — what each gate rollout renders. Null keeps C2's behaviour (full: three views per cell). A campaign round on a walk or omni task is gated on c_matrix × 5 seeds with poster (the founder's call, 2026-09-26): physics plus one still per cell, so a gate lands in minutes; the three views stay on the 20-seed band scan, and any gate's can be rendered later (renderRollout).

horizon_sread-onlynumber | null

P1 — the seconds each seed runs, as the battery defines it for the run's robot (c_matrix 20 s, recovery 10 s). Filled by the hub wherever it returns the spec (Run.recipe.gate, AgentPlanRound.gate); a value sent in a request is ignored and never stored — the battery decides, as the gate always did.

cell_countread-onlyinteger | null

P1 — how many cells the battery runs on the run's robot (c_matrix 13; smoke 5 on legs, 3 on a fixed base). Filled and ignored like horizon_s.

GateVerdict

string

one of PASS · FAIL

A cell's verdict under the harness rule.

RunConfigGroupId

string

one of actions · contract · observations · plant · rewards · network · algorithm · domain_randomization · curriculum · terminations · commands · sim · bookkeeping · other

L1 — the RL structure a resolved-config key belongs to (docs/API.md "Config groups": one rule table, first match wins, for both the Isaac Lab and the importer's manifest shape).

RunConfigDiffEntry

object

RunConfigDiffEntry fields
FieldTypeDescription
pathrequiredstring
kindrequiredstring

one of added · removed · changed

fromrequiredstring | null

The parent's value; null when the key is absent there.

torequiredstring | null

This run's value; null when the key is absent here.

in_declared_changeboolean | null

C3 — for a campaign round's run: true when the key lies inside the round's declared change, false when it does not (the one-variable rule counts it), null when the run is in no round or the key is bookkeeping that changes on every run by construction (run.*, the run ids, the iteration cap) and is never counted.

groupstring | null

L1 — the RunConfigGroup.id this key falls in (docs/API.md "Config groups").

parent_pathstring | null

L1 — on a cross_shape comparison, the parent's key this run's path was mapped to (contract.action_clip for env.actions.joint_pos.clip); null when both sides use the same path.

HostedAllowance

object

K2 — what an account outside HOSTED_OWNERS may spend on the hub's Modal account per calendar month (UTC), CPU work only: hosted robot and model checks, ppo_mujoco runs, CPU sim rollouts (single, scans, gates). Every GPU tier and endpoint stays HOSTED_OWNERS-only (L0's 403). A job reserves its most possible cost before it is queued (max_hours x rate, capped by budget_usd, for a run; the function's timeout x its host rate for a rollout or check) and settles from the ledger when it ends; a job that does not fit is 403 with errors[].type: forbidden.hosted_allowance.

HostedAllowance fields
FieldTypeDescription
limit_usdrequirednumber
cpu_onlyrequiredboolean
workrequiredarray of string

What the allowance covers, by name.

viewerHostedAllowanceUsage | null

The caller's own account this month; null when anonymous, or when the caller is in owners (no allowance applies).

RolloutExecutor

string

one of worker · modal

default "worker"

Where a rollout runs (C2). worker (the default) — it waits queued for a lucen rollout worker the owner runs. modal — on a deployment that names a deployed Modal app (MODAL_APP_NAME), the hub spawns the app's rollout function (CPU, EGL, ffmpeg) after the commit and stores modal_call_id; the container claims and reports like any worker. Unset, modal is 503 naming the variable and nothing is recorded. A CPU rollout is unpriced either way.

GateTripwire

object

Stop the run when a gate metric crosses a line (C2): fires when the last consecutive gated checkpoints at or past from_step all satisfy metric op threshold. pass_rate = PASS cells / cells, alive_rate = seeds alive / seeds run, gates_passed = PASS cells. An early stop on success is a tripwire too: pass_rate >= 1.

GateTripwire fields
FieldTypeDescription
metricrequiredstring

one of pass_rate · alive_rate · gates_passed

oprequiredstring

one of < · <= · > · >=

thresholdrequirednumber
from_stepinteger

default 0 · ≥ 0

consecutiveinteger

default 1 · ≥ 1 · ≤ 20

VideoMode

string

one of none · poster · full

default "full"

L1 — what the worker renders of each cell's seed 0. full (the default, and what every rollout rendered before L1) — C2's three 640×480 views (side, front, feet) as mp4s, ~75 s of CPU per 20 s cell. poster — ONE still per cell: the side view, seed 0, at the episode's last control step (the end of the horizon, or the step the robot fell), 320×240 PNG — enough to scan a checkpoint row without playing anything. none — physics only. The measurements, the trajectory and the verdict are identical in all three: rendering never touches the physics. A gate rollout's videos can be rendered afterwards with renderRollout.

HostedAllowanceUsage

object

HostedAllowanceUsage fields
FieldTypeDescription
accountrequiredstring
used_usdrequirednumber

Settled spend this month (ledger rows of finished hosted jobs).

reserved_usdrequirednumber

The most the account's queued and running hosted jobs can still cost.

remaining_usdrequirednumber
period_startrequiredstring (date-time)
resets_atrequiredstring (date-time)