Skip to main content
The onyx command is the full terminal-only user and orchestrator CLI. The companion onyx-worker command exposes the worker-safe primitive surface used by supervised agents. In normal usage, you log in once and start research from an agent with /onyx ...; the agent composes these commands into the loop. Use this reference when debugging, scripting, or understanding what the agent is doing.

Install and Version

Developer Mode

Developer mode lets contributors keep the persistent onyx command installed while switching it between the release CLI and a linked source checkout.

Login

Uses Authorization Code with PKCE on desktops and device authorization in SSH or headless shells. One-team accounts skip team selection. Sessions refresh silently and credentials are stored in the system keyring when available, falling back to a private local file with a warning. Logout revokes the remote session before removing the local profile. Every login starts a one-time Onyx login attempt. In the browser flow its nonce travels through the WorkOS authorization request and comes back in the ID token, which the CLI presents when it binds the session. In the device flow Onyx starts device authorization and completes the code exchange on the CLI’s behalf while you approve in a browser, because WorkOS does not carry a nonce through device authorization; Onyx records who approved and hands the tokens to the CLI once without storing them. Either way each device is independently revocable. The profile pins the OAuth issuer, client ID, and token endpoint at login, so token refresh always goes straight to WorkOS and never re-reads the API server’s configuration. A custom --api-url (not production, not localhost) prints the API URL, issuer, and client ID, explains that the server receives your access and ID tokens and, for device login, also your refresh token, and asks for confirmation; use --trust-api-url in non-interactive shells. If a request fails with cli_session_revoked or cli_session_invalid, the CLI removes the stored login and asks you to run onyx login. A 503 from the API while the identity provider is unreachable is retried and never removes a login. Older config files are migrated by onyx login, which leaves a sanitized backup (no keys or tokens) beside the config.

Agent Skill

skill-path prints the bundled skill location. install-skill copies it into the managed Claude Code, Codex, and OpenCode skill directories by default: ~/.claude/skills/onyx/SKILL.md, ~/.agents/skills/onyx/SKILL.md, ${CODEX_HOME:-~/.codex}/skills/onyx/SKILL.md, and ~/.config/opencode/skills/onyx/SKILL.md. Pass --dir only when you need to install one custom skill root instead.

Profiles

Profiles are team-scoped and contain only non-secret metadata plus a random credential identifier. onyx profile list shows each profile’s credential store and worker defaults. Profiles can also store default worker agent/model choices used by onyx research run and onyx worker run.

Analytics preference

Official CLI releases collect structured command outcomes from the first eligible command. ONYX_TELEMETRY_DISABLED=1 and DO_NOT_TRACK=1 always disable collection. Analytics is disabled in workers, CI, testbed environments, source/developer builds, and non-production API profiles. See CLI analytics and privacy for the exact data contract.

Worker CLI

onyx-worker is the explicit worker-safe command surface. It includes only primitives such as research brief, research session-state-brief, research finish, exp run, exp log, exp list, workflow status, tools run, knowledge add, and knowledge list. It excludes login/profile/developer, campaign/setup, research orchestration, worker launching, public push, and removed research sync commands. Humans and orchestrators can run onyx-worker directly for local debugging. A supervised worker starts in the project root and receives an isolated ONYX_HOME, an ONYX_WORKER_CONTEXT file carrying its identity and one short-lived scoped credential, isolated temp directories, and a wrapper named onyx-worker placed first on PATH. In that runtime, onyx-worker defaults commands to the assigned campaign, session, hypothesis, and worker from the context file (no identity flags needed), rejects conflicting identity or target overrides, and re-execs the pinned wrapper if a different onyx-worker install is resolved first. The full onyx CLI refuses operational commands when worker context is present and points back to onyx-worker.

Campaigns

Creates or idempotently selects a logical campaign by reading campaign metadata from onyx/setup.json, resolving the repository remote from origin, and writing remote campaign state through /api/v1. When GitHub access is available, Onyx creates or reuses the internal project automatically. The Onyx orchestrator usually runs this for you after interpreting your /onyx prompt. The campaign’s metric name, unit, and direction are immutable. A same-name campaign with a different metric contract is rejected; create a new campaign instead. The command may record the current git HEAD as creation provenance, but a campaign has no mutable base commit or shared research-history branch. The onyx/ setup surface must be committed before this command runs.
use selects an existing remote campaign for direct one-agent mode and updates the small local state cache. status prints the active campaign and cached profile/project pointers.

Setup and Tools

onyx setup init creates onyx/setup.json, onyx/validation.json, onyx/onyx.md, and onyx/tools/evaluation/run.sh. The scaffold is minimal and does not infer editable scope, source files, or evaluation behavior from the repository. --editable-scope and --eval-command write exactly the values you provide. Without --eval-command, the scaffolded evaluation tool exits nonzero until the orchestrator replaces it with a real eval that emits exactly one primary metric line such as METRIC score=0.42. The required metric tool declares non-empty fingerprintPaths; the scaffold defaults to ["onyx/tools/evaluation"]. Onyx expands committed tracked files under those paths and hashes their git contents together with the normalized metric and tool configuration. Missing, untracked, escaping, or symlinked inputs fail validation. A changed evaluation fingerprint creates a separate evaluation revision, so incomparable results never share one leaderboard. onyx setup validate runs static local checks and writes onyx/validation.json. It checks schema shape, setup hash, project path, workflow/tool/resource references, a leading agent step, one required metric step, referenced tool files, protected setup paths, and nontrivial narrative context. Warnings for missing safety/readiness/reliability-style steps do not block Research. onyx-worker tools run executes a declared tool from onyx/setup.json with declared cwd/env, timeouts, output capture, and local resource slot leases. It is for manual diagnostics only: it never creates workflow state and never creates a measured attempt. Operators can also reach this primitive through the full onyx CLI.

Experiments

onyx-worker exp run owns one workflow run: one experiment attempt, one result commit, and one primary metric. In worker context, a new run can infer the clean current HEAD as the base, pause at the leading agent step, and later resume with onyx-worker exp run --resume --auto without copying workflow IDs. Command steps execute declared setup tools, capture step logs under .git/onyx/workflow-runs/<workflowRunId>/, and create transient pending-report outbox entries for succeeded, failed, checks_failed, or setup_violation outcomes. These entries provide delivery and teardown safety, not research history, and successful reporting clears them. onyx-worker workflow status is read-only. It shows the run status, next step, block reason, step summaries, metric snapshot, run ref, result commit, and log paths. With --campaign, it defaults to the latest workflow run for that campaign, including terminal runs after completion. Add --active to restrict the lookup to running or paused runs, or --blocked for blocked runs. In worker context, status lookups are scoped to the current worker. --run remains an exact lookup.
Pushes the immutable git result ref first, then reports the measured experiment through the scoped /api/v1/research/worker/experiments data plane for a supervised worker. The API returns recorded or duplicate; recorded means delivery succeeded and normally returns with received disposition. Workers do not wait for acceptance. Server settlement later assigns accepted/discarded disposition and any accepted index. Failed unmeasured attempts require --status failed --allow-unmeasured. Protected path or out-of-scope diffs are reported as setup_violation.
Reads paginated experiment state from the Onyx API. Online API access is required.

Research Sessions

onyx research run is the normal local supervisor. Every invocation creates a new bounded remote session; sessions are never attached to or resumed. Before session creation, Onyx resolves the provider and worker CLI to absolute paths, checks the worker protocol/build handshake, and performs exactly one minimal model inference probe (custom worker commands skip only the model probe). Managed workers run bare onyx-worker commands — the supervisor pins the verified wrapper first on PATH, and the ONYX_WORKER_CONTEXT runtime context is the authoritative scope: identity flags default from it, conflicting values are rejected, and worker commands refuse to operate outside the assigned worktree. Workers use onyx-worker research session-state-brief --json for bounded routine context, and onyx-worker diagnostics handshake prints version, protocol, and capability metadata. It includes complete assigned-hypothesis guidance, concise peer and accepted-result summaries, complete selected knowledge items, progress, and worker-specific stop guidance. Use explicit list commands when deeper history is needed. The default is committed HEAD, all active campaign hypotheses, and one worker. Use repeated --hypothesis flags to narrow membership, --base to choose the session default, and --hypothesis-base for assignment-specific starting commits. All refs resolve locally to immutable SHAs and must contain the same setup hash and evaluation fingerprint. For GitHub-backed projects, push those exact commits first; preflight identifies an invisible commit and creates no session. A run requires --experiments, --max-minutes, or both. The CLI refuses a second local supervisor for the campaign unless --new is passed. A dead cached supervisor is never resumed: its old session is ended as supervisor_failed, identity-verified orphan processes are cleaned up, and a fresh session is created. The supervisor keeps up to the mutable worker target active, requests server-owned leases, refreshes the local session brief and atomic supervisor control snapshot, sends presence and heartbeat batches, handles assignment cancellation, and records bounded terminal diagnostics when push or reporting fails; the worker workspace is still discarded and is not recoverable. A bounded no-progress breaker ends systemic replacement loops and is visible in research status and listen. The server owns worker slots, immutable session-hypothesis assignments, report settlement and accepted experiment order. Evaluation-revision best/count/latest summaries are computed from indexed experiment facts. Workers remain cheap: the supervisor keeps launching while the server grants leases. onyx-worker exp log pushes the immutable result ref then records or deduplicates the experiment through the API. After the CLI’s bounded reporting retry fails, a worker stops new work and exits; teardown owns the one remaining pending-attempt delivery. Overflow attempts are retained as discarded diagnostics and can be requested with --disposition discarded, but do not count toward targets or rankings. onyx worker run remains a lower-level/debug primitive. Worker startup records the worker id, scoped-credential lease state, hypothesis id, worktree, manifest path, worker CLI path, worker context path, raw provider log path, and readable activity log path. research status --summary --json returns bounded orchestrator progress and supervisor health. Detailed research status --json returns structured launch suggestions plus terminal reason, delivery, and cleanup outcomes for debugging disposable worker failures. onyx listen renders the same outcomes and the no-progress breaker. onyx-worker research brief fetches a compact campaign/session brief from the API; it prints Markdown by default and structured data with --json. --agent selects the coding harness. --model selects the model for that harness; OpenCode model values must use provider/model. For new sessions, worker settings resolve from command-line flags, then ONYX_WORKER_AGENT or ONYX_WORKER_MODEL, then the active profile’s worker defaults, then Codex. Existing sessions keep their recorded worker agent/model and reject conflicting flags. onyx research scale records an auditable capacity change. Scaling up opens lease capacity immediately; scaling down drains naturally and never kills running workers. Use stop instead of scaling to zero. When more than one local session is open, stop and scale require an explicit --session. onyx research stop writes a local stop marker before signaling only identity-verified process groups, then records the immutable server cutoff. If the API call fails, the CLI retains stop_pending_remote state and prints the retry command. Closing a hypothesis cancels its assignments and workers in every open session; late reports are preserved as discarded with hypothesis_closed. Reopening it makes it eligible only for future sessions. Hypotheses added while research is running are likewise available beginning with the next session.

Recovering measured reports

The supervisor retries report and cleanup-receipt delivery at startup, every 30 seconds, and during finalization. Periodic delivery does not reconcile runtimes or terminate orphan processes; runtime reconciliation is a startup check or an explicit research recover --runtime operation. Without a supervisor, run onyx research recover --dry-run --json to inspect pending counts, then onyx research recover --json to deliver eligible records. Each pass is bounded to 25 records and a five-second request budget. Credentials must match the saved team and API origin. Conflicts and unsupported old records remain available for inspection; recovery never reruns an experiment or changes its immutable report body. Canonical local experiment refs keep measured commits reachable after a failed push. research clean refuses active or pending execution state and preserves reports, immutable refs, and cleanup receipts. Stop by session ID works without a live supervisor or local session pointer: onyx research stop --session <id> resolves the session remotely. Resource locks never expire under a holder. Before resetting a resource, stop all its launchers and child processes, then inspect onyx research locks reset --resource <name> --confirm-idle --dry-run. Omit --dry-run only after confirming the resource is idle. Missing PIDs alone do not establish this. Internal push and report resources are onyx-result-ref-push and onyx-report-delivery. Use onyx research recover --runtime [--session <id>] [--dry-run] [--json] to reconcile interrupted local execution before bounded delivery. Dry-run does not create runtime files, refresh credentials or change remote state. JSON keeps the delivery summary and adds runtime.recovered, runtime.skippedActive, runtime.blocked, and bounded session actions/reasons. Blocked runtime recovery returns a nonzero status; pending report delivery is reported separately. Recovery verifies site/run ownership and boot/process identity, skips healthy supervisors, and retains artifacts when descendant termination is uncertain. It preserves terminal refs before removing disposable worktrees and credential homes. Explicit recovery never changes the remote session cutoff; stop an open session explicitly. Occupied resource slots require the existing idle reset, including onyx-runtime-reconciliation after an interrupted launch/recovery. Malformed evidence is retained. Repeated recovery never measures scratch work or resumes a session. Clean refuses unresolved runtime and removes worktrees through Git before deleting logs.