iframe CLI · v0.1
Operate your iFrame fleet 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+.
npm install -g @iframeai/cli
Other distributions
- brew
brew install iframeai/tap/iframe - curl
curl -fsSL https://cloud.iframe.ai/install.sh | sh
The CLI is a single Node entry point with one runtime dep
(commander). It uses the built-in fetch; no extra HTTP stack.
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 fleet.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 nodeusage: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, 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"}
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>.
iframe keys list inst-...
# [
# {
# "grant_id":"…",
# "linux_user":"cust_kmpkhm5q_alice",
# "pubkey_fingerprint":"SHA256:…",
# "pubkey_type":"ed25519",
# "status":"active",
# "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 underlying default user (e.g. ec2-user, ubuntu). Combined with the iFrame-isolation AppArmor + iptables profile shipped with the orchestrator, tenants on shared nodes cannot see each other.
iframe keys add <id> --user <label> --pubkey <path> [--note <text>]
--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
# Example
iframe keys add inst-... --user alice --pubkey ~/.ssh/alice.pub
# {
# "grant_id":"…",
# "linux_user":"cust_kmpkhm5q_alice",
# "pubkey_fingerprint":"SHA256:…",
# "ssh_login_hint":"ssh cust_kmpkhm5q_alice@203.0.113.42"
# }
Refuses with exit 5 when the instance lifecycle is not live. Refuses with exit 2 on a malformed label or unparseable public key.
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.
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 Linux users confined by AppArmor + iptables — not by cloud security groups.
Ship faster from the terminal.
Your iFrame fleet, scriptable, redacted, and one npm install away.