iframe CLI · v0.2.1
Operate your iFrame estate from the terminal.
The iframe CLI puts your iFrame instances under your fingertips:
lifecycle, status, customer-scoped SSH access, usage, and billing.
Provider-agnostic by design — your scripts never see cloud-provider instance
types or region codes.
Install
One command. Node 20+.
curl -fsSL https://cloud.iframe.ai/install.sh | sh
Other distributions
- npm
npm install -g @iframeai/cli - brew
brew install iframeai/tap/iframe
The CLI is a single self-contained binary
(iframe.cjs) bundled with esbuild. It uses the built-in
fetch; no extra HTTP stack. Verify with
iframe --version.
Quick start
Login, list, reboot — in under 60 seconds.
- Issue a key. Open the dashboard and create one under Sidebar → CLI Keys → New key. The plaintext is shown once.
- Save it. Run
iframe login. The key lands at~/.iframe/credentialswith mode 0600. - Confirm.
iframe whoamiverifies connectivity and prints your scopes. - Use.
iframe instances listshows your active instances.iframe instances rebootperforms an in-OS soft reboot that preserves identity, IP, and storage.
# 1. Issue a key in the dashboard
# cloud.iframe.ai → Sidebar → CLI Keys → New key
# (copy the value once — it is only shown at creation)
# 2. Save it to ~/.iframe/credentials
iframe login --profile default --api-url https://cloud.iframe.ai
# 3. Confirm
iframe whoami
# 4. List your iFrame instances
iframe instances list
# 5. Soft reboot — preserves instance identity, public IP, and storage
iframe instances reboot inst-123e4567-e89b-12d3-a456-426614174000 --yes
Authentication
Keys, scopes, and storage.
Key shape
Keys are 12-character public IDs and 32-character secrets, prefixed by environment. Only a salted SHA-256 of the secret is stored on our side; the plaintext is shown once at creation.
ifk_live_<keyId>_<secret>
# or ifk_test_<keyId>_<secret> for non-prod
Override per invocation
IFRAME_API_URL=https://cloud.iframe.ai \
IFRAME_API_KEY=ifk_live_........ \
iframe instances list --output json | jq '.[] | .id'
Scopes
Each key carries a coarse scope set. Mint narrow keys for CI; mint wider keys for on-call rotations.
instances:readlist, get, statusinstances:lifecyclesoft rebootinstances:accessSSH grants on a nodeinstances:reprovisionrebuild root disk from launch imageinstances:webhookconfigure per-instance notification webhookusage:readcurrent-month accrued cost per resourcebilling:readlist and inspect invoicesRevocation
Revocations are immediate. The next request from a revoked key receives HTTP 401 / exit 3. Revoke from Sidebar → CLI Keys.
Output formats
Table, JSON, YAML.
--output table is the default on a TTY. When stdout is not a TTY (e.g. piped),
json is auto-selected so jq Just Works. Use --output yaml
for hand-readable structured output.
# piping into jq → JSON auto-selected
iframe instances list | jq '.[] | select(.lifecycle=="live") | .id'
# explicit YAML
iframe instances get inst-... --output yaml
# explicit table (forces TTY rendering even when piped)
iframe usage --output table
Lifecycle vocabulary
Four states. Provider-agnostic.
The CLI never exposes provider lifecycle words. Both the iFrame application status and the underlying cloud state are mapped to a small canonical vocabulary:
| Lifecycle | Meaning |
|---|---|
live | the instance is up and reachable |
halted | the instance is stopped, terminated, or decommissioned |
transient | the instance is starting, stopping, or rebooting |
unknown | the orchestrator could not derive the lifecycle |
Mutating commands (instances reboot, instances reprovision, keys add) refuse with HTTP 409 / exit 5 when lifecycle is anything other than live. The reply includes the current lifecycle value so scripts can branch.
Command reference
Every command, every flag.
Auth & profile
iframe login
Save an API key for a profile to ~/.iframe/credentials (mode 0600). Prompts for the key on stdin.
iframe login [--profile <name>] [--api-url <url>]
--profile default profile name (also: IFRAME_PROFILE)
--api-url API base URL (default: https://cloud.iframe.ai)
iframe logout
Remove a saved profile from the credentials file.
iframe logout [--profile <name>]
iframe whoami
Inspect the active credentials, the bound organization, and the scopes on the active key.
iframe whoami
# table:
# API URL : https://cloud.iframe.ai
# Key ID : kmpkhm5qeprr
# Key name: ci pipeline
# Org : Acme (org-uuid)
# Scopes : instances:read, instances:lifecycle, usage:read
iframe doctor
Diagnose connectivity, key validity, and the bound org. Useful as a CI smoke check.
iframe doctor
# config: OK (api_url=https://cloud.iframe.ai)
# auth: OK (key=kmpkhm5qeprr, scopes=5)
# org binding: Acme (org-uuid)
# api version: v1
Instances
iframe instances list
List every iFrame instance in the active org. Lifecycle is always re-derived from the live cloud-provider state at the boundary; the value you see is current as of the request.
iframe instances list [--state live|halted|transient|unknown|all]
--state filter by canonical lifecycle (default: shows everything)
Bad --state values are rejected with exit 2 and a list of valid values.
iframe instances get <id>
Show full details for one iFrame instance: lifecycle, IPs, attached volumes, hardware specs, rate, contract dates.
iframe instances get inst-123e4567-e89b-12d3-a456-426614174000
# Returns 404 / exit 4 if the id does not exist in your org.
iframe instances status <id>
Liveness probe: the canonical lifecycle, plus an out-of-band SSH reachability check with latency and a redacted reason on failure.
iframe instances status inst-...
# {
# "lifecycle": "live",
# "public_ip": "203.0.113.42",
# "ssh": { "ssh_reachable": true, "latency_ms": 38 }
# }
iframe instances reboot <id>
Soft reboot via in-OS systemctl reboot --no-block. Preserves the instance's identity, public IP, private IP, and root volume. The cloud-provider lifecycle API is never called, so any underlying reservation remains intact.
iframe instances reboot inst-... [-y|--yes]
-y, --yes skip the interactive confirmation prompt
# Refuses with exit 5 if lifecycle != live; the reply includes the
# current lifecycle so scripts can branch:
# {"error":"Soft reboot unavailable: instance is halted — start it first",
# "exit_code":5,"lifecycle":"halted"}
iframe instances reprovision <id>
Rebuild the instance from its original launch image — a "clean slate" reset that preserves every piece of identity that matters and wipes everything that doesn't. Three steps run in order:
- Root replacement. The root volume is replaced from the same launch image the instance was originally provisioned from. Instance ID, public IP, private IP, and the underlying reservation are preserved.
- Ephemeral wipe. Every non-root NVMe instance-store device is
blkdiscard'd. Persistent data volumes are not touched. - Soft reboot. Issued at the hypervisor level so the new root sees a clean kernel. Skipped automatically if step 2 wiped nothing.
iframe instances reprovision inst-... [-y|--yes]
-y, --yes skip the interactive confirmation prompt
# Returns immediately with a task_id; the orchestrator drives the
# rest in the background. Track progress with `iframe instances get`:
# lifecycle — `live` once back online
# last_reprovision_state — pending → in-progress → succeeded
# last_cleanup — wipe_state and reboot_state
#
# Refuses with exit 5 if lifecycle != live or another reprovision
# is already in flight on the same instance.
SSH grants survive automatically. Active grants stored against the instance are replayed onto the new root: each cust_* user is recreated, its public key is reauthorized, and its --sudo flag is restored. Grants created before pubkey persistence (legacy) are marked pending_resync and must be re-issued by the customer.
Customer-scoped SSH grants
iframe keys list <id>
List active and revoked SSH grants on an iFrame instance. Each grant maps an SSH public key to a per-customer Linux user named cust_<keyId8>_<label>. The sudo field reflects whether the grant was created with --sudo.
iframe keys list inst-...
# [
# {
# "grant_id":"…",
# "linux_user":"cust_kmpkhm5q_alice",
# "pubkey_fingerprint":"SHA256:…",
# "pubkey_type":"ed25519",
# "status":"active",
# "sudo": false,
# "created_at":"2026-04-26T07:48:56Z"
# }
# ]
iframe keys add <id>
Land a customer-scoped Linux user with a public key. The user is named cust_<keyId8>_<label> and never shares the default OS user (ubuntu). Linux users are created on the live node over the orchestrator's admin SSH channel — no provider-specific handoff is exposed to the caller.
iframe keys add <id> --user <label> --pubkey <path>
[--note <text>] [--sudo]
--user label segment, [a-z0-9-]{2,16}, becomes part of cust_…_…
--pubkey path to a public key file (or '-' to read stdin)
--note optional, free-form note saved with the grant
--sudo grant the cust_ user NOPASSWD:ALL via /etc/sudoers.d/.
Off by default — opt in per grant.
# Example — no sudo (default)
iframe keys add inst-... --user alice --pubkey ~/.ssh/alice.pub
# {
# "grant_id":"…",
# "linux_user":"cust_kmpkhm5q_alice",
# "pubkey_fingerprint":"SHA256:…",
# "sudo": false,
# "ssh_login_hint":"ssh cust_kmpkhm5q_alice@203.0.113.42"
# }
# Example — opt-in NOPASSWD root
iframe keys add inst-... --user alice --pubkey ~/.ssh/alice.pub --sudo
When --sudo is set, the orchestrator validates a single <linux_user> ALL=(ALL) NOPASSWD:ALL line with visudo -cf before installing it as /etc/sudoers.d/iframe-<linux_user> (mode 0440, owned by root). The flag is recorded on the grant and replayed automatically across instances reprovision. Refuses with exit 5 when lifecycle is not live; exit 2 on a malformed label or unparseable public key; exit 6 if the orchestrator can't reach the instance over its admin channel.
iframe keys remove <id> <grantId>
Revoke an SSH grant and remove the Linux user. If the instance is currently live the user is deleted in-OS over SSH; if not, the grant is marked revoked in the database and the user is removed automatically the next time the node is live.
iframe keys remove inst-... <grant-id>
# {
# "revoked": true,
# "ssh_cleanup": { "attempted": true, "ok": true },
# "message": "Linux user removed and grant revoked"
# }
Usage
iframe usage
Per-resource accrued cost for a calendar month. Defaults to the current month. Same numbers your dashboard shows.
iframe usage [--month YYYY-MM]
--month defaults to the current calendar month
# Output (JSON)
# {
# "period": "2026-04",
# "total_accrued": 4321.56,
# "currency": "USD",
# "items": [
# {
# "iframe_id":"inst-…",
# "lifecycle":"live",
# "rate_per_hour":2.50,
# "rate_per_month":1800.00,
# "accrued_cost":1234.50,
# "days_with_cost_records":21
# }
# ]
# }
A malformed --month is rejected with exit 2.
Billing
iframe billing invoices
List invoices for the active org. Optional --year filter.
iframe billing invoices [--year YYYY]
iframe billing invoice <period>
Show one invoice. <period> is YYYY-MM.
iframe billing invoice 2026-04
# Returns 404 / exit 4 if the invoice does not exist.
# Bad period strings are rejected with exit 2.
Notifications
iframe instances webhook *
Per-instance signed webhook for node-disruption events. Configure, rotate, test, list deliveries, and remove. Full reference, payload contract, signature recipe, and a drop-in receiver live on a dedicated page.
iframe instances webhook set <id> --url <https-url> --events advisory,warning,offline
iframe instances webhook get <id>
iframe instances webhook rotate <id>
iframe instances webhook test <id> [--event advisory|warning|offline]
iframe instances webhook deliveries <id> [--limit N]
iframe instances webhook remove <id>
Requires scope instances:webhook. See Notifications for the wire format, the three event severities, the HMAC recipe, and a zero-dependency Node receiver.
Discoverability. iframe --help lists every command;
iframe <command> --help drills in. Every subcommand also accepts
--profile and --output.
Environment
Variables the CLI honors.
| Variable | Effect |
|---|---|
IFRAME_API_KEY | full API key — overrides the credentials file |
IFRAME_API_URL | API base URL — overrides the credentials file |
IFRAME_PROFILE | profile name (default: default) |
IFRAME_CONFIG | path to the credentials file (default: ~/.iframe/credentials) |
IFRAME_YES | set to 1 to skip interactive confirmation prompts |
Exit codes
Predictable signals for scripts.
| Code | Meaning |
|---|---|
0 | success |
2 | misuse — bad arguments or validation error |
3 | auth failure (401/403, missing key, bad scope, revoked key) |
4 | not found (404) |
5 | conflict (409 — e.g. lifecycle is not live) |
6 | server error (5xx) |
7 | network error (DNS, TLS, timeout) |
Security
What the server sees. What your terminal sees.
Provider-agnostic surface
Every CLI response is redacted server-side: no provider names, no instance types, no region codes, no vol-… or i-… identifiers. Only your iFrame IDs cross the wire.
Hashed at rest
We store a salted SHA-256 of each key; the plaintext is never written to disk. Revocation is immediate — the next request from a revoked key receives exit 3.
Cross-tenant isolation
A key issued to org A cannot read or mutate org B. SSH grants land distinct cust_* Linux users; iFrame instances are single-tenant by policy, so root is gated behind the explicit --sudo opt-in per grant.
Ship faster from the terminal.
Your iFrame estate, scriptable, redacted, and one npm install away.