CLI Reference
Run sigil --help or sigil <subcommand> --help for up-to-date usage. This page is a tour of the common subcommands.
sigil eval
Section titled “sigil eval”sigil eval <pr-ref> --service <svc> [--control-ref <REF>] [--tag <T>] [--exclude-tag <T>] [--failure-artifact <FILE>] [--holdout-detail] [options]Deploy PR + baseline, run scenarios against both, score, and append eval.complete to the ledger.
-
--holdout-detail— print and write holdout ids, titles, and errors. Since 0.36.0 they are off by default: the stderr summary,--format json, and--outputshow each holdout asholdout_<n>, with no id, title, error text, or skip reason, because CI logs and artifacts are readable by the coding agent. Use the flag only on a trusted terminal.sigil reportshows the stored full report either way. -
--tag/--exclude-tag— run a subset. A filtered eval cannot ALLOW:sigil decideREVIEWs it withpartial_scenario_set. -
--pr-endpoint/--baseline-endpoint— use services you deployed yourself. Nothing binds such endpoints to the artifact, so the eval REVIEWs withendpoint_overrideunless the control config sets[deploy] backend = "external". -
--failure-artifact <FILE>— write a fixed-vocabulary failure phase as JSON on every terminal outcome, including runs that produce no eval report. Lets a caller tell a failed deploy leg from an undecryptable scenario set from an uncovered strict MUST from an internal evaluator error, without parsing stderr. Deploy phases name the leg and the stage, e.g.deploy.baseline.health-wait. The vocabulary is closed, and the artifact carries only that vocabulary plus config field names — never scenario ids, endpoints, rubric text, config values, or raw error strings, so it stays safe where holdout secrecy matters. Fail-closed on both sides: an unrecognized, malformed, or missing artifact never reads as success.$SIGIL_FAILURE_ARTIFACTis honored when the flag is absent. Each structured deployment failure also lists which config fields came from the working checkout, the control snapshot, a CLI flag, or the environment — field names and sources only.
sigil decide
Section titled “sigil decide”sigil decide <pr-ref> --service <svc> [--control-ref <REF>] [--break-glass <REASON>] [--format json]Apply the policy to the eval of this PR’s artifact and emit the decision. Exit 0 (ALLOW), 1 (REVIEW), 2 (BLOCK). Since 0.36.0 decide uses only the eval whose artifact digest matches the ref. It REVIEWs with artifact_unresolved when the ref does not resolve, and with no_evaluation_found when no eval matches. Before, it could fall back to the service’s latest eval.
The policy mode caps the decision independently of trust: only auto can ever ALLOW, so a clean evaluation in shadow or advisory returns REVIEW. Since 0.28.0 the output says so rather than leaving you to infer it — the rationale carries shadow_mode_cap / advisory_mode_cap / policy_mode_unset_cap, and --format json adds policy_mode, mode_ceiling, mode_capped, and the full lattice. A REVIEW that came from actual findings carries no cap code, so an operator can confirm a clean shadow run without opening a protected report.
Since 0.36.0 decide also:
- Gates on earned trust. It runs the trust state machine, records each step as a
trust.transitionortrust.decayledger event, and gates on the lower of the earned level and the level the current score implies. A new service starts at NONE. See Trust Model. - REVIEWs any regression (
regression_detected). A P0 regression still BLOCKs. Improvements elsewhere never outweigh a regression. - Names the cause of a risk x trust hold in
automode:risk_class_r3_requires_review,trust_level_insufficient:<none|low>, orrisk_class_r2_requires_high_certainty. - REVIEWs a PR that edits its own grading inputs (
governance_files_changed), in every mode: files under.sigil/(except.sigil/types/),scenarios/or any[[scenarios]]root,.github/,action.yml, or aCODEOWNERSfile. - REVIEWs an eval whose scenarios did not all load or run (
scenarios_not_loaded,partial_scenario_set), or that ran against overridden endpoints (endpoint_override). - Checks the ledger chain before and after its pre-ALLOW sync (
ledger_chain_broken), and REVIEWs if the sync pulled a newer eval of the artifact (eval_superseded_by_sync). - Signs an in-toto attestation for the decision when
[attestation] signing_keyis set, and reports its path asdecision.attestation.
--break-glass <REASON> needs an interactive terminal. It lowers a BLOCK to REVIEW, except an OPA policy denial (opa_policy_denied), which stays BLOCK.
For behaviour-preserving PRs (no scenario changes outcome), the evidence class is NO_CHANGE when at least one scenario passes on both sides and none flaked or hit plugin infrastructure. Confidence is min(1.0, passing / 10), so a refactor that keeps 8 or more scenarios green can ALLOW in auto with min_confidence = 0.8.
sigil ci
Section titled “sigil ci”sigil ci owner/repo#42 --service <svc> [--control-ref <REF>] [--comment] [--auto-merge] [--dry-run]eval + decide + GitHub integration in one. See CI Integration. Since 0.36.0, if sigil decide fails for any reason, sigil ci fails closed: REVIEW, or BLOCK when the eval shows behavioural failures with satisfaction below 0.5, never ALLOW, and auto-merge never runs. The status reads sigil: decide failed, needs review and the JSON report carries decide_error. An eval that ran zero scenarios cannot ALLOW.
Scenarios
Section titled “Scenarios”sigil run
Section titled “sigil run”sigil run [PATHS...] [--filter <SUBSTR>] [--tag <T>] [--exclude-tag <T>] [--endpoint <URL|NAME=URL>] [--plugin-route <SERVICE:LOGICAL_PORT=URL>] [--endpoints-from <PATH>] [--env KEY[=VALUE]] [--lib-dir <DIR>] [--allow-origin <URL>] [--allow-cross-origin] [--deny-capability <NAME>] [--reset [NAME=]METHOD:/path] [--resets-from <PATH>] [--seed <64-hex|auto>] [--json]Minimal scenario runner — runs .lua files directly without requiring .sigil/sigil.toml, the eval pipeline, or a ledger. Each positional is a file or a directory (recursive walk for *.lua, lib/ skipped). --filter is substring-matched against scenario file path and title (repeatable, OR’d). --tag / --exclude-tag use the same semantics as sigil scenario run (exclude always wins). --endpoint is optional — surfaces a clear error at first HTTP call if a scenario needs one. Exit codes: 0 all passed, 1 some failed, 2 zero scenarios matched (pytest convention).
--env KEY[=VALUE](repeatable) — populatesigil.env().KEY=VALUEsets a literal value (only the first=splits, so values may contain=); bareKEYpasses the value through from sigil’s own process environment, keeping secrets off the command line. Strict allowlist: only named keys are visible to the scenario; duplicates are last-wins. If a selected plugin operation needs a granted name that is absent, trusted human output names the exact--env KEYto add; JSON, eval feedback, and ledger evidence omit the name and value.--lib-dir <DIR>— the directoryrequire('lib.<module>')resolves inside, for every scenario in the run. Defaults to the discovery anchor joined withlib: a directory argument itself, or a file argument’s parent — the same anchor the scenario id is derived from. Name it explicitly when staging a tree whose helpers do not sit there. Validated up front, so a missing directory aborts the invocation rather than surfacing as a require failure partway through the suite. When a helper is missing at run time, the error names the anchor it was resolved from and suggests this flag if it was not already given. See Writing Scenarios.--endpoint <NAME=URL>— declare a named route, alongside the bare-URL form that sets the primary HTTP base URL. An HTTP(S) named route is reachable from Lua assigil.service("name")and enters the HTTP origin pin set. A named non-HTTP route such as--endpoint s2sql=mysql://127.0.0.1:3306is available only to locked plugins: it never enterssigil.service(), the HTTP pin set, or reset targets, and the plugin grant still controls TLS. Repeatable and validated up front.--plugin-route <SERVICE:LOGICAL_PORT=URL>— bind the full plugin grant target to a published port, for example--plugin-route minio:9000=http://127.0.0.1:49172. Repeatable; these bindings never create HTTP service handles, origin pins, or reset targets. Legacy named--endpointroutes work for plugins only when their effective URL port equals the grant’s logical port.--endpoints-from <PATH>— load a service map (-reads stdin). Values may be URL strings or structured entries such as{"url":"http://127.0.0.1:49172","logical_port":9000}under the keyminio. Structured HTTP(S) entries retain named HTTP service behavior while also binding the full plugin target. Legacy strings require matching effective and logical ports for plugin use; enrich an orchestrator’s map when it publishes different ports. Non-HTTP entries require an explicitly written port and remain plugin-only; a written scheme default such asws://host:80counts. The primary endpoint stays argv-only; a name declared in both inputs is an error. Invalid entries abort before scenarios run with the source path, key, and cause. Dotted keys are exact:singlestore-pipelines.sql:3306never falls back tosinglestore-pipelines.--allow-origin <URL>(repeatable) — allowlist one specific extra origin on top of the declared set, keeping pinning enforced for everything else. Each value must be a barescheme://host[:port]. Prefer this over--allow-cross-originwhenever the extra origins are known in advance.--allow-cross-origin— disable endpoint pinning. By default every HTTP call and redirect is confined to the run’s declared origins — the--endpointorigin (when a bare one is given), every named service origin, and every--allow-originvalue; a cross-originbase_urlis a runtime error. A run that declares only named services (no bare--endpoint) is pinned to exactly those service origins; only a run that declares no origins at all is unpinned. This flag restores the old unconfined behavior — only use it for trusted scenarios (see the security note in--help).--reset [NAME=]METHOD:/path(repeatable) — send an HTTP request before every scenario:METHOD:/pathtargets the primary--endpoint,NAME=METHOD:/pathtargets a named service declared with--endpoint name=url/--endpoints-from. Any 2xx is success. A hook targeting the primary needs a bare--endpoint; aNAMEnobody declared is an error listing the declared names. All of it is validated before the first scenario runs, with the same rules as[scenario.reset].--resets-from <PATH>— load reset hooks from a JSON array (-reads stdin) whose elements carry the same fields as a[[scenario.reset]]entry:method,path,service,headers,body,expected_status— for hooks that need headers, a body, or an exact status. Order is--resetflags in argv order, then file entries in file order. A hook that fails at run time fails that scenario without executing its body, reported withfailure_class = "crash"(tooling, not behavior). Hooks only ever reach the primary or a declared service with a relative path, so they add no pinning surface.--deny-capability <NAME>(repeatable) — refuse to run scenarios that declare or use this capability. Nothing is denied by default; an unknown name aborts the invocation. Enforced fail-closed: a scenario that declares or calls a denied capability fails lint (E007) before it executes — reported withfailure_class = "capability"— and the runtime installs a denying stub regardless of what the scenario declared.sigil.execrunssh -c <command>on the host running sigil, not inside any container; pass--deny-capability execwhen running third-party or agent-authored scenarios. Projects with asigil.tomlset the same list in[eval] denied_capabilities.--seed <64-hex|auto>— choose the 32-byte root for deterministic generators.autois the default and uses fresh OS entropy; an exact 64-hex value reproduces the same per-scenario sample stream. Human output prints the chosen seed and JSON records it as top-levelrng_seed.--json— emit a machine-readable report on stdout instead of human output. The top level includesrng_seed; per-scenario entries are{id, title?, status, duration_ms, failure_class?, plugin_failure?, checks, expects, logs, attachments, error?, diagnostic?}—logsis everysigil.logmessage in call order andattachmentsthesigil.attachname→value map (both always present, empty when unused).failure_classis"assertion"(a behavior problem, including an uncaught expectation inrequire("lib.x")),"crash"(a tooling/scenario problem),"pinning"(an endpoint-pinning violation — a cross-originbase_urloverride or redirect blocked by the runtime origin gate),"capability"(a capability denied by--deny-capability/[eval] denied_capabilities), or"plugin_infrastructure"(the plugin host could not make the requested behavior available), omitted for passing scenarios. An exact typed plugin-infrastructure failure always retains its fixed safeerrorand a bounded source-freediagnostic, and addsplugin_failure = {code, stage, operation?, message}; the optional operation is absent when none was recorded. The diagnostic uses only the closed error code and fixed operator summary rather than a Lua code frame, because source text could repeat a plugin identity, grant, route, or secret name. This bounded direct-run projection never contains plugin identity or version, routes, source chains, secret names, or secret values. It is not the agent-visible evaluation feedback schema, which keeps its coarse isolation-wall marker.
For PR evaluation, scoring, ledger writes, baseline comparison, and decision policy use sigil eval instead.
Supplied plugin routes retain eager URL, TLS, DNS, and ambiguity checks.
An absent route is deferred until an operation uses that endpoint; it then
fails as sticky PLUGIN_NETWORK_DENIED, even if guest code catches the error.
See route configuration.
Log entries preserve arbitrary bytes; see the
log representation and budget.
sigil scenario lint / lint-path
Section titled “sigil scenario lint / lint-path”sigil scenario lint [--service <svc>]sigil scenario lint-path <paths...> [--json]lint parses + static-checks every scenario in a configured project (capabilities, metadata, sandbox-safety rules). E009 requires a literal top-level return { ... }, because dynamically built metadata cannot be reviewed statically; the literal table may delegate its run field to a helper. lint-path lints arbitrary .lua files without a .sigil/ project layout — the same rule set, with machine-readable findings under --json ({file, code, severity, message, line, span}). Exit 0 when clean, nonzero when any error-severity finding is present.
Project lint additionally follows bounded literal require("lib.*") graphs
and attributes nested plugin loads to the calling scenario’s capability checks.
Missing, malformed, cyclic, or over-limit graphs are E012. lint-path has no
project helper root and does not perform that transitive check; dynamic module
names remain runtime-only. See capabilities.
sigil scenario run
Section titled “sigil scenario run”sigil scenario run [SCENARIO_ID | --all] [--service <svc>] [--endpoint <URL>] [--seed <64-hex|auto>] [--format human|json] [--allow-cross-origin]Run scenarios from a configured project. --seed and --format json use the same seed/report contract as sigil run (one schema across both runners). Endpoint pinning applies here too, with the project’s [eval] allowed_origins allowlist permitting known sidecar origins. Locked network plugins use full-target [eval.plugin_routes] bindings, or named [eval] services whose effective URL port matches the grant’s logical port, never the primary --endpoint.
sigil scenario generate
Section titled “sigil scenario generate”sigil scenario generate --from <spec> --service <svc>Five-input pipeline (spec, diff, bugs, traces, source) → LLM test plan → scenario code → three-stage validation (lint, parse, optional execution via --verify) with repair loop.
sigil generate-types
Section titled “sigil generate-types”sigil generate-types [--service <svc>]Emit .sigil/types/sigil.lua — a LuaLS type stub that gives editor autocomplete for the full DSL.
Plugins
Section titled “Plugins”Plugins are project dependencies, not global extensions. The per-user store is
an immutable byte cache; [plugins.require] plus
.sigil/sigil.plugins.lock is runtime authority. See Using WebAssembly
Plugins for the complete workflow.
sigil plugin add / remove
Section titled “sigil plugin add / remove”sigil plugin add NAME[@VERSION]sigil plugin add github:OWNER/REPO@VERSIONsigil plugin remove NAMEadd requires the requested release to be present in the per-user store; run
sigil plugin install NAME[@VERSION] first on a fresh machine. It creates a
minimal schema-linked .sigil/sigil.toml when the directory is not yet a
project, writes an exact formatting-preserving requirement, and transactionally
refreshes the lock and managed .sigil/types/wasm/ stubs. It never performs
network acquisition itself. remove deletes that project requirement, lock
entry, and managed stub while retaining cached package bytes. A failed
transaction restores the original config bytes.
sigil plugin lock / sync
Section titled “sigil plugin lock / sync”sigil plugin locksigil plugin lock --update NAMEsigil plugin synclock resolves [plugins.require] into exact source, version, digest, host-API,
and publisher-evidence records. It retains compatible entries and refuses a
source change unless --update NAME makes that change explicit. sync is the
CI operation: it installs only exact packages already approved by the lock and
never mutates the config, lock, stubs, or cache selection. A fresh clone does
not need .sigil/types/wasm/ for sync or execution; those are editor aids, and
sigil generate-types can recreate them from the exact lock.
Remote plugin commands honor the standard proxy environment variables and, in Sigil 0.32.6 or newer, the operating system TLS trust store. See Corporate proxies and TLS inspection for enterprise-network diagnostics.
Store and inspection commands
Section titled “Store and inspection commands”sigil plugin install NAME[@VERSION]sigil plugin listsigil plugin list-remote [NAME]sigil plugin info NAME[@VERSION]sigil plugin verify NAME[@VERSION]sigil plugin inspect NAME[@VERSION]sigil plugin use NAME@VERSIONsigil plugin update [NAME]sigil plugin uninstall NAME@VERSION --forcesigil plugin uninstall NAME --all --forceinstall prints Installed NAME@VERSION followed by digest and acquisition
evidence. These commands operate on the per-user store and current authoring
selection only; they do not make a plugin available to a scenario.
inspect NAME[@VERSION] verifies the immutable installed payload and reflects
its component offline without changing the authoring selection. An exact
version lets you compare cached APIs without locating private store paths.
Package author commands
Section titled “Package author commands”sigil plugin validate ./plugin.tomlsigil plugin inspect ./plugin.wasmsigil plugin pack ./plugin.toml --output-dir ./distsigil plugin install --path ./dist/name-version.sigil-plugin.tar.zstsigil plugin test --path ./plugin.toml --scenario ./conformance/roundtrip.lua --config ./operator-plugin-test.toml --allow-localpack emits a deterministic canonical archive. validate and inspect do not
grant execution authority. A locally installed archive can be inspected and
cached but cannot become a project dependency. plugin test is the explicit
local authoring exception: it runs named visible scenarios through the
production host in disposable state. --allow-local is required alongside
allow_third_party = true, local:path in the install and requested capability
allowlists, and effective grants in the supplied config. Only explicitly named
--env KEY[=VALUE] values are exposed, and scenario-side exec is denied.
The command creates no project locks, store selections, evaluations, ledger
events, attestations, trust changes, or ALLOW decisions.
Semantic gRPC is a separate boundary: the Host API 1.3 adapter requires a
frozen project owner that plugin test cannot supply. Local trust-policy
allowances do not make a Temporal candidate lockable or authorize this adapter.
Use an official provenance-bearing candidate and ordinary project execution
for its integration gate. See semantic gRPC.
Ledger
Section titled “Ledger”sigil ledger sync / rebuild
Section titled “sigil ledger sync / rebuild”sigil ledger syncsigil ledger rebuildsync pulls and pushes ledger events. rebuild recreates the SQLite projection from the ledger. In 0.36.0 the projection rebuilds itself once on first use, because the stored form changed.
sigil ledger verify
Section titled “sigil ledger verify”sigil ledger verify [--format json]New in 0.36.0. Checks that every line parses with a matching content hash, that every parent hash names an event in the ledger, and that each agent’s seq values run 0..n with no gap. Exits 1 when the chain is broken. sigil decide runs the same check and REVIEWs with ledger_chain_broken. Forks are normal and accepted. Duplicate seqs are reported (duplicate_seqs) but accepted, because clones that share an agent name reuse a seq until they sync.
This is not tamper evidence. The content hash is unkeyed, so removing the newest events, reordering, or rewriting an event and rehashing everything after it is not detected. It catches accidental corruption and the removal or edit of events that later events depend on. See Ledger.
sigil replay
Section titled “sigil replay”sigil replay <eval-id>Ordinary replay verifies and consumes pinned evidence without executing
components, selecting secrets, opening service sockets, or deploying a box.
Use --live only when fresh execution is intended. A selected plugin whose
semantic gRPC profile contains any mutation RPC is refused before deployment,
even when the scenario intends to call only a read alias. --live is not
mutation authorization.
Replay JSON can include original_baseline_verdict and
replay_baseline_verdict independently of PR comparison status. Missing or
skipped baseline evidence has no verdict; --pr-only omits both fields. A PR
MATCH does not establish a fresh baseline pass.
sigil trust show / history
Section titled “sigil trust show / history”sigil trust show --service <svc>sigil trust history --service <svc> [--days <N>]show prints the current trust score, its components, and the policy mode. history lists the trust.transition and trust.decay events that sigil decide records (default window: 30 days). Before 0.36.0 nothing wrote those events, so the history was always empty.
sigil risk classify
Section titled “sigil risk classify”sigil risk classify <pr-ref> --service <svc> [--base <REF>]Classify the PR’s own changes, <base>...<pr-ref> (default base origin/main), as R0–R3. An explicit A..B range is used as given. --base is new in 0.36.0.
Operations
Section titled “Operations”sigil init
Section titled “sigil init”sigil init --service <svc>Scaffold .sigil/ (config and tool state) plus scenarios/<svc>/ at the project root. The generated sigil.toml starts with a #:schema https://runsigil.com/schemas/sigil-config.schema.json line so editors with TOML schema support offer completion and flag unknown keys.
sigil schema
Section titled “sigil schema”sigil schema configPrint the JSON Schema for sigil.toml — generated from sigil’s own config types and drift-tested in CI, so it is the authoritative list of every section, key, default, and enum. The same document is published at https://runsigil.com/schemas/sigil-config.schema.json. Validate a config against it in CI, or sigil schema config > schemas/sigil-config.schema.json to vendor it.
sigil migrate
Section titled “sigil migrate”sigil migrate [--apply]Upgrade a project from the pre-0.27 layout — .sigil/scenarios/<svc>/{visible,holdout,staging}/ — to scenarios/<svc>/ with visibility carried by the file extension. Dry-run by default; add --apply to write.
Uses git mv so rename detection follows each file’s history. Idempotent, and it verifies the scenario-id set is identical before and after, so a migration that would silently drop or rename a scenario aborts rather than reporting success. It refuses a dirty git worktree, refuses when both layouts are populated, and refuses to move an unencrypted file out of holdout/ — in the new layout that name means visible, so migrating it would publish a holdout.
Scenario ids are unchanged, so ledger history stays comparable. Holdout set hashes do change once, at migration, because re-sealing yields new ciphertext; a scenario.layout_migrated ledger event is recorded so replay / diff can explain the discontinuity instead of presenting prior evals as corrupt.
sigil keys
Section titled “sigil keys”sigil keys add <name> <age1...> [--force]sigil keys add-self [--force]sigil keys rotateManage the age recipients that holdout scenarios are encrypted to (the [keys] table). add / add-self create the table when sigil.toml lacks one, and re-adding a name with the same recipient is a successful no-op that never prompts — safe to run repeatedly from headless provisioning. Re-adding a name with a different recipient confirms interactively, and fails closed without mutating sigil.toml when there is no terminal; --force replaces it non-interactively. After adding a recipient, run rotate as a current key-holder to re-seal existing holdouts to it.
sigil override
Section titled “sigil override”sigil override <eval-id> --service <svc> --reason <REASON> [--ticket <TICKET>] [--note <TEXT>]Record a human override of a decision. REASON is one of SCENARIO_BUG, ACCEPTABLE_REGRESSION, FALSE_POSITIVE, BUSINESS_URGENCY, or OTHER. Appends override.record to the ledger. Overrides feed the trust score, and an override rate above 10% drops earned trust to LOW.
sigil incident report
Section titled “sigil incident report”sigil incident report <pr-sha> --service <svc> --severity <sev1|sev2|sev3|sev4> --description <TEXT>Record a production incident attributed to a PR. A Sev-1 or Sev-2 incident drops earned trust to LOW.
sigil verify
Section titled “sigil verify”sigil verify <eval-id> [--service <svc>]Verify the signed in-toto attestation for an eval against [attestation] trusted_public_keys. Since 0.36.0 sigil decide writes one when a signing key is configured.
sigil store gc
Section titled “sigil store gc”sigil store gc [DURATION] [--execute] [--yes]Delete unreferenced blobs older than DURATION (default 30d). Dry-run unless --execute. Since 0.36.0 gc also removes .<uuid>.tmp files that a crashed put left behind, once they are more than an hour old, and reports them separately (stale_temp_file_count, deleted_temp_file_count, freed_temp_file_bytes in --format json).
Hidden / advanced
Section titled “Hidden / advanced”sigil intent-mcp-server
Section titled “sigil intent-mcp-server”Internal MCP stdio server used when provider = "claude-code" in [judge]. Not usually invoked directly.