TacidOS
Developers
Documentation

Decision 0061

61. An integration is a principal anybody may claim, because claiming it only subtracts

Architecture decision record 0061: 61. An integration is a principal anybody may claim, because claiming it only subtracts

Status
accepted
Date
2026-09-07

Status: accepted. Builds the MCP server ADR-0018 decided and left unbuilt.

Context

ADR-0018 settled that Tacid exposes itself as an MCP server so that other agents can use the machine, “each under its own principal and policy”, and that MCP is never equal to root. The policy engine has been ready for it since: Principal::RemoteAgent is capped at consult whatever the owner’s autonomy level says, counts as an agent for the hard invariants, and is refused the handful of daemon methods reserved for a person at the machine.

What was missing was the way to become one. The daemon reads a connection’s principal from the socket’s peer credentials, which is right — it is the one claim on a Unix socket that cannot be forged — and gives a person’s connection a person’s authority. An editor plugin or another agent started by that person inherits it, which is exactly what must not happen: the whole point of per-integration policy is that the thing plugged in is not the person who plugged it in.

The obvious answers are all worse. A separate socket per integration needs the system layer to know every integration in advance. A key per integration needs enrolment, storage and revocation for a claim the socket already answers. A token needs somewhere to keep it.

Decision

A connection may say integration.hello { name } once, and is another agent from then on. Nobody has to prove anything to make that call, because the only thing it can do is take authority away.

  1. It only ever subtracts. After the call the connection is capped at consult, is treated as an agent by the invariants rather than as a person, and is refused every method a person is reserved — the genesis build and forgetting a memory among them. There is no request that undoes it and none that widens what it left. A claim that can only cost the claimant needs no proof: the socket’s peer credentials still bound what this connection could ever have been, and this is the connection choosing to be less.

  2. Once per connection. A second name is refused. A connection that could re-introduce itself could carry one integration’s request under another’s name, and the record of which of them asked would be a record of nothing.

  3. The name is read as the untrusted string it is. It is written into this machine’s record, so it must be something a person can read: one to sixty-four characters, letters, digits, spaces and -_.. A name a person could not read is refused rather than quietly rewritten, because a per-integration record whose names are mangled is not one.

  4. Answered by the connection, never by the service. integration.hello is about who is talking rather than about the machine, so the daemon answers it before dispatch. Nothing inside the service has any business deciding who it is talking to.

  5. tacid mcp is a translation, not a second implementation. MCP is JSON-RPC 2.0 over a pipe, which is what this machine already speaks over a socket. The bridge says integration.hello before it reads a byte from the pipe — a bridge that narrowed itself afterwards would have a window in which it was the person who started it — and then relays: an MCP tool call becomes the typed daemon method behind it, and the daemon’s own answer goes back as the JSON it is. No prose is composed. The caller is another agent, and rendering this machine’s facts into sentences would be deciding how somebody else’s model should read them.

  6. Questions only, in the first surface. Facts, doctor, generations, policy, history and memory search. An integration that wants a change asks in a session like anything else, and a person says yes. A tool call that quietly became a transaction would be the whole policy sidestepped by the shape of a protocol — and the consult cap would catch it, which is a reason to have the cap and not a reason to lean on it.

  7. A refusal comes back as a tool result, not a protocol error. A policy that declined something is this machine working. An agent told “internal error” learns the opposite, and the next thing it does is retry.

Consequences

  • An integration can never be more than the person who started it, which the socket already guaranteed, and is deliberately less. The failure mode of the whole design is a person’s connection being treated as an integration’s, which costs authority rather than granting it.
  • The machine’s record names which of the things plugged into it asked, because the name travels with every transaction the integration composes.
  • Nothing here is MCP-specific. A future editor adapter or a remote transport says the same thing on the same socket; tacid mcp is the first caller rather than the mechanism.
  • The MCP surface being read-only means an integration cannot yet propose. That is a real limitation and it is deliberate for a first landing: proposing takes a session, and a session opened by an integration is a design question — whose memory does it write, whose scribe does it reach — that is worth answering on its own rather than as a side effect of a protocol bridge.
  • Six questions is a small surface. Adding one is adding a row to a list and a method name; the test that every question names a read-only method is what keeps the list from growing a verb.

Alternatives rejected

  • A socket per integration. The system layer would have to know every integration before it exists, and an integration is something a person installs after their machine does.
  • A key per integration. Enrolment, storage, rotation and revocation, for a claim that only ever reduces what the holder can do. The cost is real and the thing bought is already free.
  • Trusting the peer credentials alone and giving an editor its person’s authority. This is what happens without the call, and it is the thing ADR-0018 said must not.
  • A second daemon speaking MCP on a port. Another process, another surface, another set of credentials, and a network listener on a machine whose whole egress story is one door.