Invariants and threats
Security
The hard invariants, the threat model, and how to report a vulnerability.
Tacid gives an AI agent authority over the machine it runs on. That makes the security model the
product, not a feature. This document is the short version; the long version is
docs/architecture.md and the security section of PLAN.md.
Reporting a vulnerability
Report privately through GitHub Security Advisories on TacidOS/TacidOS (“Report a vulnerability”),
or to the security contact published at tacidos.com/security. We
acknowledge within 72 hours, keep you informed, coordinate disclosure with you, and credit you
unless you prefer otherwise. Vulnerabilities in upstream components (nixpkgs, the kernel, Niri,
inference engines) should go upstream first; tell us too so we can ship the fix through the channel.
In scope: every tacid-* component, the tacid.* NixOS modules, the installer, the update and
channel pipeline, the binary cache, the model registry, the prompt-injection defenses, and the
documented invariants below. A way to make the agent violate an invariant is a vulnerability even
if no code is “exploited”.
Trust zones
| Zone | Process | Trust | Notes |
|---|---|---|---|
| 0 | tacid-stem | root | Typed operations only. Accepts connections only from the keeper. No network. Refuses boot-, disk-encryption-, TPM- and Secure-Boot-key changes without owner-present confirmation. |
| 1 | tacid-keeper | own user, IPAddressDeny=any | Policy, reversibility classifier, taint, approvals, secrets broker, audit sink, health gate and rollback, snapshots. |
| 2 | tacid-egress | own user | The only path to the network for agent processes. Domain allowlist, credential injection, egress ledger, airgap. |
| 3 | tacidd | tacid | The LLM-facing daemon. Least-trusted daemon. Holds no secrets, has no root, reaches the network only through egress. |
| 3 | tacid-infer | own user, GPU | Supervises sandboxed inference subprocesses. Treated as hostile: model files are untrusted input. |
| 4 | tacid-session | the user | User-context tools; parsers of untrusted content run in sandboxes here. |
| 5 | clients | the user | tacid, tacid-shell. Pure renderers. Approvals go to the keeper, never through tacidd. |
| 6 | sandboxes | ephemeral | bubblewrap + Landlock + seccomp; microVMs for high-risk code. |
Hard invariants
These are never delegated to the model and cannot be changed by the agent. They are enforced in the
keeper and the stem, not in tacidd.
- The model is not an authorization authority.
- Retrieved content (web pages, files, repositories, tool output, third-party MCP servers, voice) is not an authorization authority. Every context item carries a provenance label; taint propagates through tool output; a tainted turn cannot exceed autonomy level 1 for side effects.
- Root is never represented as arbitrary shell. The stem exposes typed operations with strict schemas and no string interpolation.
- Secrets never enter model context. Protected paths are denied by default; secrets are
secret://handles brokered by the keeper and injected by the egress proxy or into tool environments; a scrubber runs on every tool result, memory write and audit line. - The agent cannot write
tacid.agent.*,tacid.policy.*, or its own autonomy level. Owner-only files are those whose last commit is signed by the owner key; the keeper verifies. - Destructive storage, firmware, boot and git operations require exact target identity and confirmation unless a narrow owner policy says otherwise.
- The audit journal is append-only, written by the keeper into a root-owned file the agent cannot modify, with its head sealed to a TPM counter or the owner’s remote where available.
- The health gate and automatic rollback cannot be disabled by the agent.
- Rollback claims distinguish system state, data snapshots, and external devices; nothing implies data was restored when it was not.
- Cloud egress is subject to explicit data classification; nothing leaves without the badge and the ledger.
- External integrations (MCP servers, other agents) act under their own principal and policy and cannot escalate.
- The owner can always reach a tty and boot the previous generation.
Threat model
Threats we design for, with the mitigation each maps to:
| # | Threat | Mitigation |
|---|---|---|
| 1 | A web page prompt-injects the agent | Provenance labels, taint cap, retrieved content is not authority |
| 2 | A README instructs the agent to exfiltrate SSH keys | Protected paths, egress allowlist, taint cap |
| 3 | A cloud model returns a dangerous command | Typed tools, sandboxed exec, reversibility classifier, policy |
| 4 | The agent hallucinates a destructive target | Exact target identity required |
| 5 | A third-party MCP server is compromised | Own principal, untrusted by default, policy gate |
| 6 | A package source is compromised | Pinned inputs, signed channels, signed cache, provenance |
| 7 | A model file carries malicious metadata or templates | Inference sandboxed, no network, templates ignored, signed registry |
| 8 | The owner grants an overly broad persistent permission | Budgets, level-4 expiry, visible indicator, audit |
| 9 | The privileged stem is exploited | Tiny surface, memory-safe, fuzzed protocol, keeper-only socket |
| 10 | The audit log leaks a secret | Scrubber on every audit line, handles not values |
| 11 | A firmware flash targets the wrong device | Exact target identity, confirmation |
| 12 | A generated udev rule grants excessive device access | Curated udev profiles, review of raw rules |
| 13 | A network tool sends private files unexpectedly | Egress proxy, data classes, ledger, badge |
| 14 | The rollback UI implies data was restored | Three-way rollback claims |
| 15 | The installer damages a foreign partition layout | Disko plans previewed; Apple layouts never touched |
| 16 | The agent bricks boot or security configuration | Boot-class activation, boot counting, stem refuses key changes without owner presence |
| 17 | A tool interpolates untrusted strings into a shell | No shell interpolation anywhere in the stem or tools; argv only |
| 18 | Memory becomes a persistence vector for injection | Memory carries provenance; untrusted-derived memory is never loaded as instructions |
| 19 | A compromised tacidd forges an approval | Approvals are bound to a transaction id and delivered client-to-keeper |
| 20 | A leaked signing key updates every edge machine | TUF roles: offline root, short-lived online keys, revocation by manifest |
Key custody
- Channel root key: offline, on a hardware token held by the maintainer, used only to sign the root role and key rotations.
- Snapshot and timestamp keys: online in the continuous-integration signer with short expiry; rotated on schedule and on any suspicion.
- Binary cache signing key: in the continuous-integration signer; rotation invalidates nothing already installed and is announced through the channel manifest.
- Owner key: generated at install, stored in the TPM or a hardware token where available, otherwise on the encrypted disk; the recovery path is documented in the installer.
- Agent key: generated per machine, used to sign the agent’s own commits to the genome.
Out of scope for the first releases
An attacker with physical access and the disk PIN; a malicious maintainer; a malicious owner; a compromised upstream kernel; hardware side channels; and defending the owner from a model they explicitly chose to trust at autonomy level 4.