CONTEXT FOR YOUR CODING AGENTMeet Infimal.
Meet Infimal.
Bring your agent.
One file with the model, the lifecycle, and the commands. Give your agent the context to build with Infimal.
infimal.mdMarkdown · no authentication required
01Give it the reference.
Paste the file into your agent’s context or let it fetch the raw URL.
02Describe your model.
Share its initialization, inference code, and the service you need.
03Review the plan.
Your agent defines the infrastructure. You stay in control of changes.
Infimal
Infrastructure as code for model services with unpredictable demand.
Names and domains
Airglow is now infimal. The primary website is https://infimal.ai; the backup is https://infimal.dev. API aliases are https://api.infimal.ai and https://ai.infimal.dev. The CLI and the SDK default to https://api.infimal.ai; the API host from before the rename remains supported. All aliases reach the same platform. The native command is infimal, and it is the only CLI. The Python package is infimal (import infimal); it installs no command, and the native command runs it for apps plan, apply and deploy. The environment is INFIMAL_API_KEY, with INFIMAL_API_URL for the native command and INFIMAL_ENDPOINT for the SDK. For one release the pre-rename AIRGLOW_* and GLOW_* names are still read, with a warning; write the new names. Existing API keys do not change. /infimal.md, /airglow.md and /agent.md serve this reference. See /docs/migration/ for account and browser-session details.
Read this first
Python is the current authoring interface, not the core infrastructure format. The platform contract is the serialized deployment specification. GPU selection belongs to Infimal. Do not invent GPU SKU parameters or require a traffic forecast to get started. The product is designed for scale to zero and scale out to thousands of replicas. A configured maximum is a bound, not reserved capacity or a measured scaling guarantee. Service configuration and request intent are separate. Access, scaling, and batching belong to the service. Urgency and performance objectives belong to individual calls. The current SDK still includes deployment-level perf/tier fields. /docs/model-syntax/ is the reference for the request surface that ships: every Client.model(id) method, named exactly as the SDK implements it. It is not where a proposal is described.
Discover the documentation
- /docs/overview/ — product model and lifecycle - /docs/quickstart/ — complete first-service workflow - /docs/installation/ — source installation and environment configuration - /docs/infrastructure/ — App, Endpoint, Scale, Batch and specification translation - /docs/lifecycle/ — setup, snapshots, handlers and checkpoint boundaries - /docs/scaling/ — replica bounds, queue age and residency policy - /docs/batching/ — dynamic batching and streaming - /docs/requests/ — request intent and priority contract - /docs/deployments/ — plan, deploy, apply and verification - /docs/observability/ — status, snapshots, logs and usage - /docs/pricing/ — rates, accounting, held credit and funding - /docs/access/ — endpoint visibility, key roles and API keys - /docs/cli/ — exact current CLI surface - /docs/troubleshooting/ — diagnostics by failing stage - /docs/model-syntax/ — the shipped request syntax reference, by surface Resolve these paths against the origin serving this document.
Install and configure
The native Rust infimal binary provides a terminal dashboard and scriptable commands. Install on macOS or Linux (or WSL), then create a full-access CLI key at /cli/: ```sh curl -fsSL https://infimal.ai/install.sh | sh infimal login --key infimal whoami infimal ``` The login prompt is hidden. Never put a key in a command argument or URL. INFIMAL_API_URL selects the API; INFIMAL_API_KEY overrides a saved credential. Run infimal guide for setup help, or infimal tui --demo to explore sample data. Python model authoring and requests use the SDK. infimal apps plan, apply and deploy run your module through it, so install it where your module's dependencies are, or set INFIMAL_PYTHON to that interpreter: ```sh python -m pip install infimal infimal apps plan --help ``` The Python SDK uses INFIMAL_ENDPOINT and INFIMAL_API_KEY; the native client uses INFIMAL_API_URL and INFIMAL_API_KEY. The native installer does not install the SDK.
Call a model from Python
The request API ships in the SDK today:
```python
from infimal import Client
client = Client.from_env()
handle = client.model("MODEL_ID") # an id from infimal models list --json
print(handle.chat("Explain this diff.").max_tokens(256).run().text)
caps = handle.capabilities() # kinds, readiness and options for this listing
```
client.model(id) returns a handle. .chat(), .embeddings(), .speech(), .images() and
.video() each build one request; run() or stream() submits it once. Images and video
are jobs: .submit() returns a Job to wait(), refresh() or cancel(), run() waits for
you, client.job(id) adopts a job after a restart and client.jobs() lists them. Every
failure is an InfimalError carrying code, message, next and details; branch on code,
never on the message. InfimalUnreachable means nothing answered, so resubmit a job
with the same idempotency key rather than a new one.
Stack, Service, app.<model_type> and per-call bids are design proposals and are not
built: no code should call them, and /docs/model-syntax/ does not describe them —
it is the reference for the request surface that ships instead.Infrastructure lifecycle
Apps are a preview and may change: the builder and gateway that would run a deployed App
are not live yet. The lifecycle and the commands below are the interface as it stands.
1. Declare an App with endpoint access, replica bounds, and batching.
2. Initialize reusable model state in @app.setup.
3. Infimal captures initialized process/model state at the snapshot boundary.
4. Handle each request with @app.handler, or each frame with @app.stream.
5. Review a plan, deploy or apply an authorized change, then inspect actual status.
```python
from infimal import App, Endpoint, Scale, Batch
app = App(
"asr-emotion-checkpoint4200",
endpoint=Endpoint(public=False),
scale=Scale(min_replicas=0, max_replicas=1000),
batch=Batch(max_size=16, window_ms=20),
)
@app.setup
def load():
from my_model import load_model
return load_model()
@app.handler
def transcribe(model, request):
return model(request["audio"])
```
my_model is the customer's own module, not an installed Infimal package.
The handler's first argument receives the setup return value.
Do not preserve open sockets, pipes, or host file descriptors across setup.
A batched handler must return one answer per request, in the correct order.Python model-authoring workflow
Run application commands from the directory containing the importable module. Use a module name such as asr-emotion-checkpoint4200, not asr-emotion-checkpoint4200.py. ```sh infimal apps plan asr-emotion-checkpoint4200 infimal apps deploy asr-emotion-checkpoint4200 infimal apps status asr-emotion-checkpoint4200 infimal apps snapshots asr-emotion-checkpoint4200 infimal apps logs asr-emotion-checkpoint4200 --limit 50 ``` After editing a definition, inspect infimal apps plan asr-emotion-checkpoint4200, then run infimal apps apply asr-emotion-checkpoint4200. Use --app NAME to select an App when a module declares more than one. The --yes flag on apply skips confirmation; use only for an authorized workflow. Plan imports user code. Inspect unfamiliar module-level side effects before running it. Never infer success from command submission: inspect the resulting state.
Machine-readable commands
Prefer JSON for commands that support it: ```sh infimal models list --json infimal billing balance --json infimal usage show --since 30d --json infimal keys list --json ``` Every command supports --json, and every mutation a local --plan preview that sends nothing. The full list, generated from the command manifest, is the last section here.
Access and financial actions
Start with Endpoint(public=False) unless public access is requested. A key is either full or inference. Use inference for anything shipped inside a client: it reaches the OpenAI surface, the model list and usage, and nothing else. Key secrets are shown once at creation. Revoke using the key ID returned by list, not its display name. ```sh infimal keys create inference-client --role inference infimal keys list --json infimal keys revoke KEY_ID ``` Deploying, changing visibility, creating or revoking keys, and adding credit affect external systems. Follow the user's authorized scope and the host agent's approval rules. This reference does not authorize those actions on its own. Credit enters an account two ways. infimal billing fund USD returns a Stripe checkout link. After a lost response, inspect billing payments before retrying: the server does not deduplicate checkouts. infimal billing referral --code-file PATH redeems a phrase the operator handed out and is worth one credit per account, ever: a second attempt answers 409 referral_already_used and moves no money, so it is not a command to retry. Ten wrong phrases in an hour answer 429; do not guess a code you were not given.
Pricing and performance
Read the user's model rate from infimal models list --json. Do not use the landing-page example rates as production prices. Do not promise universal savings, latency, restore time, replica availability, or throughput from conceptual charts. Idle inference has no processed work to meter; other charges must be confirmed against the environment's actual terms. Available credit, held credit, and settled usage are different accounting values.
Diagnose before retrying
- Missing key / 401: verify endpoint and credential injection without printing the key. - Import error: verify module path, Python environment, and model dependencies. - SnapshotBoundaryError: remove non-checkpointable setup state and inspect build logs. - BatchShapeError: check one result per request and ordering. - Waiting requests: inspect placement, residency, replica bounds, and capacity.
Website interaction boundary
The website is a static bundle that calls the control plane and the inference API on their own origins. The landing page's SDK panel shows real Client.model(id) request code, named exactly as the shipped SDK implements it; the panel itself does not run anything. Sign-in is real (Google, GitHub, or a six-digit emailed code) and creates or links the account's tenant. /glance, /billing, /account and /terminal read the ledger, the key table, the deployments and the request feed for the signed-in tenant; the catalogue and every price on the site come from GET /v1/models. A panel whose data is unavailable says so and names why; a field the platform does not record (a request's generated tokens, its latency) is labelled "not recorded", never rendered as zero. The browser terminal executes whoami, balance, usage, requests, keys, models, quote, status, logs and chat against the live APIs; chat mints an inference key named web-terminal for the browser. plan, deploy, apply, login, signup and install print the CLI command and say the browser cannot run them: they need a source tree or a shell. Do not treat those printed commands as executed. Top-up opens Stripe Checkout and the credit lands when Stripe confirms the payment. The optional filter_infimal_catalogue WebMCP tool on /explore/ filters the live catalogue and returns real model ids, kinds, source kinds and USD prices. It provisions nothing. Terms are at /terms/ and the privacy policy at /privacy/.
Command reference
Generated from the command manifest; infimal manifest --markdown prints it. Use --json for machine output. Every mutation accepts --plan for a local, redacted request preview. Plans are not server quotes.
### infimal models list
Browse available models and prices
`GET /v1/models`
### infimal models show
Inspect one listing: its price and what it accepts right now
`GET /v1/models/{id}`
The listing as models list shows it, with what its serving replicas reported beside it (D358): accepts_references, accepts_negative_prompt, reference_mode, video_timing, the LoRAs every replica holds, the default schedule, ready and the replica counts. A value no replica reported is null, never a guess. An unknown listing, one taken off sale and one you were not granted are the same 404 model_not_found.
- `ID`: Model listing ID (required)
### infimal models publish
Set visibility of a listing you own, or any listing as an administrator
`POST /v1/models/{id}/publish`
- `ID`: Model listing ID (required)
- `--public`: Public visibility (defaults to true; false makes private)
### infimal apps list
List your applications
`GET /v1/apps`
### infimal apps show
Inspect an application and its configuration
`GET /v1/apps/{app}`
- `APP`: Application name (required)
### infimal apps deploy
Build and place the App a Python module declares
`POST /v1/apps/{app}/deploy`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Executes the module through the Python SDK (python -m infimal._synth), then posts the declaration and the source digest. A first deploy answers 409 build_queued with the build to poll. With --file, no Python runs: the file is the request body and --app names the app.
- `MODULE`: Python module that declares the App, as an import path (services.voice), not a file name (default main)
- `--app`: App name, when the module declares more than one
- `--file`: Send this JSON deploy request instead of executing a module; needs --app
### infimal apps config
Update an application's live configuration
`PUT /v1/apps/{app}/config`
- `APP`: Application name (required)
- `--file`: JSON request file; - reads stdin (required)
### infimal apps status
Inspect an application's placements
`GET /v1/apps/{app}/placements`
- `APP`: Application name (required)
### infimal apps logs
Read application build logs
`GET /v1/apps/{app}/logs`
- `APP`: Application name (required)
- `--limit`: Maximum number of rows
- `--build`: Build ID
### infimal apps snapshots
List published snapshots
`GET /v1/apps/{app}/snapshots`
- `APP`: Application name (required)
### infimal apps plan
Show what applying a Python module's App would change
`GET /v1/apps/{app}`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Executes the module through the Python SDK (python -m infimal._synth), reads the live app and diffs the two: a change above the @app.setup line rebuilds the snapshot, one below it is configured in place and keeps it. Planning imports your code, so its imports must be safe to run.
- `MODULE`: Python module that declares the App, as an import path (services.voice), not a file name (default main)
- `--app`: App name, when the module declares more than one
### infimal apps apply
Apply a Python module's App: rebuild, or configure in place
`PUT /v1/apps/{app}/config`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Plans first, then sends PUT /v1/apps/{app}/config for a change below the @app.setup line or POST /v1/apps/{app}/deploy for one above it. Asks before applying unless --yes.
- `MODULE`: Python module that declares the App, as an import path (services.voice), not a file name (default main)
- `--app`: App name, when the module declares more than one
- `--yes`: Apply without asking; required when stdin is not a terminal
### infimal builds show
Inspect a build's progress
`GET /v1/builds/{id}`
- `ID`: Resource ID (required)
### infimal gateway show
Inspect your applied gateway: its listings, their chains in order, its credential names
`GET /v1/gateway`
One row per source, in fallback order under its listing, with the credential it uses. Credentials are listed by name; a value is never returned. With none applied, the API answers 404 gateway_not_found.
### infimal gateway plan
Show what applying a Python module's Gateway would change, as the server sees it
`POST /v1/gateway/plan`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Executes the module through the Python SDK (python -m infimal._synth) and sends what it declares to POST /v1/gateway/plan (D345, D418), which answers each listing, source, relay and credential as create, change, unchanged or delete, every refusal the apply would make, and warnings (a first-token SLA its fallback kinds never act on, a credential sealed long ago), and writes nothing. Reads no credential: the body names each credential's variable and carries no value. A first apply prints what the module declares above the plan.
- `MODULE`: Python module that declares the Gateway, as an import path (gateway), not a file name (default gateway)
- `--gateway`: Gateway namespace, when the module declares more than one
### infimal gateway apply
Apply a Python module's Gateway: its listings, chains and credentials, as one body
`PUT /v1/gateway`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Plans first with POST /v1/gateway/plan (D418), which answers every change and every refusal and writes nothing; a plan with a refusal stops here, before any variable is read. With nothing to change it sends nothing, unless --reseal, which sends the unchanged declaration so a rotated value is re-sealed. Otherwise it reads the variable each declared credential names and refuses before sending if one is unset or empty, naming the variable and never a value. Then it asks, unless --yes, and sends the declaration with each credential's value to PUT /v1/gateway. --plan masks every key. Declared relays are applied around the PUT through /v1/sandbox-relays, as the plan lists them: creates by name (with each key read from its variable) and scales before it, deletes of named relays the declaration dropped after it; --reseal also re-sends every relay's keys. Relay instances are billed per second while they run, and the plan prints the rate. Nodes are declared in the platform file and rendered by infimal admin platform apply (D419).
- `MODULE`: Python module that declares the Gateway, as an import path (gateway), not a file name (default gateway)
- `--gateway`: Gateway namespace, when the module declares more than one
- `--yes`: Apply without asking; required when stdin is not a terminal
- `--reseal`: With no declaration change, send it anyway so every credential is re-sealed
### infimal keys list
List API keys without revealing their secrets
`GET /v1/keys`
### infimal keys create
Create an API key and print its secret once
`POST /v1/keys`
- `NAME`: Key name (required)
- `--role`: Key role (default: full)
### infimal keys revoke
Revoke an API key
`DELETE /v1/keys/{id}`
- `ID`: Resource ID (required)
### infimal billing balance
Show available credit and spending
`GET /v1/usage`
- `--since`: Time window: 30d, 12h, or 90m
### infimal billing fund
Create a checkout link to buy credit
`POST /v1/billing/checkout`
--wait polls your own payments (GET /v1/billing/payments) until this one is no longer pending: a completed row is credit the webhook has already put in the ledger. A wait that times out exits 1 and the payment is not lost.
- `USD`: Positive USD amount (up to six decimal places) (required)
- `--idempotency-key`: Reuse this key when retrying the same checkout; use a new key for a new purchase
- `--wait`: Block until the payment has landed and the credit is spendable
- `--timeout`: Seconds to wait for the payment (default 600)
- `--poll-interval`: Seconds between checks while waiting (default 3)
### infimal billing referral
Redeem a referral phrase once per account
`POST /v1/billing/referral`
- `--code-file`: File containing the referral phrase; - reads stdin (required)
### infimal billing payments
List credit purchases and receipts
`GET /v1/billing/payments`
- `--limit`: Maximum number of rows
### infimal usage show
Inspect spending by model
`GET /v1/usage`
--by model adds the per-model totals under client.by_model; the API's envelope is unchanged.
- `--since`: Time window: 30d, 12h, or 90m
- `--by`: Sum the spend per model, across its meters
### infimal usage series
Inspect spending over time
`GET /v1/usage/series`
- `--since`: Time window: 30d, 12h, or 90m
- `--bucket`: Time bucket
### infimal usage performance
See how your requests were served, per model
`GET /v1/usage/performance`
Per model you called, from your own served requests (D393): requests, the first-token time you received (admission to first byte) p50/p90/p99 and output tokens a second p50, in total and per bucket. Never a source or a provider: those are the listing owner's. Default window 30d.
- `--since`: Time window: 30d, 12h, or 90m
- `--bucket`: Time bucket
### infimal requests list
Inspect recent inference requests
`GET /v1/requests`
- `--limit`: Maximum number of rows
### infimal providers list
See your providers and sources, with the window's SLA summary
`GET /v1/providers`
Every provider your listings' sources dial, each source with its attempts, eligible attempts, success, first-token SLA verdicts (within, slow, timed_out, failed_fast), first-token and full-answer times and throughput, from the attempt log (D393). Default window 24h; quantiles here are estimates, the monthly report's are exact. 503 provider_data_unavailable on a deployment that keeps no attempt log.
- `--since`: Time window: 30d, 12h, or 90m
- `--tenant`: Admin tenants only: another owner's tenant ID, or platform for infimal's own sources
### infimal providers series
Chart each source's success, SLA and first-token time over time
`GET /v1/providers/series`
One point per source per bucket: attempts, eligible, success %, within-SLA %, first-token p50/p90/p99 and throughput p50. --since or --month, not both; the bucket is a day over three days or for a month, an hour otherwise.
- `--since`: Time window: 30d, 12h, or 90m
- `--month`: A UTC calendar month, YYYY-MM; not one that has not begun
- `--bucket`: Time bucket
- `--source`: One source's ID; default every source of every listing you own
- `--tenant`: Admin tenants only: another owner's tenant ID, or platform for infimal's own sources
### infimal providers attempts
List the attempts made against your sources, newest first
`GET /v1/providers/attempts`
One row per try against a provider, with its outcome, status, header, first-token and total times, tokens, the provider's own request and completion ids, and the SLA verdict and throughput the Rust rules give it. JSON pages up to 1000 rows (default 100) under an opaque next cursor; --format csv writes every row in scope to --out.
- `--since`: Time window: 30d, 12h, or 90m
- `--month`: A UTC calendar month, YYYY-MM; not one that has not begun
- `--source`: One source's ID; default every source of every listing you own
- `--outcome`: Only this outcome, e.g. timeout_first_token (docs/sla.md lists them)
- `--breaches`: Only breaches: missed the first-token SLA, or failed on the provider's side
- `--limit`: Maximum number of rows
- `--after`: The next cursor of the previous page
- `--format`: json pages; csv writes every row in scope to a file
- `--out`: Write the file here; default the name the API gives it, in the current directory
- `--tenant`: Admin tenants only: another owner's tenant ID, or platform for infimal's own sources
### infimal providers report
Produce a month's SLA report per source and provider
`GET /v1/providers/report`
Per source and rolled up per provider: eligible attempts, within / slow / timed_out / failed_fast against the bound each attempt ran under, within-SLA % and success %, exact first-token p50/p90/p95/p99/max, throughput p10/p50/p90, a row per day, the incidents (5-minute runs under 90% within or 95% success) and the log's coverage. Without --out it prints a table per source; with --out, or --format csv, it writes the API's file.
- `--month`: The UTC calendar month to report, YYYY-MM; the current one is reported as incomplete (required)
- `--source`: One source's ID; default every source of every listing you own
- `--format`: json (default) or csv
- `--out`: Write the file here; default the name the API gives it, in the current directory
- `--tenant`: Admin tenants only: another owner's tenant ID, or platform for infimal's own sources
### infimal providers breaches
Download a month's breach evidence for your provider
`GET /v1/providers/report/breaches`
Every eligible attempt of the month that was not within the bound, oldest first, with the provider's request id and completion id so the provider can match it line by line. Written to --out (default the API's file name); --format json without --out prints it.
- `--month`: The UTC calendar month to report, YYYY-MM; the current one is reported as incomplete (required)
- `--source`: One source's ID; default every source of every listing you own
- `--format`: csv (default, every breach) or json (at most 10000)
- `--out`: Write the file here; default the name the API gives it, in the current directory
- `--tenant`: Admin tenants only: another owner's tenant ID, or platform for infimal's own sources
### infimal hooks create
Create a git hook and print its secret once
`POST /v1/hooks`
- `--app`: Restrict the hook to an application
### infimal hooks revoke
Revoke a git hook
`DELETE /v1/hooks/{id}`
- `ID`: Resource ID (required)
### infimal uploads create
Reserve a tenant-owned reference image upload
`POST /v1/uploads`
- `--content-type`: image/png, image/jpeg or image/webp (required)
- `--bytes`: Exact byte length, within the API's upload limit (32 MiB by default) (required)
### infimal jobs submit
Submit an image or video generation
`POST /v1/images/generations`
Posts to /v1/images/generations or /v1/video/generations by --surface; the submit holds the credit and answers 202 with the job. Only the flags given are sent: the runner refuses a null. --wait polls GET /v1/jobs/{id}; a job that fails or is cancelled exits 1.
- `--surface`: Which endpoint: images are billed per image, video per generated second (required)
- `--model`: Model listing ID (required)
- `--prompt`: Prompt
- `--negative-prompt`: Negative prompt
- `--preset`: I1, I2, I4, V5 or V10: the workload table's geometry; explicit fields override it
- `--n`: Images: 1 to 4
- `--frames`: Video: frame count (V5 is 81, V10 is 161)
- `--fps`: Video: frames a second (16 for both presets)
- `--width`: Width in pixels
- `--height`: Height in pixels
- `--steps`: Sampling steps
- `--seed`: Seed
- `--adapter`: LoRA as id or id:weight
- `--reference`: A reference image (PNG, JPEG or WebP), sent as base64; repeatable up to the cap
- `--reference-key`: An uploaded reference key (uploads create); repeatable
- `--idempotency-key`: Resubmitting with this key returns the job you already have, not a second one
- `--wait`: Poll until the job reaches a state it will not leave
- `--timeout-s`: Seconds to wait before reporting what the job is doing (default 2700)
### infimal jobs get
Inspect one job: state, measured timings and artifact URLs
`GET /v1/jobs/{id}`
Artifact URLs are presigned and expire; read the job again for fresh ones. A job that failed or was cancelled exits 1.
- `ID`: Job ID (required)
- `--wait`: Poll until the job reaches a state it will not leave
- `--timeout-s`: Seconds to wait before reporting what the job is doing (default 2700)
### infimal jobs list
List your jobs, newest first
`GET /v1/jobs`
- `--limit`: Maximum number of rows
### infimal jobs cancel
Cancel a job that has not started, and be refunded
`DELETE /v1/jobs/{id}`
- `ID`: Job ID (required)
### infimal jobs download
Fetch a succeeded job's artifacts into a directory
`GET /v1/jobs/{id}`
Reads the job again for fresh presigned URLs and fetches each artifact without your API key: the URL is its own credential. A job that has not succeeded exits 1.
- `ID`: Job ID (required)
- `DIRECTORY`: Directory to write <index>.<ext> into (required)
- `--wait`: Poll until the job reaches a state it will not leave
- `--timeout-s`: Seconds to wait before reporting what the job is doing (default 2700)
### infimal video generate
Submit a video generation, billed per generated second
`POST /v1/video/generations`
The same submit as jobs submit --surface video. The API takes no duration: a clip is --frames / --fps seconds, or a preset's, and that length is what the hold and the charge are, never rounded. Only the flags given are sent: the runner refuses a null. --wait polls GET /v1/jobs/{id}; a job that fails or is cancelled exits 1.
- `--model`: Model listing ID (required)
- `--prompt`: Prompt
- `--negative-prompt`: Negative prompt
- `--preset`: V5 or V10: the workload table's clip; explicit fields override it
- `--frames`: Frame count (V5 is 81, V10 is 161)
- `--fps`: Frames a second (16 for both presets)
- `--width`: Width in pixels
- `--height`: Height in pixels
- `--steps`: Sampling steps
- `--seed`: Seed
- `--adapter`: LoRA as id or id:weight
- `--reference`: A reference image (PNG, JPEG or WebP), sent as base64; repeatable up to the cap
- `--reference-key`: An uploaded reference key (uploads create); repeatable
- `--idempotency-key`: Resubmitting with this key returns the job you already have, not a second one
- `--wait`: Poll until the job reaches a state it will not leave
- `--timeout-s`: Seconds to wait before reporting what the job is doing (default 2700)
### infimal sandbox-relays create
Store tenant Gemini keys or agy sign-ins and start relay instances
`POST /v1/sandbox-relays`
Keys are encrypted on the orchestrator volume. Instance limits and the tenant allowlist apply. With --name (1 to 64 of a-z, 0-9, '.', '_', '-'), an existing relay of that name is returned when its key labels, backend and models match, with changed values rotated and --instances ignored (scale it instead); anything else is 409 relay_conflict. --backend agy (D408) takes 1 to 16 --models and an agy sign-in per --env (AGY_TOKEN="$(scripts/agy-token-export.sh -)"). Each instance is billed per second while it runs, 60 s minimum. A gateway declaration that declares relays manages its named relays.
- `--env`: Key environment variable name (a Gemini key, or with --backend agy a sign-in); repeatable, never a key value
- `--instances`: Total instances across supplied keys, 0 to 32 (required)
- `--idempotency-key`: Stable key for retrying this create (required)
- `--name`: Relay name; same tenant and name is the same relay
- `--backend`: gemini (default) or agy: each --env then names a sign-in from scripts/agy-token-export.sh
- `--models`: With --backend agy: the agy model slugs the relay serves, comma-separated (a,b,c)
### infimal sandbox-relays list
List this tenant's relays
`GET /v1/sandbox-relays`
### infimal sandbox-relays get
Inspect a relay's desired and actual capacity
`GET /v1/sandbox-relays/{id}`
- `ID`: Sandbox ID (required)
### infimal sandbox-relays scale
Set a relay's desired instance count
`PATCH /v1/sandbox-relays/{id}`
- `ID`: Sandbox ID (required)
- `--instances`: Total desired instances; zero pauses the relay (required)
### infimal sandbox-relays delete
Stop a relay and remove its stored Gemini keys
`DELETE /v1/sandbox-relays/{id}`
- `ID`: Sandbox ID (required)
### infimal sandboxes create
Create a sandbox: an isolated Linux VM, billed by the second for its size
`POST /v1/sandboxes`
Full keys on enabled accounts only (403 sandboxes_not_enabled says how to ask). The create holds ten minutes at the size asked for; from Railway accepting it to its destroy it is billed by the second for that size, at least a minute (D398). It waits for running by default and exits 1 if the sandbox is not running when the wait ends; --no-wait answers at once. At most five sandboxes not ended per account (429 sandbox_limit).
- `--cpu`: vCPU, 0.25 to 4 in steps of 0.25 (default 1)
- `--memory-gb`: Memory in GB, 0.5 to 8 in steps of 0.5 (default 2)
- `--idle-timeout-minutes`: Minutes with no exec or keepalive before Railway ends it, 1 to 120 (default 10)
- `--ttl-seconds`: The longest it may live, 60 to 86400 seconds (default 3600)
- `--env`: A variable to set in the sandbox, by name: its value is read from your environment, never from an argument; repeatable
- `--wait`: Seconds the API holds its answer until the sandbox is running, 0 to 60 (default 60)
- `--no-wait`: Answer at once, while it is still being created (--wait 0)
- `--idempotency-key`: Creating again with this key returns the sandbox you already have, not a second one
### infimal sandboxes list
List your sandboxes, newest first; ended ones for seven days
`GET /v1/sandboxes`
- `--state`: Only sandboxes in this state
- `--limit`: Maximum number of rows
### infimal sandboxes get
Inspect one sandbox: its state, size, expiry and what it has cost
`GET /v1/sandboxes/{id}`
- `ID`: Sandbox ID (required)
### infimal sandboxes exec
Run a command in a running sandbox, and print what it wrote
`POST /v1/sandboxes/{id}/exec`
Prints the command's stdout on stdout and its stderr on stderr, and exits with its exit code; one that timed out or was killed exits 124. Railway keeps at most 16,000 bytes of each stream, and a cut is noted on stderr. --json prints the API's answer and still exits with the command's code. Longer work runs detached (nohup … &) and is read back with a second exec.
- `ID`: Sandbox ID (required)
- `COMMAND`: The command, run by the sandbox's shell: quote it as one argument (required)
- `--timeout-s`: Seconds before the command is stopped, 1 to 100 (default 60)
### infimal sandboxes keepalive
Reset a sandbox's idle timer
`POST /v1/sandboxes/{id}/keepalive`
- `ID`: Sandbox ID (required)
### infimal sandboxes delete
Destroy a sandbox; its billing stops when Railway confirms it gone
`DELETE /v1/sandboxes/{id}`
Answers the sandbox with its state: stopping until Railway confirms the destroy, then ended. Deleting one already stopping or ended answers it as it is.
- `ID`: Sandbox ID (required)
### infimal studio projects list
List your active Studio projects
`GET /v1/studio/projects`
### infimal studio projects create
Create an empty Studio project
`POST /v1/studio/projects`
- `--title`: Project title, 1 to 160 characters (required)
### infimal studio projects show
Read a Studio project: its title, document and revision
`GET /v1/studio/projects/{id}`
- `ID`: Studio project ID (required)
### infimal studio projects save
Replace a Studio project's title and document
`PATCH /v1/studio/projects/{id}`
The file is the body without its revision, {"title", "document": {"brief", "shots": [{"id", "title", "prompt", "picked_job"}]}}, and --revision is added to it. A project changed since that revision answers 409 and nothing is written; read it again. A picked_job must be a succeeded generation of that project and shot.
- `ID`: Studio project ID (required)
- `--file`: JSON {title, document} to save; - reads stdin (required)
- `--revision`: The revision studio projects show returned (required)
### infimal studio projects archive
Archive a Studio project; its jobs are kept
`DELETE /v1/studio/projects/{id}`
Soft: the project leaves the list, its generations stay attributable, and running work is not cancelled. A project changed since that revision answers 409.
- `ID`: Studio project ID (required)
- `--revision`: The revision studio projects show returned (required)
### infimal studio projects jobs
List a project's generations, newest first, with the shot each is for
`GET /v1/studio/projects/{id}/jobs`
- `ID`: Studio project ID (required)
### infimal studio projects generate
Generate a take of one shot from the shot's saved prompt
`POST /v1/studio/projects/{id}/generations`
Submits to /v1/images/generations or /v1/video/generations by --surface, with the saved shot's prompt in place of any prompt in --request. The job's idempotency key is studio:PROJECT:SHOT:REQUEST_ID, so a retry with the same --request-id returns the job already made, even after the project was edited; a new request against a changed project answers 409. Answers 202 with the job; follow it with infimal jobs get.
- `ID`: Studio project ID (required)
- `--shot`: Shot ID from the project's document (required)
- `--revision`: The revision studio projects show returned (required)
- `--request-id`: A UUID you choose; resending it returns the job it made, not a second one (required)
- `--surface`: Which endpoint: images are billed per image, video per generated second (required)
- `--request`: The generation body without its prompt, as JSON or @file, e.g. {"model": "…", "preset": "I1"} (required)
### infimal admin models list
List the administrator model catalogue
`GET /v1/admin/models`
### infimal admin models set
Create or update a listing from a JSON body
`PUT /v1/admin/models/{id}`
- `ID`: Model listing ID (required)
- `--file`: JSON request file; - reads stdin (required)
### infimal admin models create
Create a private, flat-priced media listing and grant it, in one step
`PUT /v1/admin/models/{id}`
A job listing needs its surface, a pinned flat price, private visibility and a grant for every tenant that submits to it; this sets all four (PUT /v1/admin/models/{id}, then PUT /v1/admin/grants per --grant). Running it again changes nothing.
- `ID`: Model listing ID (required)
- `--kind`: The surface the listing serves (required)
- `--flat`: USD per unit: per image on images, per second of encoded video on video (required)
- `--grant`: Tenant ID to grant; repeatable
- `--display-name`: Display name
- `--public`: List it for every tenant (default private)
### infimal admin models set-price
Set the one price a model is billed at
`PUT /v1/admin/models/{id}`
Give --input and --output together, or --flat, or --unpriced: an input price with no output price would bill output tokens at zero. A price set here is pinned unless --unpin.
- `ID`: Model listing ID (required)
- `--input`: USD per million input tokens
- `--output`: USD per million output tokens
- `--cache-read`: USD per million cache-read tokens
- `--flat`: USD per request, for image and speech surfaces
- `--unpriced`: Take it off sale; the router refuses it
- `--unpin`: Let the five-minute refresher move this price again
### infimal admin models set-image
Point a listing at the image that serves it, or clear it with none
`PUT /v1/admin/models/{id}`
The API parses the image id and refuses a malformed one; none clears the pointer, which is what a listing bought from a provider carries.
- `ID`: Model listing ID (required)
- `IMAGE`: gi:blake3:<64 hex> (the bare hex is accepted), or none (required)
### infimal admin models publish
Set any listing's visibility
`POST /v1/models/{id}/publish`
- `ID`: Model listing ID (required)
- `--public`: Public visibility (defaults to true; false makes private)
### infimal admin platform show
Inspect the platform declaration: listings, shares and shared resources
`GET /v1/admin/platform`
### infimal admin platform plan
Validate and diff a Platform declaration
`POST /v1/admin/platform/plan`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Executes the Python declaration and asks the API for a read-only plan of its listings, shares, shared resources and the nodes it would render (D419): each change, every refusal and warnings such as an unpriced slot class. Contains no credential values.
- `--module`: Python module declaring one Platform; default platform_config
### infimal admin platform apply
Render the platform's nodes, then price, publish and share its listings
`PUT /v1/admin/platform`
Needs Python 3.11+ with the infimal SDK installed: it runs your module.
Plans first; a plan with a refusal stops here. Each node the plan creates is rendered with POST /v1/admin/nodes and its script written to --out <dir>/<node>.sh (mode 600; the one-use credentials are in it); a node whose body changed is refused unless --rerender, because a node is re-rendered, never edited in place. Then it atomically replaces listings, shares and publications at the planned version. A listing the declaration drops goes off sale; a share it drops is revoked. --plan sends no write.
- `--module`: Python module declaring one Platform; default platform_config
- `--yes`: Apply without asking
- `--out`: Directory each node's rendered bootstrap script is written to, as <node>.sh
- `--rerender`: Render a new bootstrap script for a node whose body changed
### infimal resources list
List shared resources granted to you
`GET /v1/resources`
### infimal resources show
Inspect a shared resource's model aliases
`GET /v1/resources/{name}`
- `NAME`: Published resource name (required)
### infimal admin sources list
List model sources and their health
`GET /v1/admin/sources`
### infimal admin sources add
Add a provider a model is bought from
`POST /v1/admin/sources`
Give --cost-input and --cost-output together, or --cost-flat. The key is sealed under INFIMAL_MASTER_KEY and cannot be read back. A `jobs` object in --wire-config makes the source a venue for its listing's video jobs, placed and collected by infimal-api with this key: {"jobs": {"dialect": "openrouter"}}, with optional concurrency (1), spill_after_s (unset: never while an own replica serves the listing) and max_resolution (720p).
- `--model`: Model listing ID (required)
- `--name`: Unique source name; the credential is sealed against it (required)
- `--base-url`: The provider's API base URL (required)
- `--upstream-model`: The id the provider knows this model by
- `--key-env`: Environment variable holding the upstream key, never the key itself (required)
- `--cost-input`: USD per million input tokens the provider charges
- `--cost-output`: USD per million output tokens the provider charges
- `--cost-flat`: USD per request the provider charges
- `--wire-config`: Wire configuration: JSON, or @file
- `--priority`: Higher is tried first
### infimal admin sources set
Update a source from a JSON file
`PUT /v1/admin/sources/{id}`
- `ID`: Resource ID (required)
- `--file`: JSON request file; - reads stdin (required)
### infimal admin sources rekey
Replace a source credential
`POST /v1/admin/sources/{id}/rekey`
- `ID`: Resource ID (required)
- `--key-env`: Environment variable holding the new upstream key (required)
### infimal admin sources delete
Delete a model source
`DELETE /v1/admin/sources/{id}`
- `ID`: Resource ID (required)
### infimal admin grants set
Grant or revoke a tenant's access to a model
`PUT /v1/admin/grants`
- `TENANT`: Tenant ID (required)
- `MODEL`: Model ID (required)
- `--granted`: true grants access; false revokes it (required)
### infimal admin tenants show
Inspect a tenant: balance, keys and terms
`GET /v1/admin/tenants/{id}`
- `ID`: Resource ID (required)
### infimal admin tenants financial
Inspect bounded grant accounting and reconciliation status
`GET /v1/admin/tenants/{id}/financial`
- `ID`: Resource ID (required)
- `--grant`: Optional grant ID for canonical outcomes
- `--after`: Outcome page cursor; requires grant
- `--after-unknown`: Unknown-reservation cursor; requires grant
- `--limit`: Page size (1..100; default 25)
### infimal admin tenants set-discount
Set a tenant's discount in basis points
`PUT /v1/admin/tenants/{id}`
- `ID`: Resource ID (required)
- `BPS`: Discount in basis points (0..10000) (required)
### infimal admin financial policy
Read the durable fleet credit-risk policy
`GET /v1/admin/financial/policy`
### infimal admin financial set-policy
Explicitly configure new-grant admission and the fleet risk cap
`PATCH /v1/admin/financial/policy`
- `--enabled`: Allow new grants; does not change existing allocations (required)
- `--cap-micros`: Fleet risk cap in micro-USD (0..5000000; cannot fall below allocated risk) (required)
### infimal admin topup
Credit a tenant using a unique payment reference
`POST /v1/admin/tenants/{id}/topup`
- `TENANT`: Tenant ID (required)
- `USD`: Positive USD amount (up to six decimal places) (required)
- `--reference`: Stable payment reference; reuse only for the same payment (required)
### infimal admin audit
Inspect administrative audit events
`GET /v1/admin/audit`
- `--limit`: Maximum number of rows
### infimal admin nodes list
List enrolled GPU nodes and what each serves
`GET /v1/admin/nodes`
A replica still loading its weights is listed as not ready; the provider instance id says which machine to terminate.
### infimal admin nodes bootstrap
Mint a rented box's one-use credentials and print its bootstrap script
`POST /v1/admin/nodes`
Give --image (per image, D359) or --engine (per GPU), not both. The script alone goes to stdout and the notice to stderr, so `infimal admin nodes bootstrap … > bootstrap.sh` is a runnable file; run it on the box as root. It carries a one-use enrolment token and a one-use tailnet key and is the only copy of either. The --engine grammar is comma-separated key=value: gpu, surface (images|video), model (the listing) and checkpoint (what the runner loads) are required; loras_file=<registry.json> (read here and sent inline), lora_registry=<path on the box>, default_loras=a:1+b:0.9, storage_dtype, offload=true|false, backend=wan|diffusers|sglang and sglang_config=<path> are optional.
- `--node-id`: What to call the box; it joins as node-<id> (required)
- `--region`: Region (default us-west-1)
- `--slot-class`: A key of deploy/fleet/slot-classes.json, e.g. lambda/gpu_8x_a100_80gb_sxm4 (required)
- `--gpus`: GPUs on the box (required)
- `--image`: Per image (D359): one image or a list, as JSON or @file; repeatable
- `--engine`: Per GPU (the older form): gpu=0,surface=images,model=<listing>,checkpoint=<hub id>[,…]; repeatable
- `--residency`: How the node parks (per-image form); the node's default is checkpoint
- `--cuda-checkpoint`: The node's cuda-checkpoint: a path on the box, or {"url", "sha256"} as JSON or @file
- `--provider`: Who the box was rented from; the API assumes lambda when absent
- `--provider-instance-id`: The provider's id for this instance, so nodes list says which one to terminate
- `--torch-index-url`: The CUDA torch index the runners install from (default: cu128)
- `--vllm-version`: The exact vLLM a chat image runs on, e.g. 0.32.0; required with a chat image (D409)
- `--image-id`: Materialise this image from the librarian instead of pulling from the Hub
- `--hf-token`: Ask for the fleet's Hugging Face token; only for gated weights on the Hub path
### infimal admin nodes reconcile
Render a node's declared body again for its box to apply in place
`POST /v1/admin/nodes/{node_id}/reconcile`
D423. Re-renders the bootstrap of the node's newest declared body (the one POST /v1/admin/nodes recorded, D419) with new one-use credentials. The script alone goes to stdout and the two steps to stderr: copy it to the box, run it as root; the node reconciles its adopt manifest in place (D422). --plan mints nothing: it compares the adopt manifest the box reports running with the one the declared body renders and prints both digests.
- `NODE`: Node ID (required)
### infimal admin nodes retire
Stop placing on a node; does not terminate it
`DELETE /v1/admin/nodes/{node_id}`
- `NODE`: Node ID (required)
### infimal admin nodes logs
Read a GPU node's journal: its engines, the node and the stager
`GET /v1/admin/nodes/{node_id}/logs`
D424. The node streams its own journal over the tailnet and the api relays it to you, so no ssh is needed: `infimal admin nodes logs b200-0 --unit engines --since 1h`, or `--follow` to watch a box come up. Lines are journalctl's, UTC, with each engine named infimal-engine-<image>-gpu<N>; the runner's own lines are JSON. A read stops at 16 MiB or 30 s and a follow at 64 MiB or ten minutes, with a last line saying which.
- `NODE`: Node ID (required)
- `--unit`: all (the default), node, stage, engines, a replica id, or infimal-engine@<image>-gpu<N>
- `--since`: How far back: 30s, 15m, 1h, 2d, or an RFC 3339 time
- `--lines`: The last N lines (200 by default, at most 10000)
- `--follow`: Keep printing lines as they are written; ends after ten minutes
- `--format`: text (the default), or journald's JSON, one entry a line
- `--out`: Write the lines to this file instead of standard outputLooking for the human-readable guides?Explore the documentation