CLI reference

Every flag on every simian subcommand.

simian is a single binary with cobra subcommands. This page is generated from simian <cmd> --help output. There is a second binary, simian-eval, covered at the end: it drives a scenario pack against a subject and is deliberately not part of the operator binary.

To get the most up-to-date reference for any single command, run it with --help:

simian --help
simian serve --help
simian chaos --help
simian sut deploy --help

Subcommand index

SubcommandPurpose
simian arenaManage chaos arena namespaces (create/destroy/describe). The arena is the namespace+RBAC unit of isolation for chaos.
simian sutManage Systems Under Test (deploy/destroy/list). Built-in SUT: Online Boutique.
simian serveRun the controller: Fault Executor + MCP server + autonomous loop.
simian webServe the web UI alone, with no controller behind it, for watching several Simians from one page. No Kubernetes, no LLM.
simian chaosSubmit a fault either as plain-text intent (LLM-translated) or as a hand-built FaultManifest (deterministic-control). Also list/clear active faults.
simian planGenerate an AttackPlan against a real arena and emit it as JSON. Default --dry-run=true does not apply.
simian audit exportFold the audit trail into one row per fault: what was asked for, what ran on what, and how it ended — or, with --cycles, one row per autonomous cycle: what it decided and why. Reads --audit-file output or kubectl logs.
simian watchLive terminal view of an arena: active faults, what autonomous mode decided (latest hypothesis and plan, each recent cycle’s outcome or skip reason), and recent faults.
simian evaluateScore a finished run offline from its audit log and the subject’s report. Contacts no cluster.

Common flag patterns

Eligibility

--eligible-namespace <ns> (repeatable, simian serve) overrides the default annotation-based lookup. Without it, the controller treats any namespace with simian.chaos/eligible="true" as eligible.

LLM provider

--llm-provider gemini|stub (default gemini); --llm-model <id> overrides the default gemini-2.5-pro. Vertex/ADC and API-key auth are both supported (Vertex preferred for production via Workload Identity).

Executor safety policy

Set on simian serve:

FlagDefaultNotes
--duration-ceiling15mHard cap per fault. The autonomous planner is told it as max_fault_duration, and a plan that exceeds it goes back to the model for correction before anything is applied.
--max-concurrent-faults0 (no cap)Total leased faults across namespaces. Rejected applies surface as executor.rejected with reason safety:budget-exceeded.
--min-cooldown0Per-namespace cooldown between consecutive faults. An apply already in flight against a namespace counts as consecutive.
--permitted-tiersnamespace,nodeBlast-radius tiers this installation permits (namespace|node|external). Repeatable or comma-separated. Unset keeps the default; pass just namespace to keep node-level chaos off the cluster entirely. An unrecognised name stops the controller starting rather than falling back — see Helm values.
--default-efficacy-probestrueAttach Simian’s own efficacy gate to every fault kind that has one. Set false to accept faults that report success without proving they landed.

Autonomous mode

Set on simian serve together:

FlagDefaultNotes
--autonomousfalseEnable the planning loop.
--autonomous-namespace(required when --autonomous)Repeatable. Arena namespace(s) the loop targets.
--cycle-interval5mTime between cycles.
--max-faults-per-cycle3Cap on faults applied per cycle.
--max-severity-per-cyclenamespaceHighest blast tier the loop will apply (namespace|node|external). Validated at startup: an unparseable cap would make the loop skip every step, which is indistinguishable from a planner producing nothing.
--hypothesis-hintemptySoft preference passed to the LLM each cycle. Useful for biasing toward specific engines.

Web UI

Set on simian serve:

FlagDefaultNotes
--uitrueServe the web UI at /ui/, its JSON API and event stream under /api/, and /healthz, on --ui-addr.
--ui-addr:8082The UI’s listen address.
--ui-authnonenone: read-only, for a port-forward. iap: behind Identity-Aware Proxy, checking IAP’s assertion on every request.
--ui-iap-audienceemptyWith --ui-auth=iap: the backend service’s audience, /projects/NUMBER/global/backendServices/ID.
--ui-iap-project-numberemptyWith --ui-auth=iap and no audience: accept any backend service in this project.
--ui-writersemptyWith --ui-auth=iap: who may inject and clear faults — emails or domain:example.com. Repeatable.
--ui-adminsemptyWith --ui-auth=iap: who may also configure, pause and resume autonomous mode, clear all faults and halt. Repeatable.
--ui-nameemptyHow this controller is labelled in a page that watches several Simians; empty shows the host.
--ui-allowed-originsemptyOther pages that may call this controller from the browser with the user’s credentials — another Simian’s UI or the standalone UI — e.g. https://simian.example.com. Repeatable. Empty: same origin only.
--ui-controllersemptyOther Simians this controller’s page offers, as name=https://origin of each controller’s UI (or just the URL; the name is then its host). Repeatable. Each must list this page’s origin in its --ui-allowed-origins. A malformed entry stops the controller starting.

Standalone web UI: simian web

The same page with no controller behind it, for one page that fronts several Simians. It serves /ui/, /healthz and GET /api/controllers; every other /api/ path is 404, so the page asks which Simian to watch.

simian web --addr :8080 \
    --controllers simian-1=https://simian.example.com \
    --controllers simian-2=https://simian-2.example.com
FlagDefaultNotes
--addr:8080Listen address.
--controllersemptySimians the page offers, as name=https://origin of each controller’s UI. Repeatable. Each must list this page’s origin in its --ui-allowed-origins (chart ui.allowedOrigins).
--scorecardsemptyA directory of scorecard documents (simian-eval report output) to serve read-only at /scorecards/: GET /scorecards/ lists the *.json files directly in it, GET /scorecards/<name> returns one. Nothing else in or outside the directory is served. The page offers them in its Scorecard mode.

Envoy SUT injection

FlagWhereDefaultNotes
--no-envoy-faultssimian sut deploytrue (skip)Inverted flag. Set --no-envoy-faults=false to opt INTO injection. Default off because of the gRPC-probe limitation.
--sut-inject-envoy-faultssimian servefalseController-side policy. Set to true to inject Envoy when SUTs are applied via the establish_baseline MCP tool.

See Known limitations for why these default off.

Submitting a fault

simian chaos accepts three input shapes:

# 1. LLM-translated path
simian chaos --intent "kill one paymentservice pod for 30 seconds" --namespace boutique-1

# 2. Deterministic-control path with engine + kind + spec
simian chaos --engine chaos-mesh --kind PodChaos \
             --api-version chaos-mesh.org/v1alpha1 \
             --namespace boutique-1 --workload paymentservice \
             --duration 30s \
             --spec '{"action":"pod-kill","mode":"one","selector":{"labelSelectors":{"app":"paymentservice"}}}'

# 3. Submit a fully-formed manifest
simian chaos --manifest examples/network-latency-manifest.json

Plus the inspection / management subcommands:

simian chaos --list-active     # all leased faults
simian chaos --list-catalog    # catalog the LLM sees (all engines)
simian chaos --clear f-<UID>   # clear before lease expiry

--spec, --spec-file, and --stdin-spec are mutually exclusive — set at most one. The CLI rejects overlapping inputs upfront rather than silently picking one.

Scoring a run

--pack takes a built-in pack name (parity, lookout) or a directory of scenario files.

simian evaluate --pack parity --audit run.log --report agent.json
simian evaluate --pack ./packs/custom --audit - --report agent.json --format json

Two artifacts, joined on the scenario ID the audit sink stamps onto every event: the audit log says which faults landed and when, the report says what the subject found and when. Nothing is executed and no kubeconfig is read, so the same artifacts produce the same scorecard on any machine, any time later.

A scenario whose fault has no passing efficacy record prints as NOT SCORED rather than as a miss — the cluster was never broken, so a zero would mean “nothing to find” while reading as “the agent missed it”. Below --min-efficacy (default 0.8) the scorecard is still printed, then the command exits non-zero: the numbers measure the harness, not the subject. Pass --min-efficacy 0 to report anyway; the warning stays either way.

Exporting the audit trail

simian audit export --since 24h audit.jsonl.1 audit.jsonl
kubectl -n simian-system logs deploy/simian-controller | simian audit export --format json
kubectl -n simian-system exec deploy/simian-controller -- simian audit export /var/lib/simian/audit.jsonl

The controller image is distroless (no shell, no cat), so read the audit file in the pod by running simian audit export there, as in the last line.

One row per fault UID, folded from its events: source, kind, targets, the spec as it reached the cluster (after the safety stage narrowed it), whether the engine confirmed injection and the efficacy verdict, how it ended — refused, driver-failed, cleared, expired, or open if nothing ended it — and, for a Chaos Mesh fault that ran to its deadline, whether its target pods were Ready again within five minutes (RECOVERED; the JSON adds the pods still down and why). A fault can end on time and leave its workload broken; no there is the place to look first. --since takes a duration back from now or an RFC 3339 time and keeps faults first seen after it. --format json writes one object per line with the full spec; the table truncates it. Lines that are not audit events are skipped, so a controller’s whole log can be piped in.

--cycles folds the same records by autonomous-mode cycle instead: one row per cycle and namespace with its outcome (completed, skipped, unfinished when the controller restarted mid-cycle), the skip reason and what the gate, the LLM or the validator said (health-gate: workload Deployment/adservice: 0/1 pods ready), the hypothesis, the plan’s steps, and how many steps were applied and refused. --format json adds each step’s rationale and duration rationale. This is the answer to “what is autonomous mode doing, and why” without reading the controller log.

simian watch --namespace <arena> shows the same live: the latest plan in full and a line per recent cycle above the active and recent faults. It reads the controller’s get_recent_cycles MCP tool, which any MCP client can call too; the controller rebuilds recent cycles from its audit file at start-up.

simian serve --audit-file PATH writes the file (--audit-file-max-bytes, default 100 MiB, sets the rotation point). At start-up it adopts the faults a previous process left running and closes the ones it left open; the chart turns it on by default — see Helm values.

Tearing down

simian sut destroy --namespace boutique-1                # SUT only
simian sut destroy --namespace boutique-1 --with-arena   # both layers

destroy --with-arena refuses if simian-managed faults are still leased; pass --force to override (after clearing them with simian chaos --clear).

simian-eval — running a pack against a subject

A second binary. Cluster lifecycle, subject processes and scoring have no business linking into the operator binary that runs in-cluster with chaos RBAC.

simian-eval --pack parity --subject exec:./bin/lookout --out runs/
simian-eval --pack parity,lookout --subject exec:./bin/agent --concurrency 4
simian-eval --pack ./packs/custom --subject noop: --cluster kind --out runs/floor
simian-eval --pack parity --subject exec:./bin/agent --only parity-crash-loop

Per scenario: provision an arena namespace, inject the faults through the normal executor path — same validation, same safety stages, same leases, same efficacy gates — hand the subject the prompt, collect its report, then clear the chaos and put the namespace back. Two files land in --out: audit.log is Simian’s side, run.json is the subject’s, and they join on the scenario ID. A subject that keeps its own evidence leaves it there too. The scorecard printed at the end comes from reading those two files back, so simian evaluate reproduces it exactly, with or without the cluster.

FlagDefaultNotes
--pack(required)A built-in pack name — parity or lookout, both embedded in the binary — or a directory. Repeatable or comma-separated; several packs run as one suite. A bare built-in name always means the embedded pack, so spell a local directory of the same name ./parity. Two packs sharing a scenario ID is a load error, not a merge.
--subject(required)exec:<command line>, lookout:<path to the lookout binary>, sre-agent:<path to the sre-agent binary>, or noop: for the zero-score floor. The lookout: adapter runs a namespace-scoped health scan and translates the finding stream; sre-agent: runs one assessment and reads the report out of the transcript it names. Both take the namespace from the prompt, as an agent does.
--only(all)Scenario IDs to run. An ID that is not in the pack is an error, not an empty run.
--outruns/<timestamp>Where audit.log and run.json go, and where a subject that keeps its own evidence leaves it — an sre-agent: subject writes one transcript-<namespace>.json per scenario.
--clustercurrentcurrent uses the kubeconfig’s cluster and leaves it standing; kind provisions a throwaway cluster for the run and deletes it afterwards, including on Ctrl-C.
--concurrency1Ceiling, not a target: scenarios sharing a namespace are serialised regardless, and a control takes the cluster to itself.
--subject-timeout10mHow long one investigation may take before the subject is killed and scored as a failure.
--subject-dir, --subject-env—Working directory and extra KEY=VALUE environment for a subprocess subject. An sre-agent: subject takes its provider credentials this way.
--remediation-poll5sHow often to ask whether the fault is gone while the subject works, for time-to-remediate. 0 disables the watch.
--eligible-namespace(annotation)Fence the run to a fixed namespace list instead of reading simian.chaos/eligible. Reach for this when the cluster has other tenants.
--terminating-wait2mHow long to wait for a namespace left over from an earlier run to finish deleting. Namespace deletion is asynchronous, so without this, running the same pack twice in a row fails on the first run’s teardown. Expiry is an InjectError, not a hang — a namespace held by a finalizer is not coming back.
--teardown-timeout2mBound on cleanup, which runs even after Ctrl-C. Most of it goes on waiting for the chaos controller’s finalizers: the arena refuses to be destroyed while chaos is still leased in it, so a teardown attempted before the finalizer finishes is refused and the namespace is left standing. Raise it for faults that are slow to undo — a NetworkChaos partition and a DNSChaos are the two that have needed it.
--keep-arenasfalseLeave arena namespaces standing afterwards, for poking at a scenario that went wrong. Faults are still cleared.
--allow-short-faultsfalsePermit faults shorter than --subject-timeout. Off by default: the lease expires mid-investigation, the reaper clears it, and that disappearance is recorded as the subject having remediated it.
--scoretrueOff writes the artifacts and stops.
--format, --min-efficacytext, 0.8As simian evaluate.

Namespaces the run creates are annotated with simian.chaos/eval-run=<run id> and destroyed at the end. A namespace that already existed is annotated and left standing — a rig that deletes namespaces it merely found is one bad scenario file away from deleting something that mattered.

Each scenario’s eval.scenario_started audit line records the pack, the subject and the cluster (the kubeconfig context, or the kind cluster’s name), so a run directory says what it measured and where.

simian-eval report — combining runs into a scorecard

simian-eval report --run runs/dataplane-lookout --run runs/dataplane-agent --out scorecard.json

Reads run directories, scores each exactly as its own scorecard did, and writes one self-describing JSON document — scenarios by subjects, with each scenario’s fault, ground truth and every subject’s report — that the web UI’s Scorecard mode renders with no server. See Reading an evaluation: the scorecard.

FlagDefaultNotes
--run(required)A run directory holding audit.log and run.json. Repeatable; each is a column.
--outstdoutFile to write the scorecard to.
--pack(as recorded)Packs to score against, by built-in name or directory, matched to each run by the pack name its audit log records.
--floorlookoutSubjects that are the floor (the deterministic detector). Default: subjects named lookout or k8s-lookout, with or without a version suffix. --floor "" for none.
--min-efficacy0.8Efficacy rate below which a run is flagged as measuring the harness, not the subject.

An arena that could not be destroyed is a suite-level failure: simian-eval prints the whole scorecard, then exits non-zero naming the scenarios and the namespaces. The scores themselves are untouched, because a namespace that would not go away says nothing about what the subject answered — but what is left behind carries simian.chaos/eligible, which is a standing permission to inject there, so it is not something to find out about from the next run. The fact is written to the audit log as eval.arena_leaked, so simian evaluate re-scoring the artifacts later reports it too.