Command reference
Every oxagen command and flag: spend, prices and billing, steering and repositories, agents and work orders, runs and their evidence, the knowledge graph, the code graph, agent memory, environments, secrets, API keys, and the market router.
Run oxagen --help for the live list, and append --help to any command for its flags. oxagen --version prints the CLI's version.
Most platform commands need an authenticated CLI. cost, logs, telemetry, and verify work offline. oxagen codegraph upload signs in with the CI provider's token and needs no login. oxagen agent enroll with a one-time token needs no login. The commands under oxagen agent that take no agent argument act on this machine's local state. oxagen work acts for the agents enrolled on this machine and needs no login, except oxagen work staffing, which reads the workspace's runs.
oxagen budget: spend ceilings
oxagen budget show [--json]
oxagen budget set --scope <org|workspace> --period <monthly|rolling> --limit <usd> [--window-days <n>] [--enabled <true|false>] [--json]Hard period-to-date spend ceilings that gate agent runs. show lists every configured ceiling with its live burn. set creates or replaces one. See Spend budgets for the full model.
Flag (set) | Description |
|---|---|
--scope <scope> | Required. org or workspace. |
--period <period> | Required. monthly, or rolling for a trailing window. |
--limit <usd> | Required. The ceiling in US dollars. It must be more than 0. |
--window-days <n> | The length of the trailing window in days. Required with --period rolling. Leave it out for monthly. |
--enabled <bool> | true or false: whether Oxagen enforces the ceiling. Defaults to true. |
--json | Print the result as JSON. |
An org ceiling needs an organization Owner or a member with the Compliance and Billing role. A workspace ceiling also accepts the workspace's Admin.
oxagen budget set --scope workspace --period rolling --window-days 7 --limit 250oxagen cost: project model cost
oxagen cost --in <tokens> --out <tokens> [-m <slug>] [--json]
oxagen cost --rates [--json]Prices a token workload against the rate card built into the CLI. With no -m, --model, it compares every priced model, cheapest first, and reports the spread. With one, it projects that model alone. --rates prints the card.
This is a projection, not an observation. It makes no network call and needs no credential. For what was spent, use oxagen budget show (period-to-date burn) or oxagen run turns <run-id> (spend per turn of one run). For the rates Oxagen bills at, use oxagen price list.
oxagen cost --in 200000 --out 40000
oxagen cost --in 200000 --out 40000 -m anthropic/claude-sonnet-5 --json
oxagen cost --ratesoxagen price: negotiated rates
oxagen price list [--at <instant>] [--include-scheduled] [--json]
oxagen price set --provider <provider> --model <model> --token-class <class> --usd-per-million <usd> [--additional-rate <class=usd>]... [--alias <model>]... [--effective-from <instant>] [--json]
oxagen price remove --provider <provider> --model <model> --token-class <class> [--region <region>] [--at <instant>] [--scheduled-entry-id <id>] [--confirm-unpriced] [--json]The price book holds the rates Oxagen bills runs at. A negotiated rate is your organization's own price for one model and one token class, and it wins over the provider's list price. oxagen cost never reads this book.
| Subcommand | Description |
|---|---|
list | Lists the prices in force now, or at --at. --include-scheduled adds your organization's future negotiated rates. |
set | Sets a negotiated rate. The new rate closes the one it replaces, and runs priced earlier keep the old rate. |
remove | Ends a negotiated rate, so the model goes back to its list price. Nothing is deleted: the rate stops applying at --at, or now. |
| Flag | Description |
|---|---|
--provider <provider> | Required for set and remove. The provider, such as anthropic. |
--model <model> | Required for set and remove. The model id, such as claude-sonnet-5. |
--token-class <class> | Required for set and remove. One of input_uncached, cache_read, cache_write_5m, cache_write_1h, output, reasoning, server_tool_request, embedding_input, rerank, image, or video_second. |
--usd-per-million <usd> | Required for set. The price in US dollars per 1,000,000 units, with at most six decimals. Write 2.40, not 2400000. |
--additional-rate <class=usd> | set only. Prices another token class in the same write, such as output=12. Repeat it for more classes. Name each class once. |
--alias <model> | set only. Another model id this rate also prices. Repeat it for more ids. |
--effective-from <instant> | set only. The RFC 3339 time the rate starts. Defaults to now. |
--at <instant> | list: the RFC 3339 time to read prices at. remove: the time the rate stops. Both default to now. |
--include-scheduled | list only. Also list future negotiated rates. |
--region <region> | remove only. The region of the rate to end. Leave it out for a rate that applies everywhere. |
--scheduled-entry-id <id> | remove only. Cancels this one scheduled rate and keeps the rates before and after it. |
--confirm-unpriced | remove only. Ends the rate even when no list price or other rate covers the class. Without it, Oxagen refuses, because the class would have no price. |
--json | Print the result as JSON. |
set and remove need an organization Owner or a member with the Compliance and Billing role. The command exits 2 on a bad flag and 1 on an API failure.
oxagen price set --provider anthropic --model claude-sonnet-5 --token-class input_uncached --usd-per-million 2.40 --additional-rate output=12oxagen tier: model tiers
oxagen tier list [--all] [--json]
oxagen tier add --name <name> --guidance <text> [--slug <slug>] [--rank <n>] [--json]
oxagen tier update <tier> [--name <name>] [--guidance <text>] [--rank <n>] [--default] [--shared-key on|off] [--json]
oxagen tier archive <tier> [--json]
oxagen tier model <tier> <harness> <model|none> [--json]A model tier is a level of model your organization names, such as Fast, Balanced, Pro and Ultra. Each tier has a rank, where 1 is the lightest, guidance that says when to choose it, and the model each harness runs for it. Triage records a tier on each work item, and dispatch starts the agent on that tier's model. A tier is named by its slug or its id.
| Subcommand | Description |
|---|---|
list | Lists the tiers in rank order with each harness's model. --all adds archived tiers. |
add | Adds a tier at --rank, or after the heaviest. The tiers at that rank and above move up one. |
update | Renames a tier, rewrites its guidance, or moves it to --rank. --default makes it the tier stella runs on when a turn names none. --shared-key off makes your organization's own model key pay for its stella turns, and on gives them back to Oxagen's shared key. The slug does not change. |
archive | Archives a tier. Work items and runs that name it keep it, and the tiers above it move down one. |
model | Sets the model a harness runs for a tier. The harness is claude-code, codex, cursor or stella. none clears it. |
A model that ends in *, such as claude-opus-*, names the newest release of that family, and a run resolves it when it starts. Every member can list the tiers. Only the organization Owner can change them. The command exits 2 on a bad flag and 1 on an API failure.
oxagen tier model pro codex 'gpt-5.*'oxagen billing: billing statements
oxagen billing statement --period <week|month|quarter|year|custom> [--anchor <date>] [--from <instant> --to <instant>] [--format summary|csv|html] [--out <file>] [--top <n>] [--page-size <n>] [--json]Reads or exports your organization's billing statement for one period. See Billing statements for what a statement holds.
| Flag | Description |
|---|---|
--period <period> | Required. week, month, quarter, year, or custom. |
--anchor <date> | A UTC date inside the week, month, quarter, or year, written YYYY-MM-DD. Defaults to today. |
--from <instant> | With custom: the first instant of the period, in RFC 3339. |
--to <instant> | With custom: the first instant after the period, in RFC 3339. It must be more than 48 hours after --from. |
--format <format> | summary, the default, csv, or html. csv lists every billed governed action. |
--out <file> | Writes csv or html to this file instead of standard output. |
--top <n> | With summary: the rows in each breakdown, 1 to 100. |
--page-size <n> | With csv: the rows fetched per request, 1 to 50000. |
--json | Print the result as JSON. |
The command needs an organization Owner or a member with the Compliance and Billing role. It exits 2 on a bad flag and 1 on an API failure.
oxagen billing statement --period month --anchor 2026-09-01 --format csv --out september.csvoxagen findings: costed findings
oxagen findings list [--run <run-id>] [--status open|applied|dismissed] [--level tool|agent|operator|workspace] [--subject <key>] [--kind <kind>] [--cursor <cursor>] [--json]A finding is a costly pattern in the workspace's runs, such as a retry loop, with what fixing it would save. list orders the findings by the money at stake, 50 to a page. With --run, it lists only the findings that cite that run, with the frames each one cites.
| Flag | Description |
|---|---|
--run <run-id> | Only findings that cite this run. |
--status <status> | open, the default, applied, or dismissed. |
--level <level> | tool, agent, operator, or workspace. |
--subject <key> | An agent key, an operator id (prn_…), a tool name, or the workspace id. |
--kind <kind> | One finding kind, such as retry_loops. |
--cursor <cursor> | The cursor the previous page printed. |
--json | Print the result as JSON. |
The command exits 2 on a bad flag.
oxagen findings list --level agent --kind retry_loopsoxagen graph: knowledge graph
oxagen graph search -q <text> [-l <labels>] [-n <n>] [--json] [--quiet]search runs a semantic (vector) search across the workspace's knowledge graph through search_graph. -q, --query is required. -l, --labels narrows the search to a comma-separated list of entity labels, such as Issue,PullRequest. -n, --limit takes 1 to 50 and defaults to 10. --json prints one JSON line, which is also the default when standard output is a pipe. --quiet hides the progress lines on standard error. See Knowledge graph from the CLI.
oxagen codegraph: code graph
oxagen codegraph init [path] [--force] [--json]
oxagen codegraph status [--json]
oxagen codegraph rebuild [path] [--json]
oxagen codegraph stop [--purge]
oxagen codegraph upload [--coverage <source=pattern>]... [--test-report <source=pattern>]... [--runtime <source=pattern>]... [--schema-dump <source=pattern>]... [--infra-plan <source=pattern>]... [--api-spec <source=pattern>]... [--environment <name>] [--api-url <url>] [--fail-on-unconnected]| Subcommand | Description |
|---|---|
init [path] | Gives each agent on this machine Oxagen's MCP entry, then builds the code graph of the working tree at path, or the current folder. Needs an enrolled machine. --force builds even when the tree matches the last build. |
status | Shows every code graph built on this machine. |
rebuild [path] | Builds the code graph again, even when the tree has not changed. |
stop | Stops the local code graph server. The next code graph call starts it again. --purge also deletes ~/.oxagen/codegraph, which holds every local graph and the grammar pack. |
upload | Runs in CI. Uploads coverage, test reports, runtime profiles, schema dumps, infrastructure plans, and API specs for the commit the pipeline ran on. It signs in with the CI provider's OpenID Connect token and needs no login. Each report flag takes source=pattern and can repeat. --environment names the deployed environment a runtime profile or schema dump came from. --api-url names the Oxagen API to upload to. --fail-on-unconnected fails when no workspace holds the repository. |
--json prints the result as JSON. See Code graph from the CLI.
oxagen memory: workspace memories
oxagen memory list [--state <states>] [--harness <harness>] [--agent <record-id>] [--repository <repo>] [--type <type>] [--limit <n>] [--offset <n>] [--json]
oxagen memory show <id> [--json]
oxagen memory promote <ids...> [--one-record] [--statement <text>] [--kind <kind>] [--force <force>] [--effect require|forbid] [--repo <repo>]... [--workspace] [--no-suggest] [--no-same-text] [--json]
oxagen memory dismiss <ids...> [--reason covered|wrong|one_off|other] [--restore] [--json]
oxagen memory drop <number> <path> [--json]
oxagen memory import <files...> [-y, --yes] [--json]The memories agents wrote in their harnesses, which Oxagen collects from enrolled hosts. See Memory from the CLI.
| Subcommand | Description |
|---|---|
list | Memories ranked by uses, one row per group of memories that say the same thing. --state takes a comma list of waiting, in_pr, promoted, dismissed, and retired, and defaults to waiting,in_pr. --harness takes claude-code, codex, cursor, stella, or claude-desktop. --agent takes the ID of the agent that wrote them. --repository takes a repository, such as github.com/acme/api. --type takes a Claude Code memory type: user, feedback, project, or reference. --limit sets the groups per page and defaults to 50. --offset skips groups. |
show <id> | One memory with its full text, its source, the runs that used it, and its memory PR. |
promote <ids...> | Promotes waiting memories into draft steering records on the open memory PR, or opens one. Each id becomes one record, unless --one-record cites every id in one record. --statement sets the record's text and defaults to the first memory's statement. --kind takes business-rule, code-rule, constraint, procedure, fact, preference, or memory. --force takes must, should, may, or info. Oxagen suggests the kind and the force unless you name them or pass --no-suggest. --effect takes require or forbid, and a constraint needs one. --repo scopes the record to a repository; repeat it or use commas for more. --workspace scopes it to the whole workspace. --no-same-text cites only the ids you give, not the other waiting memories that say the same thing. |
dismiss <ids...> | Sets memories aside so the curator does not propose them again. --reason takes covered, wrong, one_off, or other. --restore brings dismissed memories back to waiting. |
drop <number> <path> | Takes one proposed record out of the open memory PR with that number. When the PR merges, the record's statements are rejected and its memories wait again. |
import <files...> | Reads Markdown files into steering records, with a kind and a force for each statement. It previews unless you pass -y, --yes, which opens one steering PR. Records that conflict with a published record are left out. |
--json prints the result as JSON.
oxagen memory list --harness claude-code --limit 20oxagen init: link a project
oxagen init [--org <slug>] [--workspace <slug>] [--no-link] [--json]Resolves an org and workspace and writes .oxagen/workspace.json, which every other command reads to scope itself. --org and --workspace name the pair, so init runs without prompting, in CI or any shell without a terminal. Without them, init picks the only option or asks you to choose. It then checks the workspace for a GitHub connection and, in an interactive terminal, offers to create one so the graph has a source to ground on. --no-link skips that step and cannot be combined with --org or --workspace.
Inside a git repository, init writes the link at the repository root, whatever subdirectory you run it from. It also adds .oxagen/workspace.json to the root .gitignore when git does not already ignore it, because the link binds one machine, not every clone. Outside a repository, the link goes in the current directory and .gitignore is left alone.
init is idempotent. Running it again in a linked project reuses the existing link. When --org or --workspace names a different pair, init replaces the link and prints the pair it replaced.
Last, init reports the directory to Oxagen, and it appears on the Working copies tab of the Repositories page with its machine, path, repository, branch, and head commit. The machine is named by a hash of a random id kept in ~/.config/oxagen/machine-id, never a hardware serial. A hash is a short code made from the content, and any change gives a different code. If the report fails, init prints one warning and still succeeds. With --json, the result carries the recorded workingCopy id or the error.
With --org or --workspace, init exits 1 when it cannot link the pair you named.
The link names the org and the workspace by slug, ID, and name:
{
"orgSlug": "acme",
"orgId": "org_8fK2mQ9xLw3RtY6bN1pZ4c",
"orgName": "Acme",
"workspaceSlug": "payments",
"workspaceId": "wrk_3Hs7Vd1QkP9mXe2Lt5Ga8r",
"workspaceName": "Payments",
"linkedAt": "2026-09-24T16:00:00.000Z"
}Commands address the workspace by its slugs. The CLI reads the IDs only to find the org and workspace again after one is renamed, and then rewrites the slugs. init writes database IDs. To write the file by hand, copy the public IDs (org_… and wrk_…) from the Workspaces section of the Organization page. The CLI accepts either form.
oxagen pull: write the published steering here
oxagen pull [--binding <rpb_id>] [--force] [--dry-run] [--json]Writes the steering published in this workspace into this directory's .oxagen/. pull reads the .oxagen/ tree at the head of the steering repo's production branch through Oxagen, so the machine needs no git access to the steering repo. A steering repo in the current layout keeps its steering records under steering/ and has no .oxagen/ tree. For a workspace that uses one, pull finds no published files and deletes the unedited files an earlier pull wrote. --binding pulls from a linked repository instead of the steering repo.
Run it anywhere inside a project that oxagen init linked. pull walks up to the nearest .oxagen/workspace.json and uses the workspace it names.
pull does not overwrite your edits. It records what it wrote in the pull block of .oxagen/workspace.json and decides each file three ways:
| Here | Result |
|---|---|
| Absent | Created. |
| Same as published | Left alone. |
| Unchanged since the last pull | Replaced with the published file. |
| Edited here | A conflict. |
A file the last pull wrote that is no longer published is deleted if you did not edit it, and is a conflict if you did. One conflict refuses the whole pull, lists the paths, and writes nothing. --force overwrites or deletes the edited files. --dry-run prints the plan and writes nothing.
pull writes only inside .oxagen/. It refuses a published path outside .oxagen/, a path that climbs out of it, the machine's own .oxagen/workspace.json, and a local symlink that would carry a write out of .oxagen/. --force does not override those refusals.
After a pull, the directory is reported to Oxagen with the commit it pulled, the same way init reports it. The command exits 0 on success or when there is nothing to do, 1 on a refusal or an API failure, and 2 on a bad argument.
oxagen check: steering PR checks
oxagen check [paths...] [--base <ref>] [--refresh] [--json]Run check inside a clone of a steering repo to see what the steering PR check will report before you push. It runs the same checks on your working tree. The tree is every file git tracks or would add among AGENTS.md, CLAUDE.md, README.md, .gitattributes, workspace.toml, and the files under agents/, steering/, tools/, and policy/. The checks compare the tree with the production branch: --base <ref>, else origin/HEAD, else origin/main. With no base, they read the tree whole, as for a first publish.
Paths narrow the report. The checks still read the whole tree, and the report keeps the findings in the files and folders you name.
Some checks read more than the tree. The hash, conflicts, and references checks compare your change with the records the workspace has published. The references check also looks up each runtime, operator, reviewer group, and credential your repo names. check fetches the published index and this context from Oxagen in one call and caches the answer for ten minutes. --refresh fetches it again. The cache keeps one answer for each API address, organization, workspace, and login, so a run after oxagen login to another workspace fetches again.
- A workspace repo reads the workspace its
workspace.tomlnames. Your login must be for that workspace. If it is for another one,checkstops withscope_mismatchand exits 2. Runoxagen login --org <org> --workspace <workspace>for the workspaceworkspace.tomlnames, or fixworkspace.toml. - An organization repo reads the context of the workspace the CLI has selected. It reads no index, because no organization version is published yet, so its records check as a first publish.
Oxagen does not store member handles, teams, or reviewer group slugs yet. Until it does, the references check reports each agent's operator and each reviewer group as missing.
Two checks do less on your machine. The settings check is skipped, and the compile check does not evaluate the Cedar policies.
--json prints one finding per line on stdout and the result line on stderr.
The command exits 0 when no finding is an error and 1 when at least one is. It exits 2 when the checks cannot run: outside a steering repo or a git clone, with a --base that names no commit, when your login is for another workspace than the one check reads, or when check cannot fetch the index and context, for example before you log in.
oxagen steering: steering freshness
oxagen steering status [--json]
oxagen steering sync [--force] [--commit] [--dry-run] [--json]
oxagen steering gate [--harness claude-code|codex|text|json] [--no-network]
oxagen steering hooks <install|remove|status> [--harness <names>] [--json]These commands check whether this checkout's .oxagen/ carries the steering records merged on the production branch. A branch keeps the .oxagen/ it started from, so an agent on an old branch can steer by records nobody uses anymore. Run them inside the repository.
| Subcommand | Description |
|---|---|
status | Compares .oxagen/ with the remote production branch. It shows what the checkout is missing and the state of the two gates: auto-sync and block stale runs. |
sync | Takes .oxagen/ from the remote production branch and leaves the files staged. It refuses while .oxagen/ holds uncommitted or unmerged work. --force overwrites local changes. --commit commits the files instead of staging them. --dry-run shows what it would take and writes nothing. |
gate | The check an agent harness runs before each prompt. --harness picks the output the harness reads: claude-code, codex, text (the default), or json. --no-network never contacts the remote or the Oxagen API. |
hooks <action> | install, remove, or status for the gate in a harness's settings. --harness takes claude-code, codex, a comma-separated list, or all (the default). |
gate exits 0 to let the prompt run, even with a warning, and 2 to refuse it, with the reason on standard error. Its standard output belongs to the harness. If the check itself fails, the gate lets the prompt run and prints one line on standard error that says so. Any harness without a hook can run oxagen steering gate before each prompt and refuse the prompt on exit 2.
sync exits 1 when it refuses, and 0 when the checkout is not behind. hooks exits 1 when a harness refuses the change and 2 on a bad --harness.
oxagen steering status
oxagen steering hooks install --harness claude-codeoxagen steering import: Markdown files into steering
oxagen steering import [paths...] [--harness claude-code|codex|cursor] [--as records|policies|memories] [-y, --yes] [--json]Reads Markdown files into steering records, Cedar policies, or memories. Pass files, folders, a harness, or any mix of them. The command searches a folder for .md, .markdown, .mdx, and .mdc files. A .mdc file is a Cursor rule. The search skips node_modules and every folder whose name starts with a dot, except the harness folders: .claude, .codex, .cursor, .agents, .github, .windsurf, and .clinerules. In those it takes only the harness files Oxagen knows, such as Cursor rules, skills, commands, and subagent files. A skill comes with every file in its folder. Naming a harness folder, such as repo/.claude, reads it the same way as naming repo. The command takes a file you name as given.
--harness also reads your own harness folders on this machine:
claude-code: theskills,commands, andagentsfolders andCLAUDE.mdin$CLAUDE_CONFIG_DIR, or in~/.claudewhen that variable is unset.codex: the skills in$CODEX_HOME/skills, or in~/.agents/skillswhen that variable is unset, andAGENTS.override.mdor elseAGENTS.mdin$CODEX_HOMEor~/.codex.cursor:~/.cursor/skillsand~/.cursor/commands. Cursor keeps its user rules in its settings, not in a file, so the command reads no instruction file for it.
Skills, commands, and subagent files from those folders become records for the whole workspace, because a folder on your laptop belongs to no repository. Your instruction file, such as ~/.claude/CLAUDE.md, becomes your own memories. So does any CLAUDE.local.md the command finds. A memory steers nothing until a person promotes it. The command leaves out a skill folder Oxagen wrote for a session.
oxagen steering import --harness claude-code # preview your Claude Code folders
oxagen steering import --harness claude-code --yes # open the steering PR and store the memoriesWithout --as, each file takes the target its text implies. A file with a fenced cedar block becomes a policy file at policy/<file-name>.cedar, the import skips a README or index file, and it splits any other file into records. --as records, --as policies, or --as memories sets one target for every file. --as memories splits each file into statements the same way, and each statement becomes a memory.
oxagen steering import AGENTS.md docs # preview
oxagen steering import AGENTS.md docs --yes # open the steering PR
oxagen steering import notes --as memories --yes # store the memoriesThe preview lists each record with its kind, its force, its source line, and any duplicate or conflict with a published record, then each policy file with its statements and any issue, then each memory with its source line. It writes nothing. --yes opens one steering PR on steering/import-<date> with every record and policy marked add. It leaves out a record that conflicts with a published record and a policy file with an issue, and names each one. Nothing steers an agent until the PR merges.
--yes stores every memory marked add as a waiting memory, with import:<file>#L<line> as its source. It names no agent, because a person imported it. It leaves out a memory that repeats a waiting memory or a statement a person rejected, and names each one. A memory steers no agent until a person promotes it into a steering record. An import of memories alone opens no PR.
The command sends files in calls of 25 and then compares the rows of every call. A statement two calls both hold is marked a duplicate, and when two files in different calls become the same policy file, the later one is skipped. The command skips a file over 100,000 characters. One steering PR holds at most 299 records and policy files, so --yes refuses an import that marks more add. The command calls parse_markdown_import and commit_markdown_import.
oxagen repo: workspace repositories
oxagen repo list [--json]
oxagen repo link <owner/name> [--json]
oxagen repo unlink <binding-id> [--json]
oxagen repo tree <binding-id> [--json]
oxagen repo branch <binding-id> <branch> [--json]
oxagen repo init <binding-id> --workspace-toml <file> [--mode solo|team|regulated] [--governance-toml <file>] [--json]
oxagen repo governance --mode solo|team|regulated [--workspace <wrk_id>] [--apply-now] [--json]
oxagen repo scan <owner/name> [--gitlab] [--json]A workspace has one steering repository, which holds its steering records, and any number of linked repositories its agents work on. The steering repository's workspace.toml lists the linked ones, so linking and unlinking open a steering PR that edits it. The link takes effect when that PR merges.
| Subcommand | Description |
|---|---|
list | Lists every repository the workspace binds, the steering repository first, with each one's approved default ref and binding id (rpb_…). |
link <owner/name> | Opens the steering PR that links a GitHub repository. The workspace's GitHub App installation must reach it. |
unlink <binding-id> | Unlinks a linked repository. When workspace.toml lists it, a steering PR removes it. The steering repository is refused. |
tree <binding-id> | Reads what the repository holds under .oxagen/ on its production branch, from GitHub at the time you run it. |
branch <binding-id> <branch> | Confirms or changes the repository's production branch. The branch must exist on GitHub. |
init <binding-id> | Opens the pull request that adds .oxagen/ to a repository. It never writes to the production branch. --workspace-toml is required and names the .oxagen/workspace.toml file to propose. --governance-toml names the .oxagen/rules/governance.toml file to propose. Without it, Oxagen drafts one from --mode, which defaults to team. |
governance | Sets the governance mode, which decides who may merge a steering PR in the workspace. --mode is required. solo lets one person publish steering alone. team and regulated send each change through a reviewed pull request, and regulated also requires separation of duties. --workspace names another workspace by its wrk_… id. --apply-now lands the change at once even when the mode in force asks for a reviewed pull request, and Oxagen records the override. |
scan <owner/name> | Lists the harness files on a repository's default branch. See oxagen repo scan. |
link, unlink, branch, and governance need an organization Owner or the workspace's Admin. init needs an organization Owner. The command exits 2 on a bad argument and 1 on an API failure.
oxagen repo list
oxagen repo link acme/payments-api
oxagen repo branch rpb_01k5ru4a8x releaseoxagen repo scan: harness files in a repository
oxagen repo scan <owner/name> [--gitlab] [--json]Lists the files agent harnesses read on a repository's default branch. These are instruction files such as CLAUDE.md and AGENTS.md, rule files for Cursor, Windsurf and Cline, .github/copilot-instructions.md, skills, commands, subagent files, settings files and MCP configs. The repository doesn't have to be linked yet. On GitHub, the workspace's GitHub App installation must reach it. Pass --gitlab to scan a GitLab project the workspace has connected, named by its project path.
The command prints one row per file with its kind, path, harness and size. A skill is one row, its SKILL.md, with a count of the files in its folder. A file under fixtures/, test/, tests/, __tests__/, examples/ or node_modules/ is marked as a fixture. A symlink at a harness path is listed with the path it points to, and that path is listed on its own row. When GitHub cuts a large tree short, the scan reads only the folders harnesses use and says the list may be missing a nested CLAUDE.md or AGENTS.md. --json prints the full answer, with each file's blob sha.
The scan writes nothing. It needs the role linking does: an organization Owner or the workspace's Admin. The command exits 0 on success, 1 on an API failure and 2 on a bad argument. It calls scan_repository_config.
oxagen steering findings: instruction file findings
oxagen steering findings [--json]Lists the findings in the workspace's linked code repositories. A finding is a statement in an instruction file, such as AGENTS.md, that repeats or contradicts an active steering record. The Oxagen check stores each statement it flags when it runs on a linked repository's pull request. findings compares those statements with today's records, so a statement that matches no record now drops off the list.
The command prints one table for each repository that has a finding. Each row gives the finding's id (crf_…), its kind, the file and line, the record it matches, the pull request the check read, and the suggestion opened from it. The kind is repeat when the statement says what the record says, and contradiction when it says the opposite. --json prints the full answer, with each statement's text and links to the line and the pull request.
The command exits 0 on success and 1 on an API failure. It calls list_code_repository_findings.
oxagen steering promote: a contradiction into a suggestion
oxagen steering promote <finding-id> [--json]Turns one contradicting statement into a suggestion for the record it contradicts. Pass the crf_… id that oxagen steering findings shows. The suggestion is a new version of the record, with the statement as its text and the pull request and line as its evidence. The record keeps its record ID, kind, force, and scope. The suggestion's steering PR opens at once and runs the six checks. Nothing steers an agent until a person merges the PR.
The command prints the suggestion id (prp_…), the steering PR, and the status of its checks. Oxagen refuses the request and writes nothing in these cases:
| Reason | When |
|---|---|
already_in_steering | The statement repeats its record, so steering already says it. |
finding_resolved | The statement matches no record now. |
statement_too_long | The statement is over 2,000 characters. |
already_proposed | The suggestion opened from this finding is still open. |
lineage_pr_open | Another PR is open on the record. |
The command exits 0 on success, 1 on a refusal or an API failure, and 2 when the id does not start with crf_. With --json, the error line on stderr carries the reason as its code. The command calls promote_instruction_to_steering.
oxagen steering restore-block: the managed block on a steering PR
oxagen steering restore-block <suggestion-id> <AGENTS.md|CLAUDE.md|README.md> [--json]Puts Oxagen's managed block back in one file of an open steering PR. In a steering repo, AGENTS.md, CLAUDE.md, and README.md each hold a managed block: the lines between two marker lines that only Oxagen writes. The owned check fails a steering PR that changes one.
Pass the prp_… id of the suggestion whose steering PR holds the file, then the file's name. The command writes one commit on the PR's branch. The block becomes the one on the production branch, and every line outside the block stays as it is. If the PR deleted the file, the whole file comes back. If the PR removed only the block, the block goes back at the top. The six checks then run again on the new commit. The command prints the commit and the status of the checks.
Oxagen refuses the request and writes nothing when the PR is not open, and in these cases:
| Reason | When |
|---|---|
block_intact | The block at the PR's head already matches the production branch. |
no_managed_block | The production branch holds no block in the file. |
no_managed_blocks | The repository is not a steering repo. |
The command exits 0 on success, 1 on a refusal or an API failure, and 2 on a bad argument, such as an id that does not start with prp_ or a file with no managed block. With --json, the error line on stderr carries the reason as its code. The command calls restore_managed_block.
oxagen context: steering suggestions
oxagen context propose --lineage <id> --kind <kind> --force <force> --scope workspace|repository --statement "<text>" --rationale "<text>" [--effect require|forbid] [--json]
oxagen context revert <suggestion-id> [--json]propose records a suggestion for one steering record: the text the record should become, and why. It opens no pull request. A person opens the suggestion's steering PR from the Steering page in the app, and merging it there publishes the record. Every propose flag except --effect and --json is required.
Flag (propose) | Description |
|---|---|
--lineage <id> | The record ID: the file name, without its extension, under .oxagen/rules/. |
--kind <kind> | rule, constraint, procedure, fact, memory, or preference. |
--force <force> | must, should, may, or info. |
--scope <scope> | workspace or repository. |
--statement <text> | The record's claim, in one sentence. |
--rationale <text> | Why the record should be published. |
--effect <effect> | require or forbid. For a constraint only. |
--json | Print the result as JSON. |
revert opens a steering PR that undoes a merged one, named by the merged suggestion's id (prp_…). The revert merges after its own review, under the workspace's governance mode. It calls revert_steering_pr.
The command exits 2 on a bad flag and 1 on an API failure.
oxagen context propose --lineage deploy-window --kind constraint --force must --scope workspace --effect forbid --statement "Agents do not deploy on Fridays." --rationale "Weekend incidents have no on-call cover."
oxagen context revert prp_01k5ru4a8xoxagen tools: MCP server migration
oxagen tools migrate [--json]Opens the pull request that moves the workspace's connected MCP servers into its steering repo. When the pull request merges, the next publish moves each server. Running the command again prints the pull request already open, or says the workspace has already migrated. It needs an organization Owner, and it calls migrate_tools_to_steering. It exits 1 on an API failure.
oxagen agent: the agent identity
oxagen agent register --name <name> --harness <stella|claude-code|codex|cursor|claude-agent-sdk|custom> --runtime <rtm_id> [--slug <slug>] [--toolbelt <tbt_id>] [--description <text>] [--validity-days <n>] [--json]
oxagen agent status <agent> [--json]
oxagen agent unenroll <agent> [--host <tch_id>] [--reason <text>] [--yes] [--json]<agent> is an agent's id or slug. Without it, status and unenroll act on this machine instead. See oxagen agent on this machine.
register creates the agent through register_agent: one operator on one runtime with one harness, carrying a toolbelt. --name, --harness, and --runtime (an rtm_… id) are required. Without --toolbelt, the agent carries the workspace's All tools belt. The slug comes from --name unless you set --slug, written as lowercase words joined by hyphens. Registration also creates the agent's principal, its default role, its first version, and one long-lived credential. The credential lasts --validity-days days, from 1 to 365, and 180 by default. The command prints it once, and it cannot be recovered. No Oxagen surface accepts that credential yet: the API and the MCP server refuse it as a bearer token. To put the agent on a machine, run the one-time enroll command from the agent's Enrollment tab, as the command's output says. An agent carries no prompt and no definition file.
status reads get_agent: the identity, the runtime it runs on, the toolbelt it carries, its versions, its credentials (prefix and dates, never the secret), the roles on its principal, and the hosts enrolled under its key.
unenroll revokes the agent's live host enrollments through revoke_tacho_enrollment, or one host with --host. Without --host, it asks before it revokes every host, and --yes skips the question. --reason is recorded on the host and in the revoke.
register and unenroll need an organization Owner. The CLI acts as the person who approved its oxagen login, so that person's role decides.
oxagen agent register --name "Payments bot" --harness claude-code --runtime rtm_01k5ru4a8x
oxagen agent status payments-botoxagen agent enroll: put this machine under Oxagen
The Oxagen app puts this command on the machine's PATH. Install the Oxagen app on the host first.
oxagen agent enroll --token <oxe_1time_…> [--harness <list>] [--api-url <url>] [--credentials brokered|passthrough] [--validity-days <n>] [--port <n>] [--no-service] [--force] [--allow-root]
oxagen agent enroll [--token <apiKey>] [--org <slug>] [--workspace <slug>] [--harness <list>] [--api-url <url>] [--credentials brokered|passthrough] [--validity-days <n>] [--port <n>] [--no-service] [--managed] [--print-managed] [--force] [--verify] [--allow-root]Enrollment writes the device key, the host credential, the collector's user service, and the harness hooks. The first form enrolls this machine as a registered agent's host. The second form enrolls it under your CLI login, or under a platform API token you pass with --token.
| Flag | Description |
|---|---|
--token <token> | A one-time enrollment token (oxe_1time_…) from agent registration, or a platform API token. Without it, the command uses your CLI login. |
--org <slug> | Organization slug. Defaults to the logged-in org. Refused with an enrollment token. |
--workspace <slug> | Workspace slug. Defaults to the logged-in workspace. Refused with an enrollment token. |
--harness <list> | claude-code, codex, cursor, stella, or a comma list such as claude-code,cursor. A fresh enrollment defaults to claude-code. A re-apply keeps the current list. |
--api-url <url> | The Oxagen API base URL, when it is not the one oxagen login chose. |
--credentials <mode> | brokered, the default: the gateway holds each model vendor key and the harness holds a run token. passthrough: the harness keeps its own key. |
--validity-days <n> | How many days the enrollment stays valid. Defaults to 180. |
--port <n> | The loopback port for the collector, a whole number from 1024 to 65535. |
--no-service | Install no user service. Run oxagen daemon yourself. |
--managed | Also print the managed settings document for MDM. Refused with an enrollment token. |
--print-managed | Only print the managed settings document, and write no user settings. Refused with an enrollment token. |
--force | Enroll again even if already enrolled. |
--verify | Run one headless turn afterwards, of the first harness --harness names, or Claude Code. Refused with an enrollment token. |
--allow-root | Enroll even when the command runs as root. Without it, sudo oxagen agent enroll is refused, because it would write files the user's own agents cannot read. |
The first form is the scripted path of the register flow. The one-time enrollment token is the credential, so no oxagen login is needed. It is shown once on the Agents page, or created through create_enrollment_token, and it expires unused after 30 minutes by default. The control plane resolves the organization, the workspace, and the agent from it, and the host file records what it answered. A token that was already used or has expired is refused (token_used, token_expired). Issue a new one. A machine holds one enrollment per agent. Every agent keeps its files in its own directory, ~/.config/oxagen/tacho/agents/<id>/. On a machine that is already enrolled, a token for a harness that no agent hooks enrolls another agent in a new directory and revokes nothing. A token for a harness that an agent on the machine already hooks is refused: the command exits non-zero, presents nothing, and names the agent and host already enrolled. Pass --force to replace that agent's enrollment with the agent the token names, which is the one case that flag is for. The other agents on the machine keep their enrollments. A token that names a harness an agent already hooks together with a new one is refused with a message to run oxagen login. Adding a harness to an enrollment revokes it first, and only the CLI's session can revoke. With --force, the same command replaces that agent's enrollment with the token's agent instead of extending it. See Two agents on one machine. The command also reports the git remote of the directory it runs in. While onboarding is open, Oxagen records the GitHub repository that remote names.
oxagen agent on this machine
oxagen agent status [--json]
oxagen agent unenroll [--harness <name>] [--all] [--token <apiKey>] [--purge] [--reason <text>]
oxagen agent uninstall [--token <apiKey>] [--reason <text>]
oxagen agent reassign [--workspace <slug>] [--org <slug>] [--harness <list>] [--token <apiKey>] [--api-url <url>] [--reason <text>] [--default] [--allow-root]
oxagen agent export [--session <id>] [--format <format>] [--out <file>] [--list]
oxagen agent verify [--harness claude-code|codex|cursor|stella] [--json]
oxagen agent backfill [--since <date>] [--until <date>] [--project <name>]... [--exclude-project <name>]... [--session <id>]... [--dry-run] [--json]
oxagen agent detect [--json]
oxagen agent hosts [--status active|paused|suspended|revoked] [--limit <n>] [--json]Each of these acts on the machine you run it on, except hosts, which reads the control plane. See Wrap an agent for the full behavior.
| Subcommand | Description |
|---|---|
status | This machine's enrollments, collector, hooks, policy bundle, and spool. It exits 1 when no agent is enrolled, or when any agent's events are not reaching Oxagen. |
unenroll | Removes an agent's hooks and service, revokes its enrollment, and deletes its host key. On a machine with more than one enrollment, name the one to remove with --harness, or remove them all with --all. Pass one of the two. --purge also deletes the local event log, spool, and quarantine. --token is the operator token for the server-side revoke. |
uninstall | Takes Oxagen off this machine without the Oxagen app. It unenrolls every agent with --purge, then removes what the app recorded writing: the links, the shell profile lines, the per-user copies, and ~/.config/oxagen, which holds your oxagen login session. It stops after the unenroll if any enrollment is left. See Uninstall. |
reassign | Points an agent at another workspace or org. It revokes the enrollment and enrolls again through your CLI login, keeping the device key. --harness with no --workspace replaces the harness list. --default also makes the new org and workspace the CLI default in config.json. |
export | Exports a session from the local event log. --format trace writes the contextgraph-trace journal, and --format otlp writes OTLP JSON. The default, --format tacho, writes the recorder's own NDJSON. --list lists the sessions in the log. --out writes a file in place of stdout. |
verify | Runs one headless turn of a harness, Claude Code by default, and confirms the collector chained it. --json prints the result as JSON. It exits 1 when the turn was not chained. |
backfill | Records the Claude Code sessions this machine ran before it enrolled. See oxagen agent backfill. |
detect | Lists the harnesses this machine has (Claude Code, Codex, Cursor, Stella, and Claude Desktop) and which of them are enrolled. |
hosts | Lists every machine enrolled in this workspace, with the tier each of its apps reaches. |
oxagen agent backfill: record sessions from before enrollment
oxagen agent backfill [--since <date>] [--until <date>] [--project <name>]... [--exclude-project <name>]... [--session <id>]... [--dry-run] [--json]Records the Claude Code sessions this machine ran before it enrolled, from the transcripts Claude Code keeps under ~/.claude/projects. The running daemon reads each transcript, rebuilds the session's record from it, and sends it to Oxagen the way it sends a live session. The command only asks the daemon and prints its report.
A backfilled run is rebuilt, not watched. Nothing enforced a policy while it ran, so its record names no policy decision, approval, or token. Its git facts are the branch and directory the transcript recorded, and its cost is an estimate from the price book on the day it ran. That cost never counts toward a budget or a bill. Run --dry-run first and read the report before the first real pass.
| Flag | Description |
|---|---|
--since <date> | Only sessions that started on or after this UTC date, written YYYY-MM-DD. |
--until <date> | Only sessions that started before this UTC date. |
--project <name> | Only this folder under ~/.claude/projects. Repeat it for more folders. A folder's name is its working directory with each / and . written as -. |
--exclude-project <name> | Skip this folder. It applies after --project. A machine enrolled into a work workspace should skip its personal folders. |
--session <id> | Only this session and its subagents. Repeat it for more sessions. |
--dry-run | Read every transcript, count what a pass would record, and ask Oxagen which sessions it already holds. Record and send nothing. |
--json | Print the report as one JSON object, with no progress lines. |
The report prints counts only: each folder with its session count and dates, the sessions by what happened to them, the records by kind, the tokens by model, and the read errors. It never prints a prompt, a reply, a title, a tool input, or a path from inside a transcript.
While the policy bundle on this machine does not verify, the daemon holds a pass's bodies on this machine instead of sending them, as it does for a live session. The report says how many still wait and how many found no room. A live session's held bodies keep their room, so a pass drops its own bodies first. A dropped body's event still goes to Oxagen without it.
A pass leaves a session alone when this machine already records it live, when Oxagen already holds it, when an earlier pass recorded it, or when its transcript changed in the last 15 minutes. It asks Oxagen first, so a machine that lost its local state does not record a session twice. The pass records no session Oxagen did not answer for. Run the command again later to record those sessions. A pass that stops partway resumes where it stopped the next time it runs.
| Exit code | Meaning |
|---|---|
0 | The pass finished. A skipped session is not a failure. |
1 | The pass stopped before the end. Run the command again to resume. |
2 | An option is invalid. |
3 | The daemon did not answer. oxagen agent status shows why. |
4 | This machine is not enrolled. |
oxagen agent run: one agent session under Oxagen
oxagen agent run [--name <agent>] [--contained] [--image <ref>] [--workspace <dir>] [--github-repository <owner/name>] -- <command> [args...]Runs one agent session under Oxagen control. Everything after -- is the agent's command and its arguments. What the command names decides what happens:
- A custom agent. Any other command is recorded as a custom agent under
--name. Oxagen opens the session with aSessionStarthook call. The agent starts only when Oxagen accepts the session, and it runs withOXAGEN_AGENT,OXAGEN_SESSION_ID, andOXAGEN_HOOKin its environment, so it can report its own steps. The session closes withSessionEndwhen the agent exits. - A harness this machine wraps.
claude,codex,cursor-agent, orstellaruns as it is, and that harness's own hooks record the session. A harness no agent on this machine wraps is refused. --contained. Starts Claude Code or Codex in the contained launcher, on Linux with Docker.oxagen run -- claude …does the same.
| Flag | Description |
|---|---|
--name <agent> | The custom agent's name: lowercase letters, digits, ., _, and -. Defaults to the command's file name. With --name, any command is recorded as a custom agent. |
--contained | Start Claude Code or Codex in the contained launcher. |
--image <ref> | With --contained: the image. OXAGEN_CONTAINED_IMAGE also sets it. |
--workspace <dir> | With --contained: the repository root to mount at /workspace. Defaults to the current directory. This is a directory, not the workspace slug enroll takes. |
--github-repository <owner/name> | With --contained: the one repository the run may fetch and push. Oxagen's Git custody gets the credential for each request, so you mint no token. The workspace must have the repository bound. |
The command exits with the agent's own exit code. It exits 1 when the machine is not enrolled, when Oxagen refuses the session, or when the command cannot start. It exits 2 when no command follows --, when the custom agent's name is invalid, or when the harness is not wrapped on this machine. See Wrap a custom agent.
oxagen work: work orders on this machine
oxagen work list
oxagen work start <work-order>
oxagen work claim <criterion> --text "<how the commit meets it>" [--work-order <wo_id> --item <wi_id>]
oxagen work staffing <item> [--json]A person sends work to an agent from Work in the app. The work order waits on the agent's host until someone at that machine starts it.
| Subcommand | Description |
|---|---|
list | Lists each work order waiting on this machine, with its work item, when it arrived, and its key. |
start <work-order> | Claims the work order, then starts the agent's harness in the current directory with the brief as its first prompt. Run it in a checkout of the brief's repository. A work order id starts with wo_. |
staffing <item> | Recommends the model tier and effort for a work item, such as WI-12, from the runs similar work ran on in the last 30 days: the pair they ran on, the median recorded cost, and a lighter choice with how often runs on it met their checks. With no similar run on record, it shows the tier triage drafted from the tier guidance and says so. A cost is an estimate from recorded runs, not a saving. It needs a login. |
claim <criterion> --text <text> | The agent runs this inside the work order's run. It claims that the head commit in the current directory meets one criterion of the brief, such as c1, and says how. Push the commit to the pull request first. |
Flag (claim) | Description |
|---|---|
--text <text> | Required. How the commit meets the criterion, in 1 to 2000 characters. |
--work-order <wo_id> | For a batch. A batch runs several work items in one run, and the run's own work order is the one start started. A claim for any other item of the batch names that item's work order here, as the first prompt lists it. |
--item <wi_id> | The work item of --work-order. Give the two flags together or not at all. |
start claims the work order before anything runs. When Oxagen refuses the claim, nothing starts, and the message says why: the send ended, it went to another machine, or a run already started for it. start never claims again on its own. When this machine does not wrap the agent's harness, or the harness command is not installed, start tells Oxagen, and Oxagen ends the send so the item can be sent again. The work order stays in list until the harness starts.
claim works only inside a run that start started, because it reads the work order from the OXAGEN_WORK_ORDER_ID variable start sets for the harness. Oxagen records the claim beside the criterion on the work item. A claim is the agent's word: a person still checks every criterion and decides. Oxagen refuses a claim on a commit that is not the pull request's head, and the message says to push first. The same claim on the same commit again records nothing new.
| Exit code | Meaning |
|---|---|
0 | list finished. For start, the harness ran and exited 0. For claim, Oxagen recorded the claim or already had it. |
1 | No agent is enrolled on this machine, the work order is not waiting here, Oxagen refused the claim, or the harness could not start. For claim, the command ran outside a work order's run, the directory has no commit, or Oxagen refused the claim or could not be reached. |
2 | The id is not a work order id or a criterion id, the --text is empty or longer than 2000 characters, or --work-order and --item are malformed or not given together. |
After a successful claim, start exits with the harness's own exit code.
oxagen work claim c1 --text "The retry test covers the timeout path in commit 4f2a9c1."oxagen run: recorded runs
oxagen run [--image <ref>] [--workspace <dir>] [--github-repository <owner/name>] -- <claude|codex> [args...]
oxagen run --harness <claude|codex|stella> --workspace-slug <slug> (--repo <owner/name> [--branch <name>] | --work-order <wo_id>) [--runtime <slug>] [--prompt <text> | --prompt-file <path>]
oxagen run list [--limit <n>] [--cursor <cursor>] [--json]
oxagen run show <run-id> [--after <cursor>] [--session <uuid>] [--json]
oxagen run chain <run-id> [--json]
oxagen run turns <run-id> [--json]
oxagen run transcript <run-id> [--zoom turns|steps|everything] [--kinds <kinds>] [--query <words>] [--after <cursor>] [--limit <n>] [--text excerpt|full] [--json]
oxagen run context <run-id> [--json]
oxagen run export <run-id> [--json]
oxagen run export-status <export-id> [--json]
oxagen run download <export-id> [--out <file>] [--json]
oxagen run pause-all --reason "<text>" [--json]
oxagen run answer <interjection-id> (--text "<answer>" | --link | --create <name> --slug <slug>) [--json]These commands read the workspace's recorded runs and act on them. A run id starts with tse_. An older arun_ id still works.
The first two forms start runs. oxagen run -- claude … starts Claude Code or Codex in the contained launcher, with the same --image, --workspace, and --github-repository flags as oxagen agent run with --contained. oxagen run --harness … starts a sandbox run, the same as oxagen sandbox run without --json. See oxagen sandbox. A run takes one form or the other: a sandbox flag with a command after -- is refused, and so is --workspace on a sandbox run.
| Subcommand | Description |
|---|---|
list | The workspace's runs, newest first, one page at a time. --limit takes 1 to 100. --cursor takes the cursor the previous page printed. |
show <run-id> | One run's header, the steering versions it received, the pause in force, a page of its frames, and its subagent chains. --after reads the next page. --session shows one subagent chain, by the session id the run lists. |
chain <run-id> | What makes the run's record tamper-evident: the hash rule, the root, each point where the agent's computer locked the record, the gaps, and the replay ladder. Locked means any change after that point shows. |
turns <run-id> | The run's cost turn by turn: each turn's model and tool steps, frames, cache hit, cost, and the cost so far. |
transcript <run-id> | One page of the run's transcript, with the count of each kind of entry over the whole run. --zoom takes turns, steps (the default), or everything. --kinds keeps only the listed kinds, comma-separated, such as thinking,seal. --query keeps the entries that hold the words, ignoring case. --limit takes 1 to 500. --text carries an excerpt or the full text of each body. |
context <run-id> | What each of the run's model requests carried: its prompt tokens and each block's share of them. |
export <run-id> | For a locked run, queues an evidence bundle that Oxagen locks, and prints its export id (rexp_…). Oxagen builds the bundle in the background, and anyone can check it offline. Needs an organization Owner. |
export-status <export-id> | Where the export stands, and its download link once it is ready. |
download <export-id> | Downloads a ready export and checks its sha256 hash against the hash Oxagen recorded. --out names the zip file, which defaults to <run-id>-<export-id>.zip in the current directory. Check the bundle with oxagen verify. |
pause-all | Pauses every live wrapped run in the workspace as one recorded decision. --reason is required and recorded. Each run stops at its next stopping point after its host collects the command. The output lists any run it skipped, such as one whose host is offline. Needs an organization Owner or the workspace's Admin. |
answer <interjection-id> | Answers the question a run paused to ask, by its id (inj_…). --text answers an agent's own question. When a session started in a repository the workspace has not bound, --link opens the steering PR that links the repository to this workspace, and --create <name> --slug <slug> creates a new workspace for it with skills off. Give exactly one answer. The CLI does not list questions. The Run page shows them. |
--text on answer needs an organization Owner, or a workspace Admin or Member. --link and --create need an organization Owner or the workspace's Admin. --json prints the result as JSON. Each subcommand exits 1 on an API failure.
oxagen run list --limit 20
oxagen run turns tse_01k5ru4a8x
oxagen run export tse_01k5ru4a8x
oxagen run download rexp_01k5ru9c2m --out evidence.zipoxagen verify: run export bundles
oxagen verify <bundle> [--json]Checks a run export bundle offline. <bundle> is the zip oxagen run download wrote, or the folder it was unpacked into. The check covers each frame's hash and its link to the frame before, the Merkle root, each locked root and the Ed25519 lock on it, and the redaction summary against the frames. It makes no network call and reads no CLI login, so an auditor can run it on a clean machine.
The output lists each frame, then each bundle check, then the redaction summary, and ends with HELD or BROKEN. --json prints the whole verification as one JSON line. The command exits 1 when any check fails.
oxagen verify evidence.zipoxagen approvals: resolved approvals
oxagen approvals resolved [--run <run-id>] [--since <instant>] [--until <instant>] [--limit <n>] [--cursor <cursor>] [--json]Lists the workspace's resolved approvals, most recently resolved first. The list holds the calls a person approved or denied and the calls a decision rule approved with no person. The approver column shows policy:<rule id> for a rule and user:<usr_…> for a person. The command calls list_resolved_approvals.
| Flag | Description |
|---|---|
--run <run-id> | Only the approvals recorded on this run. |
--since <instant> | Only rows resolved at or after this ISO 8601 time. |
--until <instant> | Only rows resolved at or before this ISO 8601 time. |
--limit <n> | Page size, 1 to 100. |
--cursor <cursor> | The nextCursor of the previous page. |
--json | Print the raw result as JSON. |
oxagen approvals resolved --since 2026-10-01T00:00:00Z --limit 50Machine commands
These commands are hidden from oxagen --help. Enrollment writes them into the machine's hooks, service, and app settings, and those programs run them. You run one yourself only to report a custom agent's steps with oxagen hook --agent <name>.
| Command | What runs it |
|---|---|
oxagen hook | Each wrapped harness's hooks, on every hooked event. A custom agent runs it with --agent <name>. It reads the payload on stdin, prints the answer on stdout, and exits 0. |
oxagen daemon | The user service. It runs the collector in the foreground. |
oxagen mcp-stdio | A connected app, to reach this machine's Oxagen toolbelt over stdio. |
oxagen credential issue, oxagen credential status | Claude Code runs issue as its apiKeyHelper for a fifteen-minute model token. status shows the model credentials the gateway holds. |
oxagen github configure, oxagen github credential | git, to route repository requests through this machine's credential custody. |
oxagen arp | Saves a stopped run's selected files and brief as an Agent Run Protocol save point, and restores a save point to start a fresh run. |
oxagen env: workspace environments
oxagen env list [--json]
oxagen env get <id-or-slug> [--json]
oxagen env create <name> [--slug <slug>] [--description <text>]
oxagen env update <id-or-slug> [--name <name>] [--slug <slug>] [--description <text>] [--active | --inactive]
oxagen env rm <id-or-slug> [--yes]
oxagen env set-default <id-or-slug>An environment is a named set of secret values in a workspace. A sandbox run takes its secrets from the environment its sandbox runtime names, or from the workspace's default environment when the runtime names none. The app has no environment page yet, so you manage environments here or through the API.
create derives the slug from the name unless you pass --slug. update --inactive turns an environment off, and the default cannot be turned off. rm asks before it deletes unless you pass --yes, and it refuses the default. set-default makes an environment the workspace default.
oxagen env create Staging --slug staging --description "Pre-release checks"
oxagen env set-default stagingoxagen secret: credential vault
oxagen secret list [--json]
oxagen secret set <key> [value] [--env <slug>] [--no-sensitive]
oxagen secret rm <key> [--env <slug>] [--yes]
oxagen secret reveal <key> [--env <slug>]
oxagen secret import [--env <slug>] [-f, --file <path>] [--yes]
oxagen secret export [--env <slug>] [-o, --out <path>]The workspace's encrypted credential vault. list shows masked metadata only. Each key has a default value and can have an override for each environment. --env targets one environment's override. Leave it out for the default value.
set stores the value encrypted. --no-sensitive stores it as plain configuration instead. Leave the value out, or pass -, and set reads it from standard input, which keeps it out of your shell history. rm asks before it deletes unless you pass --yes. With --env, it removes only that environment's override.
reveal and export return plaintext, and Oxagen records each one in the access log. export writes .env text to standard output, or to -o, --out. import reads .env text from -f, --file or from standard input, and previews the change unless you pass --yes.
printf %s "$STRIPE_KEY" | oxagen secret set STRIPE_KEY --env staging
oxagen secret import --env staging -f .env.staging --yesoxagen sandbox: runs in their own sandboxes
oxagen sandbox run --harness <claude|codex|stella> --workspace-slug <slug> --repo <owner/name> [--branch <name>] [--runtime <slug>] [--prompt <text> | --prompt-file <path>] [--json]
oxagen sandbox run --harness <name> --workspace-slug <slug> --work-order <id>
oxagen sandbox attach <run> [--workspace-slug <slug>] [--watch]
oxagen sandbox stop <run> [--workspace-slug <slug>] [--json]
oxagen run --harness <name> --workspace-slug <slug> --repo <owner/name>run starts a run on your own agent in its own sandbox on a sandbox runtime. oxagen run --harness … does the same, without --json. Name the workspace with --workspace-slug or OXAGEN_WORKSPACE. The CLI never reads it from the repository. --workspace stays the contained launcher's directory flag.
With a prompt or a work order, run prints the sandbox id and the command that opens its terminal. With neither, the run opens a terminal, and run attaches to it at once when your terminal is interactive. A workspace with no sandbox runtime refuses the launch and says to ask an org Owner to add one on the Runtimes tab of the Agents page, with the workspace's Modal account connected. --branch defaults to the repository's default branch. --runtime picks a sandbox runtime when the workspace has more than one. With --json, run prints the run as JSON and does not attach.
attach opens the terminal of one of your runs, named by its sandbox id (sbx_…) or its run id (tse_…). Every keystroke reaches the run and the terminal size follows yours. Ctrl-] detaches and leaves the run going. --watch reads the screen and sends no keystrokes. A dropped connection is dialed again and replays the output it missed. Only the operator who launched a run can attach to it or stop it.
oxagen credentials: your model credentials
oxagen credentials add [--type api-key|subscription] [--provider anthropic|openai|openai_compatible] [--base-url <url>] [--harness claude-code|codex] [--label <name>] [--workspace-slug <slug>] [--json]
oxagen credentials list [--workspace-slug <slug>] [--json]
oxagen credentials update <credential> [--label <name>] [--pause | --resume] [--workspace-slug <slug>] [--json]
oxagen credentials rotate <credential> [--yes] [--workspace-slug <slug>] [--json]
oxagen credentials remove <credential> [--workspace-slug <slug>] [--json]oxagen keys is the same command, so oxagen keys rotate vlt_… works too.
Your own API keys and Claude or ChatGPT logins. The gateway attaches them to your runs only, in the order list shows, and moves to the next one when a provider says one is out of usage. A credential never enters a sandbox.
To keep a key for your own OpenAI-compatible server, name it with --provider openai_compatible and give its address with --base-url, such as oxagen keys add --provider openai_compatible --base-url https://llm.example.com/v1. The address must use https and can't be a private address. Sandbox runs don't use your own endpoint yet, so Oxagen keeps the key until they do.
rotate replaces the key of one of your API keys and keeps its name, its status, and its place in the order. It names the key and asks before it reads the new one. Answer y, then paste the new key at the hidden prompt. Your runs use the new key from their next model call. Oxagen keeps no copy of the old key, so revoke it at your provider. To pipe the new key in, pass --yes, because the pipe can't answer the question: oxagen keys rotate vlt_… --yes < new-key.txt. A subscription login has no key to rotate: add the new login, then remove the old one.
add reads the secret from a hidden prompt, or from standard input when it is piped. It takes no secret argument, so the secret stays out of your shell history and the process list. list shows the last four characters of each credential and never the secret. --type defaults to api-key, and an API key names its provider with --provider: anthropic, openai, or openai_compatible with --base-url for your own endpoint. A subscription login names its harness, such as --type subscription --harness claude-code, and takes its provider from it. A ChatGPT login is the file codex login writes, so pipe it in: oxagen credentials add --type subscription --harness codex < ~/.codex/auth.json. --workspace-slug, or OXAGEN_WORKSPACE, names the workspace these commands address, and they fall back to your saved login's workspace. --pause makes the gateway skip a credential until you --resume it.
These commands work only with the session oxagen login gives you. A platform API key passed with OXAGEN_API_TOKEN is refused. Each add, pause, resume, rotation, or removal made from the command line sends you an email and a notice in the app. Both name the credential, the session key, and the address the change came from. If you didn't make the change, revoke that sign-in yourself: open Account settings, choose Security, and find it under Command-line sign-ins. oxagen logout on the machine that holds it ends it too.
oxagen api-key: API key rotation
oxagen api-key rotate <key-id> [--name <name>] [--json]
oxagen api-key acknowledge <key-id> [--json]rotate issues a replacement for a workspace API key, named by its public id (aky_…). The old key keeps working for 24 hours, so a lost response does not cut a caller off. The new secret is the only thing the command prints on standard output, so a script can capture it. --name gives the replacement a new label.
acknowledge takes the replacement key's id and confirms the new secret arrived. That revokes the old key at once.
Both need an organization Owner. The command exits 1 on a failure.
NEW_KEY=$(oxagen api-key rotate aky_01k5ru4a8x --name "CI deploy key")
oxagen api-key acknowledge aky_01k5rv7d3qoxagen router: verified-outcome market router
oxagen router preview <prompt> [--files <n>] [--cross-package] [--task-class <class>] [--json]
oxagen router policy get [--json]
oxagen router policy set [--scope org|workspace] [--mode off|shadow|enforce] [--threshold <n>] [--min-samples <n>] [--window <days>] [--escalate <true|false>] [--json]Learned, cost-aware model routing. For each task class, the router reads the outcomes Oxagen has verified and sends the work to the cheapest model whose verified-success rate clears a threshold. preview dry-runs the routing decision for a prompt and changes nothing. policy get shows the policy in force and where each value came from.
| Flag | Description |
|---|---|
--files <n> | preview: how many files the task should touch. |
--cross-package | preview: the task crosses package boundaries. |
--task-class <class> | preview: overrides the task class the router derives. |
--scope <scope> | policy set: org or workspace, the default. |
--mode <mode> | policy set: off, shadow, or enforce. |
--threshold <n> | policy set: the verified-success rate a model must clear, from 0 to 1, such as 0.95. |
--min-samples <n> | policy set: the samples a model needs before the router trusts it. |
--window <days> | policy set: the days of recent outcomes the router reads. |
--escalate <bool> | policy set: true or false, whether to move up a tier when the judge rejects a round. |
--json | Print the result as JSON. |
policy set changes spend behavior. With --scope org, it needs an organization Owner. With --scope workspace, it also accepts the workspace's Admin.
oxagen router preview "Rename the billing module" --files 12 --cross-package
oxagen router policy set --mode shadow --threshold 0.95oxagen conversation: export a conversation
oxagen conversation export <id> [--format md|markdown|pdf] [-o, --output <file>] [--json]Exports a conversation's active branch. --format defaults to md, which is the same as markdown. Markdown goes to standard output or to -o, --output. A PDF is stored privately, and the command prints the serve URL.
oxagen asset: ingest a binary asset
oxagen asset upload <url> [--kind avatar|image|document|video] [--filename <name>] [--conversation <id>] [--json]Ingests an asset from a public URL into object storage. --kind defaults to image. --filename sets the name shown for it. With --conversation, it records the asset as a private chat attachment linked to that conversation.
oxagen logs: CLI debug log
oxagen logs [-n, --lines <n>] [--category invoke|api|error] [-f, --follow] [--path] [--clear] [--json]Reads the CLI's debug log at ~/.oxagen/logs/cli.output. The CLI writes that log only while OXAGEN_CLI_DEBUG is set to 1 or true. Then it records each command, each API request and response, and fatal errors. The entries carry request and response bodies, so only your user can read the file.
-n, --lines shows the most recent entries and defaults to 50. --category keeps one category: invoke, api, or error. -f, --follow follows the log as it grows. --path prints the log's location, and --clear empties it. --json prints the raw JSON lines.
OXAGEN_CLI_DEBUG=1 oxagen run list
oxagen logs --category api -n 20oxagen telemetry: anonymous usage telemetry
oxagen telemetry [on|off|status]Anonymous usage telemetry: command names, durations, coarse success and error categories, and OS and architecture. It never includes code, prompts, file contents, paths, model slugs, or keys. It is on by default. oxagen telemetry off or DO_NOT_TRACK=1 turns it off.
Login and logout
oxagen login [--token <token>] [--org <slug>] [--workspace <slug>] [--browser | --no-browser] [--signup]
oxagen logout| Flag | Description |
|---|---|
--token <token> | A platform API token. It skips the browser, for CI and other machines without one. |
--org <slug> | The organization. With a saved session and no --token, it switches the default without opening a browser. |
--workspace <slug> | The workspace. With a saved session and no --token, it switches the default without opening a browser. |
--browser | Opens the browser even without a terminal, and even when a session is saved. The desktop app runs this. |
--no-browser | Asks you to paste a token instead of opening a browser. |
--signup | Opens the sign-up page first to create an Oxagen account, then the same consent page. It implies --browser. |
logout revokes this machine's sign-in on the server, then clears the stored session from ~/.config/oxagen/config.json. It clears the session even when the server can't be reached, and then warns that the key may still work. A token passed with --token is an API key, and logout leaves it working.
oxagen login # browser sign-in and an org and workspace picker
oxagen login --token ox_… --org acme --workspace main # CI or another machine without a browser
oxagen login --org acme --workspace payments # switch the default workspace
oxagen login --no-browser # paste a token instead of opening a browser
oxagen logout # revoke this machine's sign-in and clear the stored sessionSee Account setup.