The envelope and orchestrator of verifiably: what may run unattended
What may run unattended, under what tier, and how the next research action is chosen: the run baseline and dispositions, tiers as write-class permits, the actor sandbox and session ledger, behavioural profiles and the versioned priority function. It drives science's commands as tools and holds no private kernel access. Built: tiers, the closing check, the sandbox, and autonomy run's launch side.
Part of verifiably: verifiably builds understanding of the world from small, reproducible data analyses: each one a precise question asked of real data, recorded so anyone can run it again, with the data leading and unverified claims kept out. Overview
atoms · nodes · refs · beliefs · science · autonomy
autonomy run --tier <report-only|coordination|mints> [--route KIND=FAMILY]... \
[--lease SECONDS] [--report-fd N] [--model-route NAME] -- <actor argv>The actor gets autonomy run's fds 0, 1 and 2, so each must be a pipe, a connected
AF_UNIX stream socket or /dev/null; a terminal or a file is refused. autonomy run
never reads stdin or writes stdout, help included, and its diagnostics on stderr are
for a person. At a terminal, run it as
(set -o pipefail; autonomy run --tier report-only -- <actor argv> </dev/null 2>&1 | cat -v)pipefail keeps its exit code, which a pipeline would otherwise replace with cat's:
| Exit | Meaning |
|---|---|
| 0 | clean |
| 3 | quarantined |
| 4 | unwired, or stopped before its close, its run directory left for run close |
| 2 | refused before a run directory exists: a configuration, stdio or usage error |
Until science's run endpoint exists, every run that passes its checks closes
unwired. A program learns its run from --report-fd: one JSON line with the run's
id and directory, then one with its disposition and exit code
(docs/specs/2026-10-12-envelope-design.md, decision 12).
A fresh pi analyst over declared inputs, under the same sandbox and model route, with
no endpoint (docs/specs/2026-10-12-envelope-design.md, decision 14):
autonomy analyst --model-route NAME --model MODEL --thinking LEVEL --input DIR... \
[--deny PATH]... --release DIR [--report-fd N] [--pi-dir DIR] [--node-dir DIR] \
[--lease SECONDS] [--deadline SECONDS]Each --input is bound read-only at its real path; each --deny joins the denied set
beside the route's entries, <state> and the release directory. --thinking is
required and validated before launch against pi 1.1.0's anthropic-messages model
registry (off, minimal, low, medium, high, xhigh, max), refusing any level
pi would clamp, omit or collapse for --model (thinking-unsupported, or
model-unknown if pi's registry does not list --model). Before launch the
static check, a walk of every admitted tree (no submount, no hard link in a
user-writable tree, no inode shared with a denied root) and the probe all run. pi 1.1.0
runs from the pinned install (--pi-dir, PI_DIR, or ~/.local/share/pi/1.1.0) with
a fixed argv (--model and --thinking from the request) and the fixed tool set
read,bash,grep,find,ls,write; the prompt comes
over fds 0 and 1 (pi's RPC), which the caller drives as for autonomy run. When pi
exits, outputs cross from the outputs directory under scratch to the release directory, which must exist
and be empty, by a walk that follows no link: a symlink, FIFO, socket, device or
hard-linked file is refused and listed.
| Exit | Meaning |
|---|---|
| 0 | released, the route closed clean, pi exited 0 |
| 3 | released with findings: pi exited non-zero, the route did not close clean, or an entry was refused at release |
| 4 | the analyst never started (a failed static check, walk, probe or launch) |
| 2 | refused at configuration: nothing kept |
The run directory <state>/analyst/<run-id>/ holds the events log (recording the route,
model and thinking level at allocation and launch), the epoch record (model,
thinking, pi's lock hash and the node, bwrap, rg and fd versions), the walk report, the route's
records and proxy log, the launcher's log and the release report (released files with
their hashes, refused entries with their reasons); --report-fd carries its path out as
for autonomy run. Scratch under <cache>/analyst/<run-id>/ is left for the caller:
pi's session files there hold everything the analyst read.
An actor has no network of its own. --model-route NAME gives it one path out: a
proxy on VERIFIABLY_MODEL_URL (http://127.0.0.1:4780) that forwards only the
route's operations to its one upstream and adds the provider credential there. The
actor never holds the credential. Point the harness at that URL with a placeholder
key. For pi, that is a provider baseUrl in its models.json:
{"providers": {"anthropic": {"baseUrl": "http://127.0.0.1:4780", "apiKey": "placeholder"}}}The proxy streams body-bearing responses with its own HTTP/1.1 chunked framing. Only a completed upstream body gets the final zero chunk, so an HTTP client that validates framing and reads to the end detects a route stop or upstream framing failure as an incomplete response. A clean EOF from an unframed upstream body cannot establish semantic completeness. Local 403/502 errors remain length delimited.
That configuration only directs pi. The proxy enforces the route whatever the actor
does to it (decision 13). The route is a TOML file at
$XDG_CONFIG_HOME/verifiably-autonomy/routes/NAME.toml:
api = "anthropic-messages"
upstream = "https://api.anthropic.com"
max_request_bytes = 8000000
forward_headers = ["anthropic-version", "anthropic-beta", "content-type", "accept", "user-agent"]
[header_tokens]
anthropic-version = ["2023-06-01"]
anthropic-beta = ["fine-grained-tool-streaming-2025-05-14", "interleaved-thinking-2025-05-14",
"mid-conversation-output-config-2026-07-01", "thinking-binding-controls-2026-08-01",
"inline-tools-2026-09-15"]
[credential]
file = "/home/you/.config/verifiably-autonomy/credentials/anthropic" # 0600, yours, one link
header = "x-api-key"
[[operations]]
method = "POST"
path = "/v1/messages"
queries = ["beta=true"]
models = ["claude-haiku-5-5"]
fields = ["model", "messages", "system", "max_tokens", "stream", "tools", "tool_choice",
"thinking", "temperature", "metadata", "stop_sequences", "top_p", "top_k",
"output_config"]
block_types = ["text", "image", "tool_use", "tool_result", "thinking", "redacted_thinking"]
source_types = ["base64"]What the route leaves out, the proxy refuses with a 403 that names the code, for
example body-refused for an MCP server or a URL image source. just live-egress NAME runs pi through the proxy to the real API once and prints what the proxy
recorded, including any beta token pi sends that the route does not list. The example
is what pi 1.1.0 needed on 2026-10-17. The anthropic-messages dialect now
admits only the fourteen fields shown above and client tools. Listing fallbacks,
mcp_servers, container, an unknown field or a server tool type makes the route
route-invalid, before the actor runs. A client tool may omit type; an explicit
"custom" type needs tool_types = ["custom"] on that operation.
The dialect checks message roles, every admitted control object's keys, required
fields, primitive types and nullability, content/source pairings, and recursive
route permissions. System-message effort also needs output_config in fields.
Client tool input_schema and tool_use.input stay opaque JSON, and valid request
bytes cross unchanged. A listed beta token never grants an undefined body shape.
The supported subset preserves pi's plain/effort requests and deferred placeholder.
It excludes inline tool changes, clear_at, output format/task budget, text
citations, image transformations, caller/toolset metadata and tool examples, as
well as server tools, MCP and containers. Unsupported requests are refused rather
than rewritten. URL/file sources remain explicit operator opt-ins. See
decision 13 for exact shapes and content
contexts. These checks do not validate provider numeric ranges or prove that a
provider accepts a particular compatibility option.
- User and autonomy layer design
— the governing design, in the kernel repository: §7 is this layer, §8 orders the
sub-projects. Work here starts at sub-project #6, the envelope, after the command
framework, and ends with #7, the loop and the priority function (
science.priority.v1). - The envelope's design,
docs/specs/2026-10-12-envelope-design.md, and the plans it cites are under docs/specs and docs/plans; task records and newer working documents live in the project's records repository. - This layer is split from
scienceon code-lineage independence: the envelope must not share a package with the surface it constrains, so its only seam isscience's commands as tools.
MIT, see LICENSE.