CLI
Reference for the appstrate command-line tool. Install and operate an instance, sign in, call the API, run agents, edit packages locally.
The appstrate CLI installs and operates an instance, signs you in to any instance, and drives its API from a terminal or a script. It is a single self-contained binary: no Node.js or other runtime is required on the host (except Bun for a Tier 0 install). This page covers the common cases. The full flag tables are in the CLI README.
Global flags work on every command: -p, --profile <name> selects a profile, --insecure allows an instance without HTTPS (tokens then travel in plaintext, so use it on a trusted network only), and --version prints the version.
Get the CLI
curl -fsSL https://get.appstrate.dev | bashDownloads the signed binary for macOS or Linux (x64 and arm64) into ~/.local/bin, and verifies it. In an interactive terminal it then stops and tells you to run appstrate install. Unattended (bash -s -- --yes, CI=true, or stdout not a terminal), it runs appstrate install --yes itself. To install only the CLI in every case, pipe into APPSTRATE_NO_LAUNCH=1 bash. On Windows, run it inside WSL2. Update with appstrate self-update.
bun install -g appstrate # needs Bun 1.3.9 or later
bunx appstrate <command> # or run it once without installingThe npm package is appstrate. Update with bun update -g appstrate. This is the route on native Windows.
The two channels are independent. If both are installed, appstrate doctor lists every appstrate on your PATH and which one wins. See Upgrading the CLI.
Driving the CLI from a coding agent
The repository ships an operating manual written for a coding agent: apps/cli/AGENTS.md. It covers the zero-to-first-run recipe, the rules of engagement and a curl to appstrate api cheat sheet. With a logged-in profile the CLI injects the bearer token itself, so the agent never sees it. An API key passed through the environment or --api-key is readable by the process that sets it, so give an agent a key scoped to what it may do. See also Skills and coding agents.
Commands
| Command | Purpose |
|---|---|
install, start, stop, restart, logs, status, uninstall | Install an instance and manage its Docker stack |
login, logout, whoami, token | Sessions |
org, space | Pin the organization and space that commands act in |
api, openapi | Call the REST API, explore its schema |
run | Run an agent, on the instance or locally |
models | List the model presets an instance exposes |
packages | Edit a package in a local folder, push it, publish it |
code | Sync skills and agents into Claude Code and Codex |
self-update, doctor | Maintain the CLI itself |
runner | Install the Firecracker runner daemon on a KVM host |
Headless and CI runs
appstrate run can authenticate without appstrate login: give it an API key and the instance, and it works on a CI runner or any machine with no profile. appstrate api reads the same key and instance (see Call the API with an API key). The other commands (openapi, whoami) still need a profile.
| Variable | Meaning |
|---|---|
APPSTRATE_API_KEY | An apst_ API key. Same as --api-key, and the flag wins when both are set. It replaces the profile's session entirely, so the run is made as the key. |
APPSTRATE_INSTANCE | Base URL of the instance. When unset, the active profile's instance is used. |
APPSTRATE_SPACE_ID | Id of the space (spc_...). Not needed for a platform run. A remote run with remote integrations (the default, see below) requires it, and the command stops without it. |
APPSTRATE_ORG_ID | Organization id. Not needed for a platform run. Required by --model-source preset, which refuses to start without it. |
With an API key, the organization and the space come only from APPSTRATE_ORG_ID and APPSTRATE_SPACE_ID, never from the active profile: a key is pinned to one organization and one space. X-Org-Id is ignored with an API key (the key is bound to its organization); only X-Space-Id is checked, and one that disagrees with the key is answered with 403. Leave both unset for a platform run, and when you set them, set the ones the key is pinned to. An empty value counts as unset. Only the instance falls back to the profile. If you upgrade the CLI and a job used to rely on the profile's space, it now needs APPSTRATE_SPACE_ID, or stops with the message No space id for a local run.
The key needs a few scopes, depending on what you run:
| You run | Scopes the key needs |
|---|---|
| A platform run | agents:run to launch, runs:read to follow the run |
A remote run of a package id (--local) | agents:read to download the bundle and read the space's run configuration, skills:read when the agent depends on a skill (the bundle carries it), agents:run to register the run |
| A remote run of a bundle path | agents:run, and agents:write too, because the run is registered with its manifest and prompt |
| A remote run with integrations (the default) | credential-proxy:call |
A remote run with --model-source preset | llm-proxy:call |
The last two are described under Remote runs. Mint the key with the narrowest list that covers your case.
Call the API with an API key
For a CI job or a script on a machine with no login, pass an apst_ API key instead of a session:
export APPSTRATE_API_KEY=apst_...
export APPSTRATE_INSTANCE=https://appstrate.example.com # optional when a profile exists
appstrate api GET /api/agents- The key replaces the profile credential entirely. The keyring is not read and no profile is needed.
--api-key <key>does the same as the variable and wins over it, but a command-line argument is visible to other local users, so prefer the variable. An empty--api-keyis refused. Leading and trailing whitespace is trimmed, and a key that still contains whitespace, a line break or a non-ASCII character is refused. - The instance is
APPSTRATE_INSTANCE, else the profile's. With neither, the command stops. - No
X-Org-IdorX-Space-Idis sent: a key is pinned to one organization and one space, and those apply. Your own-Hheaders still pass through. - An exported
APPSTRATE_API_KEYswitches everyappstrate apicall to the key. If that variable is already set forappstrate run,apiin the same shell stops using your login: another principal, no org or space headers, and the instanceAPPSTRATE_INSTANCEnames. Unset it to go back to your profile. - Only
apiandrunread the key.openapi,whoamiand the other commands still use the profile.
Platform run or remote run
The two words name two different things, and the CLI flag --remote selects the first one.
- A platform run executes on the instance, in its sandbox, as if you had clicked Run in the dashboard. It is the default for a package id, and
--remoteselects it explicitly. - A remote run (
runOrigin: "remote") executes on your own host, in theappstrateprocess, with your shell, filesystem and environment. It is what you get with--localor with a bundle path. When the run has credentials for an instance, the CLI also registers it and streams signed events back, so it shows up in the dashboard with a Remote badge. See Runs.
| Platform run | Remote run | |
|---|---|---|
| Command | appstrate run @scope/agent | appstrate run @scope/agent --local, or appstrate run ./bundle.afps-bundle |
| Where the agent runs | The instance's sandbox | Your host |
| Model | The instance's. Only --model <preset id> and --proxy <id> are accepted | Your own provider key (env), or the instance's models through the LLM proxy (preset) |
| Integrations | The platform's connections | --integrations remote (default, through the credential proxy), local (a creds file) or none |
| Reporting | Native. --report, --report-fallback and --sink-ttl are refused | --report auto by default: on when the run has credentials for an instance |
| Exit code | 0 on success, 1 for failed, timeout or cancelled | The CLI does not turn the final status into an exit code. Read status in the --output file |
Use a platform run when the agent should behave exactly as it does in the dashboard: same sandbox, model, connections and stored run configuration. A CI step that triggers a production agent and waits for its result is the typical case. Use a remote run when the agent has to act on the machine that runs the CLI (the files of a checkout, local tools), when it must use your own LLM key, or while you iterate on a bundle with --snapshot.
One limit of a remote run: only serverless apiCall integrations are available in-process, since MCP-server integrations are skipped.
Flags of a remote run
| Flag | What it does |
|---|---|
--api-key <key> | The API key, instead of APPSTRATE_API_KEY. Valid in both modes. |
--model-source <mode> | env or preset. Order of precedence: the flag, then APPSTRATE_MODEL_SOURCE, then automatic. Automatic is preset for a package id with --integrations remote, and env otherwise. |
--model <id> | With env, the model id sent to your provider (default claude-sonnet-4-5, or APPSTRATE_MODEL_ID). With preset, a preset id from appstrate models list. Without --no-inherit, a model stored in the agent's run configuration for the space is used when you pass none, and in preset mode the instance's default preset comes after it. |
--model-api <api> | env only. The protocol of your provider (default anthropic-messages, or APPSTRATE_MODEL_API). Accepted: pi-messages, anthropic-messages, openai-completions, openai-responses, openai-codex-responses, mistral-conversations. |
--llm-api-key <key> | env only. The provider key. Without it the CLI reads ANTHROPIC_API_KEY, OPENAI_API_KEY or MISTRAL_API_KEY according to the protocol, then APPSTRATE_LLM_API_KEY, then LLM_API_KEY. No key at all stops the run before any network call. |
--snapshot <path> | A JSON file with optional memories, history and checkpoint, seeded into the run's context before it starts. memories replaces the context's list, it is not appended. Other keys are ignored. |
--report <mode> | auto (default), true or false. true stops the run when there are no credentials for an instance (unless --report-fallback console). --report-fallback console lets the run continue console-only if registering it fails, instead of the default abort. |
--sink-ttl <seconds> | A positive integer. How long the signing credentials of the event sink stay valid. The instance takes the smaller of this value and REMOTE_RUN_SINK_MAX_TTL_SECONDS, and uses REMOTE_RUN_SINK_DEFAULT_TTL_SECONDS when you pass nothing (see Environment Variables). Events sent after the sink expires are refused, so raise it for a run that can last longer than the default. |
--run-id <id> | A platform run sends it as the Idempotency-Key of the launch: repeating the command with the same value replays the original response instead of creating a second run (see Idempotency). A remote run uses it to name the in-process run (default cli_ followed by a timestamp and a random suffix). The instance still assigns its own run_ id to a reported run. |
--model-source preset reads the list of presets with GET /api/models, using the session of a CLI profile, not the API key. So preset mode needs appstrate login on that machine even when APPSTRATE_API_KEY is set. A job that has only an API key must pass --model-source env, since preset is the automatic choice for a package id, or call the LLM proxy itself.
When you run a package id with --local, the CLI also reads the space's run configuration for that agent (model, generation settings, stored input values) and applies it under your flags. Pass --no-inherit in CI so that a change made in the dashboard cannot alter a pipeline.
CI examples
A GitHub Actions job that triggers an agent on the instance and fails when the run does:
jobs:
triage:
runs-on: ubuntu-latest
steps:
- name: Install the CLI
run: |
curl -fsSL https://get.appstrate.dev | APPSTRATE_NO_LAUNCH=1 bash
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
- name: Run the agent on the instance
env:
APPSTRATE_API_KEY: ${{ secrets.APPSTRATE_API_KEY }}
APPSTRATE_INSTANCE: https://appstrate.example.com
run: |
appstrate run @acme/triage \
--input-file ./input.json \
--run-id "ci-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" \
--json --output result.jsonThe key needs agents:run and runs:read. It names its own organization and space, so no APPSTRATE_SPACE_ID is set. With --json the CLI prints events as JSON lines, starting with an appstrate.remote.triggered line that carries the run id, and the step ends with exit code 1 when the run did not succeed. If the CLI receives SIGINT, SIGTERM or SIGHUP while the run is in flight, it detaches and the run carries on on the instance.
The same job as a remote run on the runner itself, with your own model key:
- name: Run the agent on this runner
env:
APPSTRATE_API_KEY: ${{ secrets.APPSTRATE_API_KEY }}
APPSTRATE_INSTANCE: https://appstrate.example.com
APPSTRATE_SPACE_ID: spc_xxx
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
appstrate run @acme/triage --local \
--model-source env --no-inherit \
--input-file ./input.json \
--output result.jsonThis one needs APPSTRATE_SPACE_ID, set to the space the key is pinned to, because a run on your host names the space when it registers itself and calls the credential proxy. The key needs agents:read and agents:run, plus skills:read if the agent depends on a skill and credential-proxy:call if it uses integrations. The run is reported to the instance and appears there as a remote run. Do not rely on the exit code to detect a failed agent: read status in result.json.
Profiles
A profile ties a name to an instance URL, your identity and the pinned organization and space. The active profile is the first of: the --profile flag, the APPSTRATE_PROFILE environment variable, defaultProfile in the config file, then default.
appstrate login --profile prod --instance https://appstrate.acme.com
appstrate --profile prod whoami
APPSTRATE_PROFILE=prod appstrate api GET /api/agentsProfiles live in ~/.config/appstrate/config.toml (or under $XDG_CONFIG_HOME). Tokens are stored in the OS keyring (macOS Keychain, libsecret, Windows Credential Manager) under the profile's name. If no keyring backend exists at all, for example in a headless Linux container, the CLI falls back to a 0600 file ~/.config/appstrate/credentials.json. If a keyring exists but refuses access (locked), the CLI stops unless you set APPSTRATE_ALLOW_PLAINTEXT_TOKENS=1. On Windows there is no file fallback.
Troubleshooting
| Message | Fix |
|---|---|
your session may have been revoked | Run appstrate login again |
Space context required | No space is pinned. Run appstrate space switch |
Space '<id>' not found in this organization | The pinned space belongs to another organization. Run appstrate space switch |
Docker is required for this tier | Tiers 1 to 3 need Docker. Install it, or use Tier 0 |
No space id for a local run | An API key run on your host needs APPSTRATE_SPACE_ID, the space the key is pinned to. A platform run does not |
More entries are in the CLI README.
Reference
- CLI README: every flag, exit code and edge case.
- REST API.