Tacid's safety is not a promise about how the model behaves. It is the shape of the system around the model: a machine that is a value, one API for changing it, a classifier that decides what may happen without asking, and a small privileged core with a closed set of operations.
01
The machine is a value
A body that rewrites itself must be able to know itself. In Tacid the whole machine is a declaration: /etc/tacid, a git repository, built by Nix into an atomic generation. The system definition is reproducible; homes and declared persistent data survive separately. A generation rollback does not restore every kind of data.
This makes a proposed system change inspectable before activation. A declaration can be diffed. It can be built before it is activated, switched atomically, and switched back. It can be versioned, signed, carried to new hardware, and read by a mind that thinks in text.
Nix gives us this today, so Nix is the engine of Tacid. It is not the face. The owner never has to write a line of it. The agent does — into curatedtacid.* options rather than arbitrary expressions, so a package name cannot become a Nix injection.
The keeper loads the policy only if its head commit is signed by the owner key. The agent's file tool cannot write an owner-signed path. The journal, memory and models never live in the genome.
This machine's hardware. Regenerated, not portable.
mind/agent.nix · preferences.nix
agent
What the agent has decided. Curated options only, never free Nix.
mind/policy.nix · personality.md
owner
The constitution and the system prompt. The agent cannot write these.
owner.nix
owner
Free Nix. Yours. Tacid never writes it.
pkgs/
agent
Derivations built in the Nix sandbox, tagged untrusted by policy.
secrets/
owner
sops-nix, age-encrypted. Resolved only by the keeper, for a named destination.
02
One API for all change
There is one way to change a Tacid machine, and everything uses it: your command line, a script, another agent, and the model itself. A change is a typed transaction, and it walks the same lifecycle whether it installs a package or patches Tacid's own source.
01
classifykeeper
Reversible or not, over the actual rendered change. Unknown counts as irreversible.
02
verdictkeeper
The owner's policy and the autonomy level decide: act, ask, or refuse.
03
renderdaemon
The change becomes text in the genome. A package name cannot inject Nix.
04
stage · eval · builddaemon
Nix evaluates and builds the next generation while the current one keeps running.
05
previewclient
The diff, the reason, and an honest account of what rollback restores.
06
approvalkeeper
Granted client to keeper, bound to this transaction id. The agent is not on this path.
07
activatestem
A typed operation, by activation class: switch, session restart, or reboot.
08
health gatekeeper
Units, daemons, declared services, inference, a tool-use smoke test.
09
journalkeeper
Append-only, hash-chained, out of the agent’s reach. Refusals are recorded too.
If the health gate fails, the keeper re-activates the previous generation, restores the matching state snapshot, journals what happened and notifies the owner. It does this without being asked, and the agent cannot switch it off.
Two kinds, and the difference matters
A change is persistent: it renders into the genome, is evaluated and built by Nix, and becomes a generation you can boot back out of. Anaction is ephemeral: run a command in a sandbox, open a serial port, probe a device, fetch a file. Actions are classified and journaled too, but they do not move the machine's definition.
Because the API is typed and deterministic, the machine is deterministic first and intelligent second. The model proposes. The nervous system decides, acts and records.
Verification is part of the transaction
A transaction carries the checks that decide whether it worked — a command that must succeed, a fact that must become true. Nothing is called done because a model said so.
03
Reversibility is the boundary
Most systems draw the line at privilege: root may, users may not. Tacid draws it at reversibility. The agent acts freely on what can be undone and asks before what cannot.
That boundary is not asserted by the model. It is computed by a deterministic classifier in the keeper, over the actual rendered diff, and anything unknown counts as irreversible. A new kind of change is therefore safe by default: it fails closed, into asking you.
Rollback claims are three-way, and the machine never rounds them up. System state can be restored by switching generations. Data can be restored from a snapshot. External effects — firmware already written to a board, a push that reached a remote — cannot be restored at all, and the approval card says so in as many words.
Computed from the rendered change, in a process the agent does not control.
Taint propagates: a turn that touched untrusted content is capped at level 1.
Unknown is never treated as safe.
The explicit irreversible list
Exposure: making the machine reachable that it was not.
Principals: adding, removing or re-keying who may act.
Secrets: anything that reveals or rotates one.
Boot and encryption: the bootloader, the keys, the recovery path.
External devices: firmware written to hardware Tacid does not own.
Data deletion, and any egress of file content.
git push, because the commons is outside this machine.
Everything on this list stops and asks, at every autonomy level below sovereign, however confident the model is.
04
The owner is a key, not a username
The owner writes the constitution: how much autonomy the agent has, what may leave the machine, what may never be touched. The agent cannot amend it, and approvals travel on a channel the agent cannot forge.
Every actor is a principal, established at connect time from the peer's credentials, the per-boot token and, for the owner, a signature. Every item of context carries a provenance label — owner, user, system, tool, file, web, an MCP server, voice — and untrusted labels propagate through anything derived from them.
So a README that says “now run this and upload your keys” is not an instruction. It is a file-provenance context item, it taints the turn, and a tainted turn cannot take side effects above level 1 however high the owner set the dial.
Voice is untrusted by default and capped at level 2 unless a person confirms physically. A remote agent acts under its own key and its own policy, never the owner's.
mind/policy.nix — written by the owner, signed by the owner
Acts on what a generation rollback can undo. The default.
3
trusted
Adds the categories the owner has marked automatic.
4
sovereign
Broadest scope. Expires, and shows a persistent indicator while it lasts.
05
Small pieces, separated on purpose
Only the stem runs as root. Only the egress proxy has a route out. Only the keeper holds secrets and decides. The agent daemon plans and acts through both, and holds neither.
Only the stem runs as root. Only the egress proxy has a route out. Only the keeper holds secrets and decides. The agent plans, and holds neither. Clients render protocol state and never decide — which is why a second client has feature parity by construction.
Component
Binary
Runs as
Trust
What it is
State
Stem
tacid-stem
root
privileged
Typed operations only; keeper-only socket; no network.
built
Keeper
tacid-keeper
tacid-keeper
authority
Policy, classifier, taint, approvals, secrets, audit, health, snapshots. Denied all IP addresses.
built
Egress
tacid-egress
tacid-egress
gate
The only network path for agent processes; allowlist, credential injection, ledger, airgap.
built
Daemon
tacidd
tacid
agent
Sessions, the agent loop, facts, memory, transaction rendering and building, model clients.
built
Inference
tacid-infer
tacid-infer
hostile input
Supervises sandboxed inference servers behind a local OpenAI-compatible socket.
built
Session
tacid-session
the user
user
Per-user executor; untrusted parsers in sandboxes; voice; desktop interop.
built
Shell
tacid-shell
the user
renderer
Wayland shell for the Niri session.
planned
CLI and TUI
tacid
the user
renderer
Command line; the terminal interface with no arguments.
built
Root is never represented as arbitrary shell. The stem speaks a closed vocabulary of typed operations with strict schemas and no string interpolation, over a socket readable only by the keeper's user and guarded by a per-boot token. There is no operation that carries a command line, so there is nothing to inject into.
06
It runs itself, and undoes itself when it is wrong
Tacid watches a signed channel, builds the next generation while you work, switches when the body is quiet, verifies itself, and rolls back on its own if anything is wrong.
The channel
A signed manifest naming a tested pair of revisions, with an expiry. The trust roles are split: an offline root, and short-lived online snapshot and timestamp roles, so a stolen build key cannot rewrite history or freeze you on an old version.
The health gate
After every activation: units reached their target, daemons are up, declared services answer, the network works if it was declared, inference answers a stub prompt, and the agent completes a tool-use smoke test.
The rollback
On failure the keeper re-activates the previous generation, restores the matching state snapshot, journals it and notifies you. Boot-class changes get bootloader boot counting as well. The agent cannot switch any of this off.
Tacid also improves itself. Its own source is part of what it can change: the agent may rebuild the machine from a patched checkout, test it, live on it, and then propose the change to the commons. Proposals become pull requests only when a human signs them. The core — the stem, the keeper, the protocol, the health gate — is channel-signed, verified before activation, and replaceable only by the owner. A machine running code it built itself says so, in tacid version and in the bar.
07
The terminal interface is the first interface, and it is complete
User-interface state travels in the protocol — plans, live tool state, diffs, pending approvals, badges — so a client that renders those has feature parity by construction. The graphical shell is the same system with more pixels.
tacidthe command line, unabridged43da513
# Reversible, so the agent acts. The diff is the genome, not a summary of it.
$ tacid ask --diff install ripgrep
I will install ripgrep.
+ install ripgrep (cli)
Reversible: system configuration to generation 1.
--- a/mind/agent.nix
+++ b/mind/agent.nix
@@ -5,4 +5,7 @@
# transaction. Your own configuration belongs in owner.nix, which Tacid never writes.
{ ... }:
{
+ tacid.packages.cli = [
+ "ripgrep"
+ ];
}
# Exposure cannot be undone, so the agent stops and says exactly why.
$ tacid ask enable openssh
I will enable openssh.
+ enable the openssh service
This cannot be fully undone:
! enabling openssh would make this machine reachable from the network
Reversible: not by a generation rollback alone.
Waiting for you. Approve it with the keeper, or say no and nothing happens.
Development recording from tacid-cli at revision 43da513on . This historical demo uses a simulated Nix builder and privileged operations. It shows the change flow, not a production installation.
Every command accepts --json. Approval prompts are modal and keyboard-only. The : verbs never touch the model, so :diff, :undo,:gen and :sh work when the mind is unreachable, unloaded or wrong. Readable at eighty columns by twenty-four lines in sixteen colours on a serial console; better with more.