b10x-harness
Generated by harness-docs from the clap definition of the b10x-harness command line in crates/harness-cli/src/lib.rs. It lists only commands that exist; --help after any command prints the same text. The argv surface is pinned as contract 2026-10-30 under contracts/cli/b10x-harness/. For worked examples, credentials, the skill and memory file formats, standard streams and exit status, see the command-line guide.
The b10x agent loop, run against an OpenAI-compatible endpoint
Options of b10x-harness itself
Given without a command.
Usage: b10x-harness <COMMAND>
| Argument | Required | Default | Meaning |
|---|---|---|---|
--version | no | none | Print version |
More columns: swipe horizontally, or focus the table and use the arrow keys.
Commands
| Command | What it does |
|---|---|
b10x-harness run | Run one request to completion |
b10x-harness chat | Ask one question after another over the same session, reading a line at a time |
b10x-harness sessions | List the sessions on this machine, newest first |
b10x-harness tools | Print the tools this harness publishes, without contacting an endpoint |
b10x-harness profiles | Read the profiles this machine is configured with, without running one |
b10x-harness profiles list | Every profile, and the file it came from |
b10x-harness profiles show | One profile's effective table |
b10x-harness profiles explain | What a -p expands to, and who set each key |
b10x-harness profiles init | Write a starter config, and print where it went |
b10x-harness profiles trust | Let this repository's own .b10x/harness.toml apply to runs over it |
b10x-harness profiles untrust | Stop applying a repository's own .b10x/harness.toml |
b10x-harness providers | Read the providers this build ships, and any the config overrides |
b10x-harness providers list | Every provider this build ships, and which the config overrides |
b10x-harness providers show | One provider's effective endpoint, wire, model and credential |
b10x-harness toolchains | Inspect and validate declarative toolchain providers |
b10x-harness toolchains list | List built-ins and providers from explicitly named specification files |
b10x-harness toolchains show | Show one validated provider definition and its body-free provenance |
b10x-harness toolchains validate | Validate one or more custom specification files against built-ins and each other |
b10x-harness context | Inspect attributable instruction layers without contacting a model |
b10x-harness context show | |
b10x-harness app-server | Serve one connection over the pinned Codex app-server JSON-RPC format, on stdio |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness run
Run one request to completion
Usage: b10x-harness run [OPTIONS] --input <INPUT>
| Argument | Required | Default | Meaning |
|---|---|---|---|
--profile <NAME> | no | none | Profiles to apply, in order. Later wins a contested key; a typed flag beats them all. Read from $XDG_CONFIG_HOME/b10x/harness.toml. A profile decides what a run may do, so b10x-harness profiles explain -p <name> prints the argv it expands to before a token is spent, and session.started names every profile that contributed with a digest of what it said. That record is the condition on which a file is allowed to carry a permission at all. A trusted workspace's own .b10x/harness.toml (profiles trust) sits between [default] and the first -p, and may set only provider, model and max-turns. |
--base-url <BASE_URL> | no | none | Endpoint origin plus API prefix, for example https://llmgw.example/v1. Optional only because a provider can supply it — [default] provider = "claude". A run with neither is refused by name rather than aimed at a default endpoint nobody chose. |
--model <MODEL> | no | none | Exact model identifier the endpoint serves. As --base-url: a provider may supply it |
--context-window <CONTEXT_WINDOW> | no | 128000 | Context window the endpoint serves for that model. It bounds the request the wire will build, and it is also what makes compaction token-aware: the loop compacts at 80% of this figure — measured by the provider's own last reported input count where there is one — and frees down to 50%, instead of the fixed 192 KiB byte rule that left roughly 60% of a 128k window unused. |
--api-key-file <API_KEY_FILE> | no | none | File holding the bearer credential. Its contents are trimmed and never logged |
--api-key-env <API_KEY_ENV> | no | none | Name of an environment variable holding the bearer credential. Naming it is deliberate: the harness reads no credential it was not pointed at |
--api-key-secret <NAMESPACE/NAME> | no | none | The bearer credential's place in the local secrets store, as <namespace>/<name>. The layout secretsctl put <name> --namespace <namespace> writes, read again on every call, so a key rewritten in the store is the one the next call sends. The store is opened only when this is given: without it this harness never reads the store, and a provider default never stands in for it. |
--oauth-token-file <OAUTH_TOKEN_FILE> | no | none | File holding a subscription OAuth token, for a route that takes one instead of an API key. A path typed here is re-read on every call and is never renewed or written by Harness. A selected built-in provider may instead supply a documented default path; providers show prints it before a run. The codex provider may renew and atomically rewrite only its own default source before the first request, and records that write even under --quiet. |
--oauth-token-env <OAUTH_TOKEN_ENV> | no | none | Name of an environment variable holding a subscription OAuth token |
--oauth-token-pointer <POINTER> | no | none | JSON pointer to the token inside the named source, when that source is a JSON document. Absent means the whole source is the token. Named rather than known: which field a given credential store puts its access token in is that store's business, and a built-in path would silently read the wrong field the day it changed. |
--wire <WIRE> | no | none | Which model API to speak. Both reach --base-url; they are different endpoints under it One of openai-responses, anthropic-messages. |
--workspace <WORKSPACE> | no | . | Directory the read-only workspace tools may see |
--substrate <SUBSTRATE> | no | none | The substrate daemon's socket, so a run may write and execute inside a confined workspace. Without it the toolset is read-only, which is what this harness has always published. With it, what appears is what the daemon says the machine can confine — a host with no delegated cgroup root serves workspaces and no execution, and the run then has the write tools and no run tool. Named rather than discovered, for the reason the credential is: a harness that picked up a confinement boundary from the environment is one whose effects depend on where it happened to be started. |
--substrate-embedded | no | false | Hold substrate's driver in this process instead, confining --workspace itself. The same confinement — guarded IO, openat2 containment, cgroups and namespaces around an exec are the driver's, and they are here. What a socket adds and this does not is an authenticated subject derived from kernel peer credentials; embedded there is no peer. Right for a run on the operator's own machine, wrong for anything multi-tenant. The workspace is adopted, not created: --workspace is the tree, its parent becomes substrate's root, and reads and writes land in the same place. Its directory name is the workspace's own id, so it must be one path component of ASCII alphanumerics, _ and -, and may not be ., .. or begin with -; a name that is not refuses the run by name rather than quietly writing somewhere else. ASCII, and that is the word to read twice: café, Projekt-Übung and 日本語 are ordinary directory names made of letters, and this binary refuses all three. Point this at a project you already have — no prefix on the name, and no scratch copy of the tree. |
--substrate-adopt <ROOT_NAME> | no | none | Adopt this directory under the socket daemon's workspace root, and act in it. The name is one directory under the root the daemon serves (substrate's root-name set: ASCII alphanumerics, _ and -, not beginning with -), not a path on this host. The daemon mints a workspace id for it, one stderr line names that id, and a later run passes it with --workspace-id instead — adopting the same name twice is refused, and this binary cannot list workspaces to find the earlier id. A refused adopt refuses the run. |
--execution-path <PATH> | no | direct | How this native loop was launched: directly, or as a metaharness comparison arm. This is context, not permission. Metaharness sets it explicitly when it drives this binary; no environment variable may silently change the answer. One of direct, metaharness. |
--workspace-id <WORKSPACE_ID> | no | default | Which confined workspace the write and execute tools act in. Ignored under --substrate-embedded, which opens one and names it: the driver mints the identity there, and a caller-supplied name would have to satisfy a rule only the driver knows. Under --substrate-adopt the daemon mints it, so the two are not given together. |
--cgroup-root <CGROUP_ROOT> | no | none | A delegated cgroup subtree, so the embedded driver may confine a process. Without one substrate's probe reports no exec facts and no run tool is published — which is correct and is also why a test-first task cannot be attempted: a run that may not execute its suite cannot see a test fail before writing the code, so it will not write the code. The subtree must be delegated to this user, hold cpu, memory and pids, and be free of processes. |
--toolchain <NAME> | no | none | Admit one or more declarative toolchain providers read-only inside the confined workspace. Without one a confined run can execute anything whose implementation lives under /usr — an interpreter — and nothing whose compilers and package registry live in the operator's home, which is every build tool. The directories are mounted read-only and reported in the run's observation (substrate ADR 0010); the network stays unshared, so this brings a closure in and is not a way to reach out. Built-ins include Rust, Go, Taskfile, npm and Yarn. Use auto for project-marker discovery or a comma-separated provider list for an explicit selection. Declared rather than implied, because it is the one place a confined run is given something substrate did not verify: there is no digest over a package registry. A run that does not need a toolchain should not name one. |
--toolchain-spec <FILE> | no | none | Explicit operator-authored toolchain provider definitions. Repeatable and never discovered |
--driver <PATH> | no | none | A program on this host, staged and admitted read-only so a confined run can execute it. Allow-listing a program by absolute path admits its name, not its bytes. The sandbox reaches /usr, /bin, /lib, /lib64 and the workspace; a path outside those is not there, so the exec dies at ENOENT and the model reads that as the command is wrong. Naming it here links exactly that one file into a private directory and mounts it read-only at /toolchain/driver, and adds the mounted path to the allow-list, so declaring it once is the whole declaration. Read-only on purpose: a run that could rewrite the program recording its own evidence has no evidence. The digest of what was staged is reported by tools. |
--allow-program <ALLOW_PROGRAM> | no | none | A program run may start. Repeatable, and an empty set publishes no run at all. The declaration is checked against argv[0], the root executable Harness starts. Programs that executable starts are not separately matched; they remain inside the same sandbox, process limits, no-network namespace, workspace boundary and whole-tree timeout. Declared rather than open, because an argv whose program could be anything is a shell with extra steps. A set nobody named means nobody wanted one. |
--process-write-subtree <DIR> | no | none | Workspace-relative directory a confined run process may change. Repeatable. With no declaration, process execution sees the workspace read-only. File tools keep using --write-scope; the two are separate because an arbitrary glob cannot be converted to an exact writable mount without widening it. Name every build-output directory a command needs. |
--surface <SURFACE> | no | flat | How the catalogue reaches the model: every entry as its own tool, or three verbs over it. flat by default. The three-verb surface cost 33–44% of every tool call on discovery across three measured runs and hands the provider an untyped argument object it cannot validate; the neutral names it existed to protect are the entry names, which flat publishes directly. verbs is still fully served — metaharness offers it over MCP — and is what an arm comparing the two surfaces asks for. One of flat, verbs. |
--mcp-profile <FILE> | no | none | Reviewed MCP publication profile. Repeatable; every profile freezes one connection. Discovery alone grants nothing. Each file pins the shared registry digest and the exact tools/list snapshot, then assigns Harness-owned names, descriptions and envelopes to the subset this run may publish. A changed endpoint or catalogue refuses before the first model request. MCP calls still pass through this run's ordinary approver, hooks and budget. |
--mcp-registry <FILE> | no | none | Override the shared b10x MCP registry used by --mcp-profile. Absent uses the same XDG registry as the standalone b10x-mcp command. This is a path to non-secret connection declarations; credentials remain in their explicit sources or the shared owner-only OAuth store. |
--instructions-file <INSTRUCTIONS_FILE> | no | none | File holding the standing instruction. Defaults to the built-in one |
--session-dir <PATH> | no | none | Where this run's conversation is written, so a later one can resume it. Defaults to $XDG_STATE_HOME/b10x-harness/sessions, or $HOME/.local/state/… — never the workspace, because a transcript carries whatever the model read and a file beside the code is one git add -A from being committed. |
--resume <ID> | no | none | Continue a session: its identifier, or latest. The stored conversation is replayed into the run before this run's input, opaque reasoning items included, so the model keeps what it already worked out instead of paying to think it again. A session recorded on another wire is refused by name — an opaque item may not cross wires (AGENTS.md invariant 5), and saying so here is cheaper than the far end saying it. |
--no-session | no | false | Write no session file at all. For an evaluation arm that must leave nothing on the machine it ran on: a transcript is evidence about a run, and an arm whose runs must be identically reproducible from their flags cannot have one of them silently pick up the previous one's state. Nothing is resumable afterwards, which is the trade. |
--no-project-instructions | no | false | Leave the project's own AGENTS.md or CLAUDE.md out of the standing instruction. An experiment control, and only that: the environment block — workspace, OS, date, git branch — is always there, because a run that does not know where it is spends a turn finding out. What this removes is the project's own words, so a run can be measured against the toolset rather than against the instructions a repository happens to carry. |
--max-turns <MAX_TURNS> | no | none | Ceiling on model turns |
--max-output-tokens <MAX_OUTPUT_TOKENS> | no | none | Ceiling on total reported output tokens |
--max-output-tokens-per-turn <MAX_OUTPUT_TOKENS_PER_TURN> | no | none | Ceiling on output tokens offered for any single turn |
--max-duration-ms <MAX_DURATION_MS> | no | none | Wall-clock ceiling in milliseconds. Checked between turns and between the calls of one turn, and what is left is the bound on every run the tools start |
--write-scope <GLOB=SCOPE> | no | none | Where this run may write, as <glob>=<allowed|partial-only|denied>. Repeatable, ordered. First match wins; a path no rule mentions is allowed, because this declares where writing is restricted and a scope nobody wrote restricts nothing. partial-only is the one worth knowing: the path may be changed in part and never replaced whole. It is what a store whose frontmatter is owned by a CLI needs, and it is a distinction no list of operations can make — file_write and file_edit are both writes. Refused by the tool, per call, with a reason naming the way in. This loop has no decision seam and never grows one: the published toolset is the policy. |
--scope-announce <MODE> | no | stated | Whether the declared scope is also stated in the instruction. silent is an experiment control, and it exists because the two are different claims. A run told the rule and a run refused the rule both end with the rule kept, and only the second shows that the toolset is what kept it. Stating it is cheaper — the model spends no call being refused — so a real run states it. One of stated, silent. |
--context <FILE> | no | none | A file the run is given before it starts, instead of discovering it. Repeatable. Costs input tokens on every turn, because a stateless loop replays its conversation. It is still usually a saving: what it replaces is a read, a turn, and a result that joins the same replay. A file that is not there refuses the run — one given a smaller context than it was told to have is a run nobody can reproduce. |
--skills-dir <DIR> | no | none | A directory of skills the run may load by name. Repeatable. <DIR>/<name>/SKILL.md, YAML frontmatter with name and description, the document after. That is the on-disk shape Claude Code writes, read here so a plugin written for it runs unchanged and a comparison between the two harnesses is a comparison of harnesses. Reading a vendor's file format is not becoming a client of a vendor protocol: nothing on this path speaks to a server or grants remote authority. Outbound MCP is a separate, explicitly reviewed profile at the shell boundary. The descriptions are given to the model and the bodies are not. A skill costs a skill call when the model decides it wants one, rather than input tokens on every turn of every run — which is what --context does, deliberately, for the files a run genuinely needs throughout. A document this build cannot read refuses the run by name rather than being skipped: a skill half-read is a rule its author wrote that the run would not apply. |
--memory-dir <DIR> | no | none | A directory of memories this run may recall. Repeatable. <DIR>/<id>.md, YAML frontmatter with kind, summary, trust, status and an optional supersedes, the body after. The id is the file's own name. Handed in, never discovered. There is no default vault, no $XDG location and no walk up the tree beside the workspace: a run given memories nobody named is a run nobody can reproduce or compare, and an ambient memory file is exactly the hermeticity hazard that kept this harness from having memory at all. This flag is the whole of how one arrives. The summaries are given to the model and the bodies are not, as --skills-dir does, and for one more reason: a vault grows with every run the operator ever made, so bodies in the instruction would bill the whole history of the project on every turn. Every record is unreviewed. trust has one legal value and a record claiming another refuses the run. Governed truth is promoted out of a vault into an artifact with its own review, never marked trusted in place. A record this build cannot read refuses the run by name rather than being skipped: a vault silently missing the record that mattered reads to the model exactly like a complete one. This run cannot write one. There is no memory-writing tool, here or anywhere in this binary; that is a separate change with its own gate. |
--agents-dir <DIR> | no | none | A directory of named agents a delegate may be run as. Repeatable. <DIR>/<name>.md, YAML frontmatter with name, description and an optional tools list, the body after as the agent's own standing instruction. The vendor's shape, read here for the same reason --skills-dir reads theirs. A declared toolset can only narrow this run, never widen it. The names are mapped to this harness's own and then intersected with what this run was admitted, so an agent naming a tool the machine did not give the parent does not get it — and the child's record says by name what it asked for and did not get, rather than going silent. A vendor tool name this build does not map refuses the document: a permission its author granted and this build quietly dropped is one the run would not have, with nothing saying so. Only where --delegate published a delegate to run them with. |
--plugin-dir <DIR> | no | none | A plugin directory, whose skills/ and agents/ are read as the two flags above read them. Exactly equivalent to --skills-dir <DIR>/skills and --agents-dir <DIR>/agents, with each name qualified <plugin>:<name> from the plugin's own manifest — the vendor's namespacing, and not cosmetic: two plugins may both ship a planning, and a bare name is also not what an expectation comparing two harnesses names. Named separately so this and the vendor arm of a comparison take the same flag with the same argument. Repeatable, and composes with both. |
--prices <PRICES> | no | none | A rate card, so the run reports what it cost. A JSON document naming its own source and as_of date and holding input_usd_per_mtok, cached_input_usd_per_mtok and output_usd_per_mtok per model. Declared rather than compiled in: a table baked into this binary would be a set of numbers nobody could date, and wrong silently the first time a rate moved. Without one the run reports tokens and no price — which is what it has always done, and is also why a b10x record could not be compared with a Claude Code record on cost. A model the card does not list is warned about by name rather than reported as free. |
--max-cost-microunits <MAX_COST_MICROUNITS> | no | none | Total spend ceiling for the run, in millionths of a US dollar. Needs --prices. Checked after each turn, because that is when the provider reports what it charged for. Refused rather than ignored when the run cannot price itself. |
--temperature <TEMPERATURE> | no | none | Sampling temperature. Left out entirely when unset, so the endpoint's own default stands |
--top-p <TOP_P> | no | none | Nucleus sampling mass. Left out entirely when unset |
--reasoning-effort <REASONING_EFFORT> | no | none | Reasoning effort, for an endpoint that reads one. An endpoint that fixes effort when it starts will ignore this; the route registry is what knows which ones do |
--yes | no | false | Approve every call that asks for a decision. The same as --approve all. What asks is the loop's answer, taken per call from the catalogue entry's declared risk against a ceiling that defaults to low: every write and every run asks. Kept as its own spelling because it is what every existing unattended invocation says, and it wins over --approve when both are given — except --approve caller, beside which the run is refused: one of the two would otherwise be silently ignored. |
--approve <MODE> | no | auto | Who decides a call above the ceiling: auto, prompt, deny, all or caller. auto — the default — asks a person over /dev/tty when there is a terminal to ask on and stdin and stderr are one, and otherwise denies, saying so in one line before the run rather than leaving it to be discovered from a refusal. prompt asks or refuses the run by name. deny is the library default, DenyAll. all is the declared unattended run. caller hands each decision to the process that spawned this run (atlas ADR 0104): the approval-required event on stdout asks, and one JSON line on stdin answers. This chooses the command line's approver. The library's default is DenyAll whatever this says, and a harness that approved by default would turn a review gate into decoration (AGENTS.md invariant 12). One of auto, prompt, deny, all, caller. |
--approve-up-to <RISK> | no | none | Run calls at or below this risk without asking. Default low. The ceiling the loop judges each call's declared risk against. A file_write and a file_edit are medium and a run is high, so at high all three run unasked and only a destructive call asks. Above the ceiling the approver decides, and with --approve deny that is a refusal the model is told about. --yes approves everything and makes this moot, so the two do not combine. One of low, medium, high, destructive. |
--delegate | no | false | Let the model hand a self-contained sub-task to a fresh context on the same gate. Off by default: a new tool is a change in what the model can do. The delegate sees this run's standing instruction and its task and not this conversation, gets the same tool port, the same approver and the same hooks, and spends the run's remaining budget — so it widens nothing and costs the parent one tool result instead of forty reads. It cannot delegate in turn. |
--delegate-turns <N> | no | 20 | Model turns one delegate may take before it reports what it got to. Its own ceiling and not the parent's remainder, so a child that loops does not spend the run's remaining turns finding out. At least one, refused here rather than in the child. The parent's own --max-turns 0 is refused before the first request, and a bound that admits no turn has to be refused the same way wherever it is written; left to the delegate's own Budget::validate a typed zero becomes a failed tool result on every delegation, each one after a paid parent turn had already asked for one. |
--delegate-parallel <N> | no | 4 | How many delegates of one turn may run at the same time. A turn that asks for three sub-tasks paid three whole child runs of latency back to back, and nothing about them required it: a delegate starts from an empty conversation, so none of them can read what another produced. 1 runs them one after another, which is what this harness did before this flag existed. A ceiling and not a promise. Delegates run side by side only where the run's model and tool ports will hand out a second handle on themselves, the remaining token budget divides between the children, no hook observes call order, and every reachable entry is non-mutating and needs no approval. Where any condition does not hold, the same delegates run in order and nothing else about the run changes. At least one, refused here for the reason --delegate-turns is: a bound that admits nothing is refused where it is written. |
--hooks <FILE> | no | none | A file declaring the operator's own programs to run at each call and at the end. Named here and never discovered: a hook found in the workspace would be a program the repository runs on this machine, which is the ambient fallback this harness refuses for credentials. A hook can only narrow — before-call fires after the approver said yes and its block is one more refusal — and it is spawned as an argv, never through a shell. It is otherwise unconfined: the operator's own program, with the environment this run was started in — minus the variable --api-key-env or --oauth-token-env named. A hook is trusted to act; it is not handed the key this run authenticates with. |
--json | no | false | Emit one JSON event per line on stdout instead of prose |
--quiet | no | false | Keep progress off stderr. Warnings are still reported |
--input <INPUT> | yes | none | The request |
--output-schema <FILE> | no | none | A JSON Schema for an object, published as a tool the model calls to finish. Standard output is then the answer and nothing else — one line of compact JSON — so the command composes with anything that reads JSON; the model's prose goes to stderr as progress. A run that ends in prose anyway is nudged once and then stops Unstructured, exiting 2: prose on stdout with a success status is the silent failure this harness refuses to produce. Under --json stdout is the event record instead and there is no bare answer line at all: the answer is the last answered event before a finished whose stop is completed. Last and not first, because a stop hook may withdraw an ending — the loop clears the structured answer and turns again — so an earlier answered can be followed by a second one, or by a finished that is unstructured. A driver taking the first would take the value the operator's hook refused. run only. A conversation has no single end, so there is nothing for chat to shape. |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness chat
Ask one question after another over the same session, reading a line at a time. The same flags as run without --input: each line of standard input is a follow-up turn on one conversation, and the session is written after every one of them. exit, or the end of the input, ends it. There is no line editing and no history — a shell already has both, and a harness that reimplemented them would own a terminal library forever.
Usage: b10x-harness chat [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--profile <NAME> | no | none | Profiles to apply, in order. Later wins a contested key; a typed flag beats them all. Read from $XDG_CONFIG_HOME/b10x/harness.toml. A profile decides what a run may do, so b10x-harness profiles explain -p <name> prints the argv it expands to before a token is spent, and session.started names every profile that contributed with a digest of what it said. That record is the condition on which a file is allowed to carry a permission at all. A trusted workspace's own .b10x/harness.toml (profiles trust) sits between [default] and the first -p, and may set only provider, model and max-turns. |
--base-url <BASE_URL> | no | none | Endpoint origin plus API prefix, for example https://llmgw.example/v1. Optional only because a provider can supply it — [default] provider = "claude". A run with neither is refused by name rather than aimed at a default endpoint nobody chose. |
--model <MODEL> | no | none | Exact model identifier the endpoint serves. As --base-url: a provider may supply it |
--context-window <CONTEXT_WINDOW> | no | 128000 | Context window the endpoint serves for that model. It bounds the request the wire will build, and it is also what makes compaction token-aware: the loop compacts at 80% of this figure — measured by the provider's own last reported input count where there is one — and frees down to 50%, instead of the fixed 192 KiB byte rule that left roughly 60% of a 128k window unused. |
--api-key-file <API_KEY_FILE> | no | none | File holding the bearer credential. Its contents are trimmed and never logged |
--api-key-env <API_KEY_ENV> | no | none | Name of an environment variable holding the bearer credential. Naming it is deliberate: the harness reads no credential it was not pointed at |
--api-key-secret <NAMESPACE/NAME> | no | none | The bearer credential's place in the local secrets store, as <namespace>/<name>. The layout secretsctl put <name> --namespace <namespace> writes, read again on every call, so a key rewritten in the store is the one the next call sends. The store is opened only when this is given: without it this harness never reads the store, and a provider default never stands in for it. |
--oauth-token-file <OAUTH_TOKEN_FILE> | no | none | File holding a subscription OAuth token, for a route that takes one instead of an API key. A path typed here is re-read on every call and is never renewed or written by Harness. A selected built-in provider may instead supply a documented default path; providers show prints it before a run. The codex provider may renew and atomically rewrite only its own default source before the first request, and records that write even under --quiet. |
--oauth-token-env <OAUTH_TOKEN_ENV> | no | none | Name of an environment variable holding a subscription OAuth token |
--oauth-token-pointer <POINTER> | no | none | JSON pointer to the token inside the named source, when that source is a JSON document. Absent means the whole source is the token. Named rather than known: which field a given credential store puts its access token in is that store's business, and a built-in path would silently read the wrong field the day it changed. |
--wire <WIRE> | no | none | Which model API to speak. Both reach --base-url; they are different endpoints under it One of openai-responses, anthropic-messages. |
--workspace <WORKSPACE> | no | . | Directory the read-only workspace tools may see |
--substrate <SUBSTRATE> | no | none | The substrate daemon's socket, so a run may write and execute inside a confined workspace. Without it the toolset is read-only, which is what this harness has always published. With it, what appears is what the daemon says the machine can confine — a host with no delegated cgroup root serves workspaces and no execution, and the run then has the write tools and no run tool. Named rather than discovered, for the reason the credential is: a harness that picked up a confinement boundary from the environment is one whose effects depend on where it happened to be started. |
--substrate-embedded | no | false | Hold substrate's driver in this process instead, confining --workspace itself. The same confinement — guarded IO, openat2 containment, cgroups and namespaces around an exec are the driver's, and they are here. What a socket adds and this does not is an authenticated subject derived from kernel peer credentials; embedded there is no peer. Right for a run on the operator's own machine, wrong for anything multi-tenant. The workspace is adopted, not created: --workspace is the tree, its parent becomes substrate's root, and reads and writes land in the same place. Its directory name is the workspace's own id, so it must be one path component of ASCII alphanumerics, _ and -, and may not be ., .. or begin with -; a name that is not refuses the run by name rather than quietly writing somewhere else. ASCII, and that is the word to read twice: café, Projekt-Übung and 日本語 are ordinary directory names made of letters, and this binary refuses all three. Point this at a project you already have — no prefix on the name, and no scratch copy of the tree. |
--substrate-adopt <ROOT_NAME> | no | none | Adopt this directory under the socket daemon's workspace root, and act in it. The name is one directory under the root the daemon serves (substrate's root-name set: ASCII alphanumerics, _ and -, not beginning with -), not a path on this host. The daemon mints a workspace id for it, one stderr line names that id, and a later run passes it with --workspace-id instead — adopting the same name twice is refused, and this binary cannot list workspaces to find the earlier id. A refused adopt refuses the run. |
--execution-path <PATH> | no | direct | How this native loop was launched: directly, or as a metaharness comparison arm. This is context, not permission. Metaharness sets it explicitly when it drives this binary; no environment variable may silently change the answer. One of direct, metaharness. |
--workspace-id <WORKSPACE_ID> | no | default | Which confined workspace the write and execute tools act in. Ignored under --substrate-embedded, which opens one and names it: the driver mints the identity there, and a caller-supplied name would have to satisfy a rule only the driver knows. Under --substrate-adopt the daemon mints it, so the two are not given together. |
--cgroup-root <CGROUP_ROOT> | no | none | A delegated cgroup subtree, so the embedded driver may confine a process. Without one substrate's probe reports no exec facts and no run tool is published — which is correct and is also why a test-first task cannot be attempted: a run that may not execute its suite cannot see a test fail before writing the code, so it will not write the code. The subtree must be delegated to this user, hold cpu, memory and pids, and be free of processes. |
--toolchain <NAME> | no | none | Admit one or more declarative toolchain providers read-only inside the confined workspace. Without one a confined run can execute anything whose implementation lives under /usr — an interpreter — and nothing whose compilers and package registry live in the operator's home, which is every build tool. The directories are mounted read-only and reported in the run's observation (substrate ADR 0010); the network stays unshared, so this brings a closure in and is not a way to reach out. Built-ins include Rust, Go, Taskfile, npm and Yarn. Use auto for project-marker discovery or a comma-separated provider list for an explicit selection. Declared rather than implied, because it is the one place a confined run is given something substrate did not verify: there is no digest over a package registry. A run that does not need a toolchain should not name one. |
--toolchain-spec <FILE> | no | none | Explicit operator-authored toolchain provider definitions. Repeatable and never discovered |
--driver <PATH> | no | none | A program on this host, staged and admitted read-only so a confined run can execute it. Allow-listing a program by absolute path admits its name, not its bytes. The sandbox reaches /usr, /bin, /lib, /lib64 and the workspace; a path outside those is not there, so the exec dies at ENOENT and the model reads that as the command is wrong. Naming it here links exactly that one file into a private directory and mounts it read-only at /toolchain/driver, and adds the mounted path to the allow-list, so declaring it once is the whole declaration. Read-only on purpose: a run that could rewrite the program recording its own evidence has no evidence. The digest of what was staged is reported by tools. |
--allow-program <ALLOW_PROGRAM> | no | none | A program run may start. Repeatable, and an empty set publishes no run at all. The declaration is checked against argv[0], the root executable Harness starts. Programs that executable starts are not separately matched; they remain inside the same sandbox, process limits, no-network namespace, workspace boundary and whole-tree timeout. Declared rather than open, because an argv whose program could be anything is a shell with extra steps. A set nobody named means nobody wanted one. |
--process-write-subtree <DIR> | no | none | Workspace-relative directory a confined run process may change. Repeatable. With no declaration, process execution sees the workspace read-only. File tools keep using --write-scope; the two are separate because an arbitrary glob cannot be converted to an exact writable mount without widening it. Name every build-output directory a command needs. |
--surface <SURFACE> | no | flat | How the catalogue reaches the model: every entry as its own tool, or three verbs over it. flat by default. The three-verb surface cost 33–44% of every tool call on discovery across three measured runs and hands the provider an untyped argument object it cannot validate; the neutral names it existed to protect are the entry names, which flat publishes directly. verbs is still fully served — metaharness offers it over MCP — and is what an arm comparing the two surfaces asks for. One of flat, verbs. |
--mcp-profile <FILE> | no | none | Reviewed MCP publication profile. Repeatable; every profile freezes one connection. Discovery alone grants nothing. Each file pins the shared registry digest and the exact tools/list snapshot, then assigns Harness-owned names, descriptions and envelopes to the subset this run may publish. A changed endpoint or catalogue refuses before the first model request. MCP calls still pass through this run's ordinary approver, hooks and budget. |
--mcp-registry <FILE> | no | none | Override the shared b10x MCP registry used by --mcp-profile. Absent uses the same XDG registry as the standalone b10x-mcp command. This is a path to non-secret connection declarations; credentials remain in their explicit sources or the shared owner-only OAuth store. |
--instructions-file <INSTRUCTIONS_FILE> | no | none | File holding the standing instruction. Defaults to the built-in one |
--session-dir <PATH> | no | none | Where this run's conversation is written, so a later one can resume it. Defaults to $XDG_STATE_HOME/b10x-harness/sessions, or $HOME/.local/state/… — never the workspace, because a transcript carries whatever the model read and a file beside the code is one git add -A from being committed. |
--resume <ID> | no | none | Continue a session: its identifier, or latest. The stored conversation is replayed into the run before this run's input, opaque reasoning items included, so the model keeps what it already worked out instead of paying to think it again. A session recorded on another wire is refused by name — an opaque item may not cross wires (AGENTS.md invariant 5), and saying so here is cheaper than the far end saying it. |
--no-session | no | false | Write no session file at all. For an evaluation arm that must leave nothing on the machine it ran on: a transcript is evidence about a run, and an arm whose runs must be identically reproducible from their flags cannot have one of them silently pick up the previous one's state. Nothing is resumable afterwards, which is the trade. |
--no-project-instructions | no | false | Leave the project's own AGENTS.md or CLAUDE.md out of the standing instruction. An experiment control, and only that: the environment block — workspace, OS, date, git branch — is always there, because a run that does not know where it is spends a turn finding out. What this removes is the project's own words, so a run can be measured against the toolset rather than against the instructions a repository happens to carry. |
--max-turns <MAX_TURNS> | no | none | Ceiling on model turns |
--max-output-tokens <MAX_OUTPUT_TOKENS> | no | none | Ceiling on total reported output tokens |
--max-output-tokens-per-turn <MAX_OUTPUT_TOKENS_PER_TURN> | no | none | Ceiling on output tokens offered for any single turn |
--max-duration-ms <MAX_DURATION_MS> | no | none | Wall-clock ceiling in milliseconds. Checked between turns and between the calls of one turn, and what is left is the bound on every run the tools start |
--write-scope <GLOB=SCOPE> | no | none | Where this run may write, as <glob>=<allowed|partial-only|denied>. Repeatable, ordered. First match wins; a path no rule mentions is allowed, because this declares where writing is restricted and a scope nobody wrote restricts nothing. partial-only is the one worth knowing: the path may be changed in part and never replaced whole. It is what a store whose frontmatter is owned by a CLI needs, and it is a distinction no list of operations can make — file_write and file_edit are both writes. Refused by the tool, per call, with a reason naming the way in. This loop has no decision seam and never grows one: the published toolset is the policy. |
--scope-announce <MODE> | no | stated | Whether the declared scope is also stated in the instruction. silent is an experiment control, and it exists because the two are different claims. A run told the rule and a run refused the rule both end with the rule kept, and only the second shows that the toolset is what kept it. Stating it is cheaper — the model spends no call being refused — so a real run states it. One of stated, silent. |
--context <FILE> | no | none | A file the run is given before it starts, instead of discovering it. Repeatable. Costs input tokens on every turn, because a stateless loop replays its conversation. It is still usually a saving: what it replaces is a read, a turn, and a result that joins the same replay. A file that is not there refuses the run — one given a smaller context than it was told to have is a run nobody can reproduce. |
--skills-dir <DIR> | no | none | A directory of skills the run may load by name. Repeatable. <DIR>/<name>/SKILL.md, YAML frontmatter with name and description, the document after. That is the on-disk shape Claude Code writes, read here so a plugin written for it runs unchanged and a comparison between the two harnesses is a comparison of harnesses. Reading a vendor's file format is not becoming a client of a vendor protocol: nothing on this path speaks to a server or grants remote authority. Outbound MCP is a separate, explicitly reviewed profile at the shell boundary. The descriptions are given to the model and the bodies are not. A skill costs a skill call when the model decides it wants one, rather than input tokens on every turn of every run — which is what --context does, deliberately, for the files a run genuinely needs throughout. A document this build cannot read refuses the run by name rather than being skipped: a skill half-read is a rule its author wrote that the run would not apply. |
--memory-dir <DIR> | no | none | A directory of memories this run may recall. Repeatable. <DIR>/<id>.md, YAML frontmatter with kind, summary, trust, status and an optional supersedes, the body after. The id is the file's own name. Handed in, never discovered. There is no default vault, no $XDG location and no walk up the tree beside the workspace: a run given memories nobody named is a run nobody can reproduce or compare, and an ambient memory file is exactly the hermeticity hazard that kept this harness from having memory at all. This flag is the whole of how one arrives. The summaries are given to the model and the bodies are not, as --skills-dir does, and for one more reason: a vault grows with every run the operator ever made, so bodies in the instruction would bill the whole history of the project on every turn. Every record is unreviewed. trust has one legal value and a record claiming another refuses the run. Governed truth is promoted out of a vault into an artifact with its own review, never marked trusted in place. A record this build cannot read refuses the run by name rather than being skipped: a vault silently missing the record that mattered reads to the model exactly like a complete one. This run cannot write one. There is no memory-writing tool, here or anywhere in this binary; that is a separate change with its own gate. |
--agents-dir <DIR> | no | none | A directory of named agents a delegate may be run as. Repeatable. <DIR>/<name>.md, YAML frontmatter with name, description and an optional tools list, the body after as the agent's own standing instruction. The vendor's shape, read here for the same reason --skills-dir reads theirs. A declared toolset can only narrow this run, never widen it. The names are mapped to this harness's own and then intersected with what this run was admitted, so an agent naming a tool the machine did not give the parent does not get it — and the child's record says by name what it asked for and did not get, rather than going silent. A vendor tool name this build does not map refuses the document: a permission its author granted and this build quietly dropped is one the run would not have, with nothing saying so. Only where --delegate published a delegate to run them with. |
--plugin-dir <DIR> | no | none | A plugin directory, whose skills/ and agents/ are read as the two flags above read them. Exactly equivalent to --skills-dir <DIR>/skills and --agents-dir <DIR>/agents, with each name qualified <plugin>:<name> from the plugin's own manifest — the vendor's namespacing, and not cosmetic: two plugins may both ship a planning, and a bare name is also not what an expectation comparing two harnesses names. Named separately so this and the vendor arm of a comparison take the same flag with the same argument. Repeatable, and composes with both. |
--prices <PRICES> | no | none | A rate card, so the run reports what it cost. A JSON document naming its own source and as_of date and holding input_usd_per_mtok, cached_input_usd_per_mtok and output_usd_per_mtok per model. Declared rather than compiled in: a table baked into this binary would be a set of numbers nobody could date, and wrong silently the first time a rate moved. Without one the run reports tokens and no price — which is what it has always done, and is also why a b10x record could not be compared with a Claude Code record on cost. A model the card does not list is warned about by name rather than reported as free. |
--max-cost-microunits <MAX_COST_MICROUNITS> | no | none | Total spend ceiling for the run, in millionths of a US dollar. Needs --prices. Checked after each turn, because that is when the provider reports what it charged for. Refused rather than ignored when the run cannot price itself. |
--temperature <TEMPERATURE> | no | none | Sampling temperature. Left out entirely when unset, so the endpoint's own default stands |
--top-p <TOP_P> | no | none | Nucleus sampling mass. Left out entirely when unset |
--reasoning-effort <REASONING_EFFORT> | no | none | Reasoning effort, for an endpoint that reads one. An endpoint that fixes effort when it starts will ignore this; the route registry is what knows which ones do |
--yes | no | false | Approve every call that asks for a decision. The same as --approve all. What asks is the loop's answer, taken per call from the catalogue entry's declared risk against a ceiling that defaults to low: every write and every run asks. Kept as its own spelling because it is what every existing unattended invocation says, and it wins over --approve when both are given — except --approve caller, beside which the run is refused: one of the two would otherwise be silently ignored. |
--approve <MODE> | no | auto | Who decides a call above the ceiling: auto, prompt, deny, all or caller. auto — the default — asks a person over /dev/tty when there is a terminal to ask on and stdin and stderr are one, and otherwise denies, saying so in one line before the run rather than leaving it to be discovered from a refusal. prompt asks or refuses the run by name. deny is the library default, DenyAll. all is the declared unattended run. caller hands each decision to the process that spawned this run (atlas ADR 0104): the approval-required event on stdout asks, and one JSON line on stdin answers. This chooses the command line's approver. The library's default is DenyAll whatever this says, and a harness that approved by default would turn a review gate into decoration (AGENTS.md invariant 12). One of auto, prompt, deny, all, caller. |
--approve-up-to <RISK> | no | none | Run calls at or below this risk without asking. Default low. The ceiling the loop judges each call's declared risk against. A file_write and a file_edit are medium and a run is high, so at high all three run unasked and only a destructive call asks. Above the ceiling the approver decides, and with --approve deny that is a refusal the model is told about. --yes approves everything and makes this moot, so the two do not combine. One of low, medium, high, destructive. |
--delegate | no | false | Let the model hand a self-contained sub-task to a fresh context on the same gate. Off by default: a new tool is a change in what the model can do. The delegate sees this run's standing instruction and its task and not this conversation, gets the same tool port, the same approver and the same hooks, and spends the run's remaining budget — so it widens nothing and costs the parent one tool result instead of forty reads. It cannot delegate in turn. |
--delegate-turns <N> | no | 20 | Model turns one delegate may take before it reports what it got to. Its own ceiling and not the parent's remainder, so a child that loops does not spend the run's remaining turns finding out. At least one, refused here rather than in the child. The parent's own --max-turns 0 is refused before the first request, and a bound that admits no turn has to be refused the same way wherever it is written; left to the delegate's own Budget::validate a typed zero becomes a failed tool result on every delegation, each one after a paid parent turn had already asked for one. |
--delegate-parallel <N> | no | 4 | How many delegates of one turn may run at the same time. A turn that asks for three sub-tasks paid three whole child runs of latency back to back, and nothing about them required it: a delegate starts from an empty conversation, so none of them can read what another produced. 1 runs them one after another, which is what this harness did before this flag existed. A ceiling and not a promise. Delegates run side by side only where the run's model and tool ports will hand out a second handle on themselves, the remaining token budget divides between the children, no hook observes call order, and every reachable entry is non-mutating and needs no approval. Where any condition does not hold, the same delegates run in order and nothing else about the run changes. At least one, refused here for the reason --delegate-turns is: a bound that admits nothing is refused where it is written. |
--hooks <FILE> | no | none | A file declaring the operator's own programs to run at each call and at the end. Named here and never discovered: a hook found in the workspace would be a program the repository runs on this machine, which is the ambient fallback this harness refuses for credentials. A hook can only narrow — before-call fires after the approver said yes and its block is one more refusal — and it is spawned as an argv, never through a shell. It is otherwise unconfined: the operator's own program, with the environment this run was started in — minus the variable --api-key-env or --oauth-token-env named. A hook is trusted to act; it is not handed the key this run authenticates with. |
--json | no | false | Emit one JSON event per line on stdout instead of prose |
--quiet | no | false | Keep progress off stderr. Warnings are still reported |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness sessions
List the sessions on this machine, newest first
Usage: b10x-harness sessions [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--session-dir <PATH> | no | none | Where sessions are kept. Defaults to $XDG_STATE_HOME/b10x-harness/sessions |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness tools
Print the tools this harness publishes, without contacting an endpoint
Usage: b10x-harness tools [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--surface <SURFACE> | no | flat | How the catalogue would reach the model: every entry as its own tool, or three verbs. The same flag run takes and the same default, because the question this command answers is what would that run publish — and answering it for a surface the run would not use is how a consumer comes to pin a tool list nothing serves. One of flat, verbs. |
--mcp-profile <FILE> | no | none | Reviewed MCP publication profile. Repeatable; connects and verifies the frozen snapshot |
--mcp-registry <FILE> | no | none | Override the shared b10x MCP registry used by --mcp-profile |
--workspace <WORKSPACE> | no | . | Directory the read-only workspace tools may see |
--substrate <SUBSTRATE> | no | none | The substrate daemon's socket, so a run may write and execute inside a confined workspace. Without it the toolset is read-only, which is what this harness has always published. With it, what appears is what the daemon says the machine can confine — a host with no delegated cgroup root serves workspaces and no execution, and the run then has the write tools and no run tool. Named rather than discovered, for the reason the credential is: a harness that picked up a confinement boundary from the environment is one whose effects depend on where it happened to be started. |
--substrate-embedded | no | false | Hold substrate's driver in this process instead, confining --workspace itself. The same confinement — guarded IO, openat2 containment, cgroups and namespaces around an exec are the driver's, and they are here. What a socket adds and this does not is an authenticated subject derived from kernel peer credentials; embedded there is no peer. Right for a run on the operator's own machine, wrong for anything multi-tenant. The workspace is adopted, not created: --workspace is the tree, its parent becomes substrate's root, and reads and writes land in the same place. Its directory name is the workspace's own id, so it must be one path component of ASCII alphanumerics, _ and -, and may not be ., .. or begin with -; a name that is not refuses the run by name rather than quietly writing somewhere else. ASCII, and that is the word to read twice: café, Projekt-Übung and 日本語 are ordinary directory names made of letters, and this binary refuses all three. Point this at a project you already have — no prefix on the name, and no scratch copy of the tree. |
--substrate-adopt <ROOT_NAME> | no | none | Adopt this directory under the socket daemon's workspace root, and list the tools a run in it would have. Listing adopts. The daemon mints the workspace id here, as it would for a run, and one stderr line names it. A later run or tools with the same --substrate-adopt is then refused as already adopted: pass the printed id with --workspace-id instead, because this binary cannot list workspaces to find it again. The name is one directory under the root the daemon serves (substrate's root-name set: ASCII alphanumerics, _ and -, not beginning with -), not a path on this host. A refused adopt refuses the listing. |
--workspace-id <WORKSPACE_ID> | no | default | Which confined workspace the write and execute tools act in. Ignored under --substrate-embedded, which opens one and names it: the driver mints the identity there, and a caller-supplied name would have to satisfy a rule only the driver knows. Under --substrate-adopt the daemon mints it, so the two are not given together. |
--cgroup-root <CGROUP_ROOT> | no | none | A delegated cgroup subtree, so the embedded driver may confine a process. Without one substrate's probe reports no exec facts and no run tool is published — which is correct and is also why a test-first task cannot be attempted: a run that may not execute its suite cannot see a test fail before writing the code, so it will not write the code. The subtree must be delegated to this user, hold cpu, memory and pids, and be free of processes. |
--allow-program <ALLOW_PROGRAM> | no | none | A program run may start. Repeatable, and an empty set publishes no run at all. The declaration is checked against argv[0], the root executable Harness starts. Programs that executable starts are not separately matched; they remain inside the same sandbox, process limits, no-network namespace, workspace boundary and whole-tree timeout. Declared rather than open, because an argv whose program could be anything is a shell with extra steps. A set nobody named means nobody wanted one. |
--process-write-subtree <DIR> | no | none | Workspace-relative directory a confined run process may change. Repeatable |
--write-scope <GLOB=SCOPE> | no | none | Where a run may write, as <glob>=<allowed|partial-only|denied>, so tools answers with it. The same declaration run takes. It belongs in this answer because "what can this run do?" is not answered by a list of tools alone once some paths refuse some of them. |
--toolchain <NAME> | no | none | Admit a build toolchain read-only, so tools describes the run a run would get |
--toolchain-spec <FILE> | no | none | Explicit operator-authored toolchain provider definitions. Repeatable and never discovered |
--driver <PATH> | no | none | A program on this host, staged and admitted read-only so a confined run can execute it. Allow-listing a program by absolute path admits its name, not its bytes. The sandbox reaches /usr, /bin, /lib, /lib64 and the workspace; a path outside those is not there, so the exec dies at ENOENT and the model reads that as the command is wrong. Naming it here links exactly that one file into a private directory and mounts it read-only at /toolchain/driver, and adds the mounted path to the allow-list, so declaring it once is the whole declaration. Read-only on purpose: a run that could rewrite the program recording its own evidence has no evidence. The digest of what was staged is reported by tools. |
--skills-dir <DIR> | no | none | The skills a run would be offered, so tools answers with them. The same declaration run takes. It belongs in this answer for the reason --write-scope does: "what can this run do?" is not answered by a list of tools alone once some of what the model is given arrives from somewhere else. |
--memory-dir <DIR> | no | none | The memories a run would be offered, so tools answers with them. The same declaration run takes, here for the reason --skills-dir is: "what can this run do?" is not answered by a list of tools alone once some of what the model is given arrives from somewhere else. |
--agents-dir <DIR> | no | none | The named agents a run would be offered, so tools answers with them |
--plugin-dir <DIR> | no | none | A plugin directory, whose skills/ and agents/ are read as the two flags above read them |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness profiles
Read the profiles this machine is configured with, without running one. A profile decides what a run may do, so it has to be readable before it does it — that is the condition on which one is allowed to carry a permission at all. explain is the verb that matters: it prints the argv a -p expands to, and who set each key, for nothing.
Usage: b10x-harness profiles <COMMAND>
b10x-harness profiles list
Every profile, and the file it came from
Usage: b10x-harness profiles list
b10x-harness profiles show
One profile's effective table
Usage: b10x-harness profiles show <NAME>
| Argument | Required | Default | Meaning |
|---|---|---|---|
<NAME> | yes | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness profiles explain
What a -p expands to, and who set each key
Usage: b10x-harness profiles explain [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--profile <NAME> | no | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness profiles init
Write a starter config, and print where it went
Usage: b10x-harness profiles init
b10x-harness profiles trust
Let this repository's own .b10x/harness.toml apply to runs over it. Recorded by canonical path in harness-trust.toml, beside the operator's config; that config itself is never edited. A repository profile may set only provider, model and max-turns. Trust is per path: another checkout, a parent or a subdirectory is not trusted by this.
Usage: b10x-harness profiles trust <PATH>
| Argument | Required | Default | Meaning |
|---|---|---|---|
<PATH> | yes | none | The workspace directory. It must exist, and is recorded by its canonical path |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness profiles untrust
Stop applying a repository's own .b10x/harness.toml
Usage: b10x-harness profiles untrust <PATH>
| Argument | Required | Default | Meaning |
|---|---|---|---|
<PATH> | yes | none | The workspace directory, canonicalised; one that no longer exists is made absolute |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness providers
Read the providers this build ships, and any the config overrides
Usage: b10x-harness providers <COMMAND>
b10x-harness providers list
Every provider this build ships, and which the config overrides
Usage: b10x-harness providers list
b10x-harness providers show
One provider's effective endpoint, wire, model and credential
Usage: b10x-harness providers show <NAME>
| Argument | Required | Default | Meaning |
|---|---|---|---|
<NAME> | yes | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness toolchains
Inspect and validate declarative toolchain providers
Usage: b10x-harness toolchains <COMMAND>
b10x-harness toolchains list
List built-ins and providers from explicitly named specification files
Usage: b10x-harness toolchains list [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--toolchain-spec <FILE> | no | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness toolchains show
Show one validated provider definition and its body-free provenance
Usage: b10x-harness toolchains show [OPTIONS] <NAME>
| Argument | Required | Default | Meaning |
|---|---|---|---|
<NAME> | yes | none | |
--toolchain-spec <FILE> | no | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness toolchains validate
Validate one or more custom specification files against built-ins and each other
Usage: b10x-harness toolchains validate <FILE>...
| Argument | Required | Default | Meaning |
|---|---|---|---|
<FILE> | yes | none |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness context
Inspect attributable instruction layers without contacting a model
Usage: b10x-harness context <COMMAND>
b10x-harness context show
Usage: b10x-harness context show [OPTIONS]
| Argument | Required | Default | Meaning |
|---|---|---|---|
--workspace <WORKSPACE> | no | . | |
--instructions-file <INSTRUCTIONS_FILE> | no | none | |
--context <FILE> | no | none | |
--no-project-instructions | no | false | |
--toolchain <NAME> | no | none | |
--toolchain-spec <FILE> | no | none | |
--json | no | false | Print the body-free manifest as JSON |
--body | no | false | Print the exact rendered instruction body sent to a model |
More columns: swipe horizontally, or focus the table and use the arrow keys.
b10x-harness app-server
Serve one connection over the pinned Codex app-server JSON-RPC format, on stdio. Tools arrive from the client on thread/start; the workspace toolset is not published here. The endpoint and credential stay outside the protocol.
Usage: b10x-harness app-server [OPTIONS] --base-url <BASE_URL> --model <MODEL>
| Argument | Required | Default | Meaning |
|---|---|---|---|
--base-url <BASE_URL> | yes | none | Endpoint origin plus API prefix, for example https://llmgw.example/v1 |
--model <MODEL> | yes | none | Exact model identifier the endpoint serves |
--context-window <CONTEXT_WINDOW> | no | 128000 | Context window the endpoint serves for that model |
--api-key-file <API_KEY_FILE> | no | none | File holding the bearer credential. Its contents are trimmed and never logged |
--api-key-env <API_KEY_ENV> | no | none | Name of an environment variable holding the bearer credential |
--api-key-secret <NAMESPACE/NAME> | no | none | The bearer credential's place in the local secrets store, as <namespace>/<name>. The layout secretsctl put <name> --namespace <namespace> writes, read again on every call. The store is opened only when this is given; nothing else here reads it. |
--oauth-token-file <OAUTH_TOKEN_FILE> | no | none | File holding a subscription OAuth token, for a route that takes one instead of an API key. Named, like every other credential source here: there is no default path and no vendor directory this looks in. The file is re-read on every call, so a token an owner outside this process renews is picked up without restarting the run — nothing here renews one. |
--oauth-token-env <OAUTH_TOKEN_ENV> | no | none | Name of an environment variable holding a subscription OAuth token |
--oauth-token-pointer <POINTER> | no | none | JSON pointer to the token inside the named source, when that source is a JSON document. Absent means the whole source is the token. Named rather than known: which field a given credential store puts its access token in is that store's business, and a built-in path would silently read the wrong field the day it changed. |
--wire <WIRE> | no | openai-responses | Which model API to speak. Both reach --base-url; they are different endpoints under it One of openai-responses, anthropic-messages. |
--max-turns <MAX_TURNS> | no | none | Ceiling on model turns, applied to every turn on the connection |
--max-output-tokens <MAX_OUTPUT_TOKENS> | no | none | Ceiling on total reported output tokens per turn |
More columns: swipe horizontally, or focus the table and use the arrow keys.