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

  • brewbrew install iframeai/tap/iframe
  • curlcurl -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.

  1. Issue a key. Open the dashboard and create one under Sidebar → CLI Keys → New key. The plaintext is shown once.
  2. Save it. Run iframe login. The key lands at ~/.iframe/credentials with mode 0600.
  3. Confirm. iframe whoami verifies connectivity and prints your scopes.
  4. Use. iframe instances list shows your fleet. iframe instances reboot performs 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, status
instances:lifecyclesoft reboot
instances:accessSSH grants on a node
usage:readcurrent-month accrued cost per resource
billing:readlist and inspect invoices

Revocation

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:

LifecycleMeaning
livethe instance is up and reachable
haltedthe instance is stopped, terminated, or decommissioned
transientthe instance is starting, stopping, or rebooting
unknownthe 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.

VariableEffect
IFRAME_API_KEYfull API key — overrides the credentials file
IFRAME_API_URLAPI base URL — overrides the credentials file
IFRAME_PROFILEprofile name (default: default)
IFRAME_CONFIGpath to the credentials file (default: ~/.iframe/credentials)
IFRAME_YESset to 1 to skip interactive confirmation prompts

Exit codes

Predictable signals for scripts.

CodeMeaning
0success
2misuse — bad arguments or validation error
3auth failure (401/403, missing key, bad scope, revoked key)
4not found (404)
5conflict (409 — e.g. lifecycle is not live)
6server error (5xx)
7network 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.

Issue an API key →