Cloud CLI — iterion remote
iterion remote turns the local CLI into a full client for a remote iterion instance (cloud or self-hosted iterion server): every operator and admin capability of the HTTP API is reachable as a typed subcommand. The raw escape hatch iterion remote api <METHOD> <path> remains for anything not wrapped; iterion remote routes / iterion remote openapi enumerate the live surface.
All commands honour the global --json flag: the output is then the server's response body verbatim (lossless, stable for jq).
The same surface is also exposable to MCP agents (Claude Code, desktop, Cursor): iterion mcp serves remote_* tools over the same stored credential, plus the remote_api escape hatch — see mcp-server.md.
Setup
Interactive (browser)
iterion remote login https://iterion.example.comOpens the instance's /cli-auth page; you approve in the studio and a personal access token (iap_…) is stored in ~/.iterion/cli-auth.json.
Headless alternatives:
iterion remote login https://… --token iap_… # existing PAT
iterion remote login https://… --email e@x --password … # mints a CLI PATiterion remote status shows the logged-in instance + account; iterion remote logout forgets the credential.
CI / scripting (environment only)
export ITERION_REMOTE_URL=https://iterion.example.com
export ITERION_REMOTE_TOKEN=iap_… # fallback: ITERION_TOKEN
iterion remote runs list --json | jq '.runs[].id'When ITERION_REMOTE_URL is set the stored config file is ignored entirely (a stored token is never sent to a different host). ITERION_REMOTE_TEAM / ITERION_REMOTE_ORG set the tenant scope.
Tenant scope (teams / orgs)
Team-scoped commands (secrets, api-keys, webhooks, forge, audit team, bindings) resolve their team in this order:
--team <id>flagITERION_REMOTE_TEAM- the default persisted by
iterion remote teams switch <id> - the account's active team (from
/api/auth/me)
A PAT's identity team is pinned at mint time, so teams switch mints a new token pinned to the target team, stores it, and revokes the previous CLI token (matched by fingerprint — tokens you minted for other purposes are never touched). Org-scoped commands (--org / ITERION_REMOTE_ORG / orgs switch) work the same way but without re-minting (org scope is path-based).
The launch → follow → inspect recipe
# Launch a local .bot file (its source is uploaded inline) and tail it.
# What travels is the bot written out as ONE file: a bot in several files
# (`import "lib/x.bot"`) is flattened, and every prompt `{{include "x.md"}}`
# is resolved beside the file that carries it, before the upload — a server
# pod has none of the files beside the source. A bot in one file with no
# include uploads byte-identical. The run's identity is that text: a resume
# (`runs resume --file`) re-flattens the same unit into the same text, an
# edited fragment or include into another, which the server refuses without
# --force.
iterion remote runs launch ./review.bot --var repo=org/app --follow
# Or launch a catalog bot by id:
iterion remote runs launch --bot whats-next --follow
# Separately:
id=$(iterion remote runs launch ./wf.bot --json | jq -r .run_id)
iterion remote runs follow "$id" # exit 1 if the run fails
iterion remote runs log "$id"
iterion remote runs artifacts "$id"
iterion remote runs files "$id" src/main.go --diff
iterion remote runs merge "$id" --strategy squashruns follow polls GET /api/runs/{id}/events by seq cursor (default every 2s, --interval to tune) — no WebSocket dependency, so it works through any proxy.
Attachments: --attach name=./file uploads via POST /api/runs/uploads and wires the returned id into the launch. runs upload <path> does the staging step alone and prints the upload id.
Command tree
| Group | Commands |
|---|---|
runs | list · launch · get · events · follow · log · workflow · artifacts · files · commits · cancel · pause · resume · fork · send · merge · conflicts · rename · delete · preview-cost · upload · stats · repos |
bots | list · get · put · overlay · install · upload |
marketplace | list · get · download · submit · install · uninstall · moderation |
issues | list · get · create · update · delete · transition · comment · push · pulls |
labels / board | list · rename · merge · delete / get · set · bind · show · unbind |
dispatcher | status · state · start · stop · pause · resume · refresh · reload · config · issue · cancel |
triggers | list · get · create · update · delete · emit |
schedules | list · create · delete (team-scoped, cloud recurring bots) |
teams | list · create · switch · members · invitations |
orgs | list · switch · members · invitations · usage · teams |
me | password · sessions-revoke-all · sso-links |
tokens | list · create · revoke |
secrets / api-keys | list · set/create · rotate/update · delete (--scope team|me) |
bindings | per-bot secret bindings (list · create · delete) |
webhooks | list · get · create · update · delete · rotate · deliveries |
forge | connections · refresh · repo-bots · oauth-apps · integrations |
audit / usage / limits | audit team|org|admin · org usage · cost limits |
memory | usage · docs · doc get|put|delete · export · import (--name space) |
admin | orgs · users · dlq · llm · caps · bots · roles · sandbox (super-admin; llm api-keys/llm oauth = the platform fallback credentials — rotate without a redeploy, see cloud-llm-credentials.md; caps = the runtime usage-cap percentages — retune without a restart, see usage-caps.md; bots = platform bot overrides — push any bot without an image rollout, roles/sandbox = runtime webhook role bindings + sandbox: auto image, see platform-bots.md) |
sso | providers · domains (org-scoped) |
plugins | list · enable · disable · install · uninstall · config |
pool | status · history · share · pause · resume · withdraw · donors · policy — lend your own LLM subscription or personal metered key to the shared credential pool, bounded by ceilings you set on share (--max-usd-day/-week, --max-runs-day, --max-concurrent, --from-hour/--to-hour, --bots). donors is the operator view of the pool's policy and its lenders; policy is the operator write side (--enabled, --name, audience flags — the audience is replaced whole). |
server | info · health |
Structured mutation payloads follow the --data '<json>' / --data @file.json / --data @- (stdin) convention shared with remote api.
Bind the team to a GitHub project board
Makes a Projects v2 board and the native board the same tickets: the board's Status column becomes two-way with the native columns, and its Area/Mode/Priority land as area:/mode:/prio: card labels.
iterion remote forge connections # find the connection id
iterion remote board bind --project SocialGouv/203 --connection conn_123
iterion remote board show # the EFFECTIVE map + coverageField and option ids are discovered by name at bind time, never hardcoded. A board with different columns binds with --status-map "Todo=ready,Doing=in_progress,Shipped=done"; the map must be injective (two columns on one state is refused, naming the collision). --sync-every 2m (default; 0/off disables, floor 1m) sets the reconciliation interval, which the server runs elected per tenant. Full runbook, incl. the permissions the credential needs and what the sync deliberately refuses to do: github-board-sync.md.
Give a bot account the iterion-bot avatar
A GitLab group/project access token's bot user gets the mascot avatar at connect time. For a connection created before that, or a dedicated account the forge does not flag as a bot (Forgejo, a hand-made GitLab user), apply it once:
iterion remote forge connections avatar <connection-id> # a flagged bot account
iterion remote forge connections avatar <connection-id> --force # a dedicated, unflagged accountRefused on an OAuth connection (a person's account) and on GitHub, which has no avatar or App-logo API — the error names where to upload it by hand. Full policy + the manual GitHub App upload: brand.md.
Refresh GitHub App grants
After changing a GitHub App installation's permissions on GitHub, refresh the connection immediately instead of waiting for a periodic worker or restarting the server:
iterion remote forge refresh <connection-id>This command applies only to GitHub-App connections. It re-probes the live installation, replaces the connection's stored grant map, and forces a fresh installation-token mint. The table deliberately shows GRANTED (what the installation allows) beside TOKEN (what the newly minted token actually carries); --json returns both maps plus missing-permission lists. The active team is resolved by the normal remote-team precedence.
Secrets hygiene
Secret values are never taken as command arguments (argv leaks via process lists). secrets set, secrets rotate and api-keys create read the value from --from-env VAR, --from-file path, or stdin:
printf '%s' "$GITLAB_TOKEN" | iterion remote secrets set gitlab-token
iterion remote api-keys create --provider anthropic --name prod --from-env ANTHROPIC_API_KEYErrors
Any non-2xx response surfaces as HTTP <code> <METHOD> <path>: <first line of the server's message> and a non-zero exit. Admin commands do no client-side role check — a 403 from the server is the answer.
Everything else
The typed tree covers the operator surface; for the long tail (examples, filesystem browse, CDP proxies, anything new):
iterion remote routes # live method+path inventory
iterion remote api GET /api/… # authenticated raw call