owlette cli — overview
owlette is the command-line client for the owlette.app api. It runs on macOS, linux, and windows and lets you authenticate, push roosts, manage sites and machines, mint api keys, and inspect audit logs from your terminal or ci pipeline.
This page is the 15-minute onboarding: install → log in → push your first roost. For per-command reference, start with auth, roost, machine, listen, or trigger. For route/stub/deferred status across the whole CLI, see the readiness matrix.
installation
npm release target
npm install -g @owlette/cli@rc
owlette --versionThe cli is prepared for the @owlette/cli@1.0.0-rc.0 release channel, but it is not externally complete until the SDK and CLI distribution gate is verified. Install from source until the npm rc tag is published.
local dev (from the monorepo)
git clone https://github.com/owlette-app/owlette.git
cd owlette/cli
npm install
npm run build
npm link # makes `owlette` available on your PATH
owlette --versionThe bin/owlette launcher prefers the compiled dist/ output but falls back to running the typescript source via ts-node so node bin/owlette … works without npm run build.
first 15 minutes
1. log in (device-code flow)
owlette auth loginOpens your browser to owlette.app/cli with a 3-word pairing phrase pre-filled. Approve the request in the dashboard and the cli stores the issued api key for the default profile using the OS keychain when available, or ~/.config/owlette/credentials.json as a 0600 token-file fallback.
If you can't open a browser on the cli host (ssh, headless ci, etc):
owlette auth login --no-browser
# copy the pairing phrase + url from stderr, open them on a different machine2. confirm who you are
owlette whoami
# user id u_abc...
# email you@example.com
# scopes site:*=read|write|deploy
# environment live
# apiUrl https://owlette.app
# profile default
# configPath /home/you/.config/owlette/config.tomlowlette auth status is an alias of whoami and prints byte-identical output.
3. push your first roost
A "roost" is a content-addressed bundle of files (a touchdesigner project, a media payload, an npm build artifact — anything). Pushing it dedupes chunks against the server and publishes a new immutable version.
owlette roost push ./my-project --to rst_my_project_id --site site-1The cli walks the directory, computes content-addressed chunks, asks the server which chunks it doesn't have yet, uploads the missing ones via signed urls, then publishes a new version on the named roost. On subsequent pushes only changed chunks upload.
4. trigger a deploy
owlette roost deploy rst_my_project_id --site site-1 --dry-run
# inspect the canary / fleet split + extract path
owlette roost deploy rst_my_project_id --site site-1
# real deploy — auto idempotency-keyed so a network blip can be retried safelyThat's the loop. owlette roost diff <roostId> --against <versionRef> shows what changed between any two versions; top-level owlette rollback <roostId> reverts to the previous version.
config precedence
Config resolution is first-wins, but the credential store and the config profile swap positions depending on the field:
token (auth):
1. cli flag / env var (OWLETTE_TOKEN)
2. credential store (OS keychain, then ~/.config/owlette/credentials.json)
3. profile in config (legacy token in ~/.config/owlette/config.toml)
4. built-in default (none)
api_url / environment:
1. cli flag / env var (--api-url, OWLETTE_API_URL, OWLETTE_PROFILE, OWLETTE_ENVIRONMENT)
2. profile in config (~/.config/owlette/config.toml — selected by --profile or OWLETTE_PROFILE)
3. credential store (OS keychain, then ~/.config/owlette/credentials.json)
4. built-in default (api_url=https://owlette.app)config file schema
# top-level defaults — used when the active profile doesn't override
api_url = "https://owlette.app"
environment = "live"
[profiles.default]
api_url = "https://owlette.app"
[profiles.dev]
api_url = "https://dev.owlette.app"
environment = "test"Legacy token = "owk_*" fields are still read for migration, but owlette auth login now writes secrets to the credential store and uses config.toml for non-secret profile metadata. Switch profiles with --profile dev or OWLETTE_PROFILE=dev.
global flags
Flags inherited by every command:
| flag | env var | default | purpose |
|---|---|---|---|
--profile <name> | OWLETTE_PROFILE | default | named profile from config.toml |
--json | — | false | emit structured json instead of ascii tables on stdout |
--api-url <url> | OWLETTE_API_URL | https://owlette.app | target api host |
-h, --help | — | — | show help for the current command |
-V, --version | — | — | print cli version |
--json mode is scriptable, but the output shape is command-specific: newer commands use { ok, data } or { ok: false, error } wrappers, while legacy commands may return raw payloads or compatibility objects. Check the per-command page before scripting against a field.
noun matrix
Every command lives under one of these top-level groups. [ready] verbs hit a public api today. [stub] verbs reserve the namespace and exit 3 with a pointer to a future plan.
top-level verbs
| command | tier | description |
|---|---|---|
owlette auth login | ready | device-code login; stores token in active profile |
owlette auth status | ready | alias of owlette whoami |
owlette auth logout | ready | clear token from active profile |
owlette whoami | ready | print server-resolved identity + scopes |
owlette version | ready | print cli version, server version, and the supported API date catalog |
operator nouns (site-scoped)
| noun | tier | verbs | what it does |
|---|---|---|---|
roost | ready | push list get diff versions deploy | content-addressed project distribution |
machine | ready | list get deployments reboot shutdown screenshot | manage windows machines |
machine live-view | stub | — | streaming desktop feed; deferred as live-view-webrtc outside the MVP |
audit-log | ready | list get | site audit log + hash-chain verification |
quota | ready | show history | site storage + bandwidth usage |
chat | ready | new list send delete rename | cortex ai chat |
webhook | planned | create list get update delete rotate-secret deliveries delivery get retry probe | public routes exist; CLI noun group remains deferred |
deploy | ready | create list get retry cancel uninstall delete | classic agent-installer deploys (NOT roost deploy) |
process | ready | list get create update delete kill start stop schedule | process lifecycle on machines |
user nouns
| noun | tier | verbs | what it does |
|---|---|---|---|
site | ready | list get | sites you have access to |
key | ready | create list rotate revoke | your api keys |
superadmin nouns
| noun | tier | verbs | what it does |
|---|---|---|---|
user | ready | list get promote demote assign-sites remove-sites delete | platform user management |
installer | ready | list latest upload set-latest delete | agent installer binary management |
legacy top-level verbs (kept for muscle memory)
| command | description |
|---|---|
owlette rollback <roostId> | top-level rollback helper for roost versions |
owlette listen | open the scoped SSE liveness stream and forward received events to a local url |
owlette trigger <event> | fire a synthetic webhook for local testing |
disambiguation:
owlette deploy …is the classic installer deploy group (silent exe pushes).owlette roost deploy <roostId>is the content-addressed deploy that ships per-version diffs to a fleet. Same word, different surfaces — the help text disambiguates.
exit codes
scripts can branch on these — they're stable and meaningful:
| code | meaning | example trigger |
|---|---|---|
0 | success | owlette roost push ./dist --to rst_abc |
1 | generic error — network failure, api 5xx, unexpected state | server unreachable; invalid response shape; transient 429 |
2 | usage error — missing required flag, bad arg, unknown command | owlette roost push with no path |
3 | stub — noun exists but has no public api yet | owlette machine live-view m-1 --site site-1 |
Exit code 3 is intentionally distinct from 1 so ci can tell "the api will never accept this until the backend ships" apart from "transient failure, retry."
json output shapes
Commands that accept --json write one JSON document to stdout. Newer commands generally use the wrapper shapes below; legacy commands can return raw server payloads or command-specific objects, so the per-command reference is authoritative.
wrapped success
{ "ok": true, "data": { /* command-specific payload */ } }Examples in source include roost list, key list, site list, machine list, whoami, and trigger, which print raw payloads or command-specific objects rather than a universal { ok, data } envelope.
wrapped failure
{ "ok": false, "error": { "code": "<stable_string>", "message": "human-readable", "detail": { /* optional */ } } }When a command surfaces API problem+json details, stable code values match the API's codes (scope_insufficient, token_expired, idempotency_key_mismatch, manifest_stale, rate_limited, unsupported_version).
stub (exit 3 only)
{ "ok": false, "stub": true, "noun": "machine", "reason": "live-view streaming is being reframed as a webrtc-native feature; resume when prioritized", "dashboard_url": "https://owlette.app/dashboard", "future_plan": "public-api deferred: live-view-webrtc" }next steps
- Command reference pages: auth, roost, listen, and trigger
- api reference — the underlying http surface every cli command wraps
auto-rollback on deployment.failed webhook
a small node/express webhook receiver that listens for deployment.failed events from roost, verifies the Roost-Signature hmac, calls POST /api/roosts/{roostId}/rollback, and notifies a slack channel. automatic production delivery of deployment.failed is still future/launch-blocked; use this receiver with POST /api/webhooks/probe to test signature handling and keep real rollback execution operator-gated until event fanout is enabled. deploy it anywhere that can accept https requests - vercel, cloudflare workers, or a plain node server behind nginx. the same code shape runs in all three with minor entrypoint tweaks.
cli readiness matrix
Status as of 2026-05-02. Source of truth for registered commands is cli/src/index.ts plus cli/src/commands/**.