Decision 0005
JSON-RPC 2.0 over Unix sockets everywhere; no polkit on the Tacid path
Architecture decision record 0005: JSON-RPC 2.0 over Unix sockets everywhere; no polkit on the Tacid path
Context
Clients, tools, helpers and remote bodies need one wire dialect. The TUI and the shell must have feature parity without duplicated logic.
The three plans
- Grok: local IPC, “JSON-lines or capnp; keep it boring”.
- Fable: Unix socket, JSON-RPC; MCP for tools.
- Codex: D-Bus or a tightly defined local IPC; polkit where appropriate.
Decision
JSON-RPC 2.0, newline-delimited, over Unix domain sockets, with file descriptors passed by
SCM_RIGHTS for audio, pseudo-terminals and large blobs. One tacid-protocol crate with generated
JSON Schema; a separate minimal crate for the stem. Authentication by SO_PEERCRED and a per-boot
token. MCP is JSON-RPC 2.0 as well, so one codec serves clients, tools and helpers. The protocol is
transport-agnostic so the same messages travel over SSH or WireGuard to a remote body (ADR-0030).
The protocol carries UI state (plans, live tool state, diffs, pending approvals, badges); clients are pure renderers; parity follows.
polkit is not used between Tacid processes: it requires a session agent that is absent on a tty,
a serial console and a headless node, adds a second policy language, and has its own history. It
remains available for udisks-class peers. D-Bus via zbus only for desktop interop.
Consequences
- One codec, one schema, one place to version.
- Streams are notifications; binary never travels base64.
- Remote transport is a wrapper, not a redesign.
Alternatives rejected
- D-Bus for the core: a bus daemon dependency on the boot-critical path and a second type system.
- Varlink: elegant and systemd-native, but a weak Rust story and a second codec.
- gRPC/Cap’n Proto: heavier tooling for no gain on a local socket.