Reference
The supported torio command surface and the fixed boundaries of Torio.
Running the binary
Build with go build -o torio ./cmd/torio, then put the result on your PATH — for example sudo install -m 755 torio /usr/local/bin/torio. Every command below is written as torio … and assumes that.
A freshly built binary sits in the repository root and is not on your PATH, and your shell does not search the current directory, so torio yields command not found until you install it. Running it in place as ./torio … from the repository root also works, but then every command needs the prefix.
Command surface — torio vm
Controls the torio Lima VM. torio vm init creates it; every other subcommand operates on the existing instance. The parent takes no action itself; an absent or unknown subcommand is a usage error.
| Command | What it does |
|---|---|
torio vm init | Create the Torio VM from the embedded pinned template, or succeed idempotently when a compatible instance already exists. Incompatible instances fail closed — there is no --force. Sizing: --cpus N, --memory SIZE, --disk SIZE (defaults 4, 8GiB, 60GiB). They apply at creation only, since init never recreates. |
torio vm status | Report the Torio VM state. |
torio vm start | Start the VM. Idempotent; confirms a Running post-state before reporting success. |
torio vm stop | Stop the VM. Graceful and idempotent; never uses --force, never removes the VM or its data, and requires a Stopped post-state. |
torio vm bootstrap | Reconcile and verify the existing target for Hermes. Installs the pinned Hermes Agent when the launcher is missing; verifies operator membership in torio-projects. Idempotent on a reconciled target. Accepts --timeout and --json. |
torio vm ssh -- COMMAND… | Run a command inside the VM. Does not open an interactive shell, forward stdin or a TTY, migrate data, start a chat, or copy credentials. |
Command surface — torio serve
Manages the loopback-only Hermes Desktop backend as a user systemd service. It accepts no secrets.
| Command | What it does |
|---|---|
torio serve install | Generate, validate (with systemd-analyze), and enable the backend user service. Idempotent; does not start the backend. |
torio serve start | Start the backend and prove loopback readiness. |
torio serve stop | Stop the backend service. |
torio serve restart | Restart the backend and prove loopback readiness. |
torio serve status | Report systemd state and loopback endpoint readiness. |
torio serve logs | Show recent, bounded, redacted, unit-scoped service logs. Accepts --lines N. |
Command surface — torio brain
Manages the private Second Brain: a Markdown vault at /home/hermes/brain, versioned by a local Git repository, owned by hermes, and registered with Hermes as its own project so any session can search it. The parent takes no action itself; an absent or unknown subcommand is a usage error.
| Command | What it does |
|---|---|
torio brain init | Create the canonical scaffold atomically through private guest staging, make the first local commit, and register the Hermes project. Then install or refresh the global torio-brain retrieval skill so other projects can search the Brain. Idempotent for managed state; refuses to touch non-empty data it did not create. Configures no remote and pushes nothing. |
torio brain status | Report state (initialized, uninitialized, or drift), the canonical path, native filesystem, ownership and mode, Git worktree state, aggregate counts, Hermes project registration, and skill state. Changes nothing. |
torio brain import <host-directory> | Import an existing Markdown vault through private host and guest staging, verified by checksum on the guest. Accepts --into SUBDIR to land the import as one new contained subtree, and --dry-run to preflight without transferring anything. |
Output never contains note names or note content — not in success output, not in error.details. Every command reports bounded aggregate metadata only: file counts, total bytes, a manifest digest, and stable drift markers. This is the Brain's privacy boundary, not a matter of brevity.
import refuses or skips credential-shaped files, repository metadata, links, hardlinks, special files, and executables. Existing data is never overwritten — the single exception is an untouched scaffold that Torio itself created.
Sessions that were already open when init ran will not see the retrieval skill: Hermes caches a skill's prompt per backend process, so restart them.
Getting the Brain back out
Torio brings data in and does not take it out. There is no torio brain export. Copying the Brain to your host is an explicit thing you do:
limactl copy torio:/home/hermes/brain/ <host-destination>/That is your command, not a Torio feature: nothing verifies the result, and Torio does not call it a backup.
Command surface — torio project
Attaches repositories to the guest, inspects them, and forgets them. The parent takes no action itself; an absent or unknown subcommand is a usage error.
| Command | What it does |
|---|---|
torio project add <name> <remote> | Clone the exact remote into the derived workspace path, or verify and adopt a checkout already there; give the operator and hermes shared access; register the project with Hermes before recording it in config. --id SLUG picks an id other than <name>; --use makes it active on success. |
torio project list | List the registered projects. Reads config only, runs nothing on the guest, and works with the VM stopped. |
torio project show <id> | Report the registry entry, checkout state, and Hermes registration. Reports drift as stable markers instead of repairing it, and returns no filenames, diffs, or raw Git output. |
torio project use <id> | Make a registered project the active one in Hermes. |
torio project remove <id> | Archive the Hermes project and drop the config entry. The checkout is never deleted, and the output says where it still is. |
torio project enter <id> | Open an ordinary interactive terminal in the checkout with SSH agent forwarding disabled. Interactive, so it does not support --json. |
torio project shell <id> | Open an ephemeral operator session in the checkout with your SSH agent forwarded. Interactive, so it does not support --json. |
The workspace path is not an input. It is always derived as /home/hermes/projects/<id> — never taken from you, never stored in config. Without --id, the id is <name> itself, which must be a lowercase slug.
Torio stores no Git credentials. A remote the guest cannot already read without prompting fails closed; the fix is a human granting access on the guest, outside Torio, not a retry.
add resets, cleans, and deletes nothing on the guest, so a rerun after a failure finishes the work rather than starting over.
Routine terminals and push capability are separate
Use torio project enter <id> for ordinary editing, checks, and local commits. The SSH transport disables agent forwarding and connection multiplexing, so it cannot reuse a push-capable operator connection.
The persistent Hermes backend has read access and nothing more. project shell forwards your SSH agent for exactly as long as the session lasts, and the capability leaves with you when you exit.
The session is preflighted first — the project registered, the VM bootstrap-verified, the checkout present with the registered origin and shared permissions, your local agent holding an identity to forward. Torio never test-pushes to prove any of it, and once you exit it makes no claim about what you pushed. Check the remote yourself.
The session is not bounded by --timeout; you end it.
Command surface — torio mcp
Prepares and verifies the guest boundary that a future MCP broker will use. The parent command takes no action itself; an absent or unknown subcommand is a usage error.
| Command | What it does |
|---|---|
torio mcp install | Provision the unprivileged torio-mcp identity, its private 0700 home, the torio-mcp-clients group, and the root-owned policy directory. Then verify the resulting custody boundary. A settled rerun reports changed:false. |
torio mcp status | Verify the identity, group, home, policy, Hermes-profile, and optional runtime invariants without repairing them. A never-provisioned guest is an unmet precondition; drift is a verification failure. |
install accepts no secrets. It may report changed:true together with an error when a durable mutation succeeded before a later verification failed. That partial state is retained in JSON details and the human error explains the required rerun or backend restart.
Both subcommands report the grant they verified: every service in /etc/torio-mcp/policy.d/, its upstream endpoint, how many tools it allows and how many of those write. Nothing about a service is built into the CLI — a service is a policy document, and adding one means writing a second file as root. Under --json the grant is a policy object with a services array ordered by name, alongside the generation digest a running broker publishes, so a report and the process enforcing policy can be compared rather than assumed equal.
A reported write tool is a count, not a capability. The count exists because a document that marks writes must be able to say how many it grants; no released binary sends MCP traffic upstream.
This is custody preparation, not an active integration. The released CLI does not package, install, or activate the dormant broker and relay binaries. It does not perform OAuth or send MCP traffic upstream. Runtime transport and credential lifecycle remain blocked by ADR-0004 until a complete contract is accepted.
Command surface — torio version
| Command | What it does |
|---|---|
torio version | Print the version, commit, build date, and Go toolchain of the binary you are running. Accepts --json. |
This is the only place Torio names its own version. Nothing else in the CLI or in these docs is labelled by release, because the label would not tell you anything you could act on — and it would be wrong the moment the next one shipped.
A binary built straight from a checkout reports dev with an unknown commit; that is expected, not a fault.
Readiness and exit semantics
torio vm bootstrapfails closed on any drift or unverifiable state with exit 6 and remediation. A rerun is success only when every postcondition is proven.torio serve startandrestartfail closed unless the systemd state is active andGET /api/statusanswers200through loopback.torio serve statusexits non-zero when not ready: 3 = not installed or inactive; 6 = active but the endpoint is dead.toriowith no subcommand is a usage error (exit 2); a missinglimactlis exit 8; an unmet precondition such as a stopped VM is exit 3.
Global flags
Four flags are accepted by every command, before or after the subcommand. This is the whole list; an unknown flag is a usage error (exit 2), never silently ignored, and there is no global --force.
| Flag | What it does |
|---|---|
--json | Emit a single machine-readable JSON document on stdout, where the command has one to emit; human logs stay on stderr. --help is the one exception — it prints usage and exits 0 without an envelope. |
--verbose | Raise stderr diagnostics from warnings to debug. Stdout is untouched, so machine output is identical with and without it, and the extra lines are redacted like every other diagnostic. |
--timeout DURATION | Bound the operation. Default 30s, or default_timeout from the config file when set; an explicit flag always wins. Anything above the policy maximum 10m is rejected before any work starts. |
--config PATH | Read the non-secret config document from PATH instead of $XDG_CONFIG_HOME/torio/config.json. It bypasses XDG entirely, is resolved and validated rather than merely parsed, and applies to the project registry as well; a missing file or an invalid document is a usage error (exit 2). |
Fixed boundaries
| Thing | Value |
|---|---|
| VM | torio — a Lima instance Torio creates from a pinned template and never re-images or deletes |
| Guest identity | hermes |
| Profile / application state | /home/hermes/.hermes (native Linux filesystem; Hermes $HERMES_HOME, not the Second Brain) |
| Second Brain vault | /home/hermes/brain (native Linux filesystem; separate from profile) |
| Workspace root | /home/hermes/projects (setgid torio-projects shared workspaces) |
| A project's checkout | /home/hermes/projects/<id> — always derived from the project id, never supplied |
| Backend bind | 127.0.0.1:9119 inside the VM (never a public address) |
| Backend API auth | non-public /api/* requires an X-Hermes-Session-Token header; /api/status is public. Headless serve surfaces no token, so the operator pins one. |
| Serve unit working directory | /home/hermes/hermes-agent — which is why Desktop's working directory must be set explicitly |
Rootful Docker for the hermes guest identity is forbidden. Bootstrap verifies hermes is not in the docker group.
Deployment
This site is served at torio.dev by Vercel, straight from the committed site/ output: a minimal vercel.json declares the output directory, and there is no build step, no framework, and no runtime dependency. Account, project, and DNS configuration live outside the repository, with the operator.
The pages under site/ are generated by scripts/build_docs.py from the Markdown sources in docs/content/. The generated files are committed, so a push to main is a deploy.