From 8a021ede86f109cfaa82f634f18743b230622bd3 Mon Sep 17 00:00:00 2001 From: Flint <263629284+tps-flint@users.noreply.github.com> Date: Sun, 5 Jul 2026 22:01:27 -0700 Subject: [PATCH] =?UTF-8?q?docs:=20add=20DESIGN.md=20=E2=80=94=20adopter-f?= =?UTF-8?q?acing=20design=20invariants?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Distills PRINCIPLES.md's CLI one-liner into an adopter-facing doc in the product repo: signed-everything (Ed25519 identity, provenance over claims), self-healing over keepalive (loud detection + recovery, never mask a root cause), idempotent + verifiable (retry-safe, confirmed through a channel that can't lie), the dispatch->review->merge loop as the unit of work, and a Trust boundaries section pulling the three security invariants out of CONTRIBUTING.md. Links from README's positioning intro. Light cleanup alongside: - Reconcile the two divergent ARCHITECTURE.md / docs/architecture.md docs: made docs/architecture.md (the fuller Agent OS kernel / layered model, already the doc docs/branch-office.md cross-links as "the broader system model") the single authority. Root ARCHITECTURE.md is now a thin pointer. Folded its non-duplicated content (Communication Channels / three-channel model, tps.yaml mail-handler-pipeline manifests, Milton's Sandbox image) into docs/architecture.md instead of dropping it. - Fix CONTRIBUTING.md clone URL: tpsdev-ai/tps -> tpsdev-ai/cli (repo is actually tpsdev-ai/cli), and point its security-boundaries note at DESIGN.md for the rationale. --- ARCHITECTURE.md | 47 +++------------------ CONTRIBUTING.md | 8 ++-- DESIGN.md | 97 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + docs/architecture.md | 29 +++++++++++++ 5 files changed, 138 insertions(+), 45 deletions(-) create mode 100644 DESIGN.md diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 2d174c0..27644ea 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,44 +1,9 @@ # TPS Architecture -TPS is designed to securely manage a fleet of AI agents distributed across multiple "Branch Offices" (remote VMs or local Docker sandboxes). +Moved to **[docs/architecture.md](docs/architecture.md)** — the Agent OS kernel / +layered model (Trust Model, Mail Bridge, isolation strategies, state persistence) is the +single authority for TPS's architecture. -## Topology: Hub and Spoke - -TPS uses a strict hub-and-spoke topology. -- The **Host** (e.g., your laptop) is the hub. -- The **Branch Offices** (e.g., cloud VMs) are the spokes. -- Branches **do not** talk directly to each other. All cross-branch communication routes through the Host's mail relay. -- The Host initiates connections to the Branches. This solves NAT traversal issues since the Host is often behind NAT, while Branch VMs usually have public IPs. - -## Wire Protocol: Noise_IK over WebSocket - -To securely bridge the Host and Branch Offices, TPS uses the **Noise Protocol Framework** (specifically `Noise_IK`). - -1. **Transport**: WebSockets (`wss://`). This allows traffic to pass seamlessly through HTTP proxies and TLS terminators (like `exe.dev` or `ngrok`), avoiding the need for raw TCP ports. -2. **Authentication**: Mutual authentication using static Ed25519 keypairs. The `_IK` handshake allows the Host to send encrypted payload data in the very first flight (0-RTT for known peers). -3. **Key Custody**: The Host's private key NEVER leaves the Host. Branch Offices generate their own keys during `tps branch init`. - -## The Three-Channel Model - -Agents in TPS do not dump code, text, and data into a single massive LLM context window. Communication is strictly segregated: - -1. **Mail**: For control plane messages, status updates, commands, and webhook events. Sent as JSON envelopes, Ed25519-signed, and delivered via the Noise_IK transport. -2. **Git**: For artifacts. Code, specs, and documentation are committed and pushed. Agents pull the repo to see the state of the world. -3. **APIs**: For external data (e.g., web searches, tool use). - -## Mail Handlers & Manifests (`tps.yaml`) - -Each agent directory contains a `tps.yaml` manifest. - -The Branch Daemon discovers these manifests and wires up a **mail handler pipeline**. When mail arrives, the daemon checks the sender and body against the handler's `match` rules. If matched, the daemon executes the handler script, providing the mail content via `stdin` and metadata via environment variables. - -Handlers can return plain text (to reply) or JSON envelopes (`{"action": "forward", "to": "other-agent"}`) to route messages locally within the branch. - -## Sandboxing and Process Isolation - -![Milton's Sandbox](docs/media/miltons-sandbox.png) - -TPS provides multiple layers of isolation for agents, ensuring they only access what they need: -- **Docker Sandboxes**: For local agents, TPS provisions secure Docker microVMs (Branch Offices) that have no direct host filesystem access. -- **Remote VMs**: For true physical isolation, Branch Offices can be deployed on remote cloud VMs. -- **`nono` System Call Filtering**: Host agents execute commands under `nono` profiles, providing read-only boundaries and strict system call filtering to prevent root pollution or unauthorized network egress. +For the wire-protocol detail of remote Branch Offices specifically (Noise_IK handshake, +SSH-tunnel transport, provisioning), see [docs/branch-office.md](docs/branch-office.md). +For design rationale — *why* TPS is built this way — see [DESIGN.md](DESIGN.md). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a1902c3..36a8ca9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -8,8 +8,8 @@ TPS is built with TypeScript and [Bun](https://bun.sh/). ```bash # Clone the repository -git clone https://github.com/tpsdev-ai/tps.git -cd tps +git clone https://github.com/tpsdev-ai/cli.git +cd cli # Install dependencies bun install @@ -23,9 +23,9 @@ bun test ## Architecture Notes -Before contributing, please read the [ARCHITECTURE.md](ARCHITECTURE.md). TPS uses a strict hub-and-spoke topology and isolates communication across three channels (Mail, Git, APIs). +Before contributing, please read [docs/architecture.md](docs/architecture.md) (the system model) and [DESIGN.md](DESIGN.md) (why it's built this way). -When modifying the branch daemon or transport layers, keep the following security boundaries in mind: +When modifying the branch daemon or transport layers, keep the following security boundaries in mind — see [DESIGN.md § Trust boundaries](DESIGN.md#trust-boundaries) for the rationale: - **Never expose the Host's private key**. - **Always validate inputs** on cross-boundary messaging. - **Fail closed** on authentication or permission errors. diff --git a/DESIGN.md b/DESIGN.md new file mode 100644 index 0000000..84462b3 --- /dev/null +++ b/DESIGN.md @@ -0,0 +1,97 @@ +# TPS — Design Invariants + +This is TPS's design DNA: the invariants that decide what we build and, just as +importantly, what we refuse to build — not a feature list (see [README.md](README.md)) +and not an architecture reference (see [docs/architecture.md](docs/architecture.md)). If +you're adopting TPS, extending it, or wondering why it works a certain way, this is the +page that answers "why," not "what." + +TPS exists to let a fleet of agents — on your workstation, in a Docker sandbox, on a +remote VM — coordinate without stepping on each other or on you. Every invariant below is +in service of that: agents you can trust because their actions are provably theirs, +infrastructure that recovers instead of quietly rotting, and a unit of work small enough +to review honestly. + +## Signed everything + +Every agent identity in TPS is an Ed25519 keypair, generated on the machine it belongs +to and never exported. Mail envelopes are signed. Branch Office pairing is a mutual +Noise_IK handshake between static keys, not a shared secret or a bearer token. When a +Branch relays mail back through the Host, the relay overwrites the `from` field with the +verified sender identity rather than trusting whatever the message claims — a compromised +Branch can flood or misbehave, but it cannot forge who it is. + +This is the same principle applied consistently everywhere in the system: trust flows +from **verified provenance** — a signature that checks out, a handshake that completed — +never from "who claims what." An agent proves itself cryptographically or it doesn't get +to act as itself. There is no middle tier of "probably fine, no key required." + +## Self-healing over keepalive + +A stuck mail consumer, a stale watcher, a failed delivery — these are expected failure +modes in a system with this many moving daemons, not exceptions. The invariant is that +the system detects and recovers from them **itself**, and does so loudly: an alert plus a +recurrence count, so a restart never quietly papers over a root cause that's getting +worse each time it fires. + +A `KeepAlive: true` plist entry that respawns a crashed process is not the same thing as +self-healing — it hides the failure instead of surfacing it. The bar is higher: watchers +that notice their own staleness and kick themselves, daemons that drain a backlog on +reconnect instead of losing it, connection state that's inspectable so "is this actually +healthy" is a `status` command, not a guess. A fix that needs a human to notice something +is broken before it can be applied isn't done — it's deferred, and deferred failures in +an agent fleet compound. + +## Idempotent + verifiable + +Every action in TPS is safe to retry. Mail delivery is deduplicated and replay-safe — +messages carry an issuing UUID and timestamp, subscribers catch up via cursor-based +replay, and re-running a drain doesn't double-deliver. Nothing in the critical path +assumes it will only ever run once. + +Retry-safety only matters if you can tell whether the retry — or the original action — +actually worked, and that confirmation has to come from a channel that can't lie. A +GitHub PR's review state, a `tps office status` health probe, an actual served response +— these are ground truth. An agent's own "done" is not: agents self-report success on +work that's stubbed, partial, or silently failed, and a design that takes a self-report +at face value is building on sand. Verification through an independent channel is not an +optional nicety layered on top of the CLI — it's the property that makes autonomy safe to +extend in the first place. + +## The dispatch → review → merge loop is the unit of work + +Work moves through TPS as: one task, dispatched with everything the recipient needs to +execute it (a spec, a branch, an output contract) — never a vague pointer requiring +back-and-forth to clarify. The agent does the work and opens a PR; it does not merge its +own change. An independent review — architecture on one lane, security on another — is +the gate, not a courtesy step. Only a change that clears CI and clears review merges. + +This loop is deliberately small and deliberately serial: one task in flight per agent, +not a queue of half-started work. The unit of work is the whole loop, not the code — +a task without its review isn't finished, it's paused mid-flight, and pretending +otherwise is how unreviewed shortcuts accumulate in a fleet no single human is reading +line-by-line. + +## Trust boundaries + +TPS draws one hard line: the **Host** (where your identity, mail, and context live) and +everything else. A Branch Office — Docker sandbox or remote VM — is untrusted by design, +even though it's running your own agent. Three rules enforce that boundary, and they are +non-negotiable regardless of what's convenient at the call site: + +- **Never expose the Host's private key.** It is generated once, lives only on the Host, + and every cross-boundary interaction is a signature or a handshake, never a + key handoff. +- **Always validate input at the boundary.** Every message crossing from a Branch into + the Host's mail relay passes a validation gate — identity is overwritten to the + verified sender, origin is stamped, size and quota are enforced — before it's ever + written to a recipient's inbox. Nothing from an untrusted side is trusted just because + it parsed as JSON. +- **Fail closed.** An authentication failure, a permission error, a malformed + cross-boundary message — any of these abort the operation. TPS never falls back to an + unauthenticated or partially-validated path to keep something moving; a blocked + action is recoverable, a silently-downgraded trust boundary is not. + +A Branch has no direct access to the Host's filesystem, its `~/.tps/context`, or another +agent's mail — the Mail Bridge is the only door, and it's the door these three rules +guard. diff --git a/README.md b/README.md index 5b196be..04c8956 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ Most agent frameworks assume all agents run in the same memory space. TPS assume > "I have eight different bosses right now. So that means that when I make a mistake, I have eight different people coming by to tell me about it." — Make your agents communicate through a single, auditable mail interface instead. +See **[DESIGN.md](DESIGN.md)** for the invariants behind that — why identity is signed, why failures self-heal instead of hiding, and why every task moves through review before it merges. + ### What You Get - **Identity & Keys** — Ed25519 keypairs per agent. Agents prove who they are cryptographically, not by env var. diff --git a/docs/architecture.md b/docs/architecture.md index deb73bf..b130f7c 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -25,6 +25,33 @@ It uses a layered design: **TPS Reports** define the agent's identity and capabi --- +## 0. Communication Channels + +Agents in TPS do not dump code, text, and data into a single massive LLM context window. +Communication is strictly segregated across three channels, each suited to a different +kind of payload: + +1. **Mail** — control plane messages, status updates, commands, and webhook events. Sent + as Ed25519-signed JSON envelopes (see [§2 The Mail Bridge](#2-the-mail-bridge-ipc) for + the trust boundary they cross, and [docs/branch-office.md](branch-office.md) for the + Noise_IK wire transport used between Host and a remote Branch). +2. **Git** — artifacts. Code, specs, and documentation are committed and pushed. Agents + pull the repo to see the state of the world rather than holding it in-context. +3. **APIs** — external data (web searches, tool use, third-party services). + +### Mail Handlers & Manifests (`tps.yaml`) + +Each agent directory carries a `tps.yaml` manifest — this is the "TPS Report" in the +System Layers diagram above (the Definition Layer). The Branch Daemon discovers these +manifests and wires up a **mail handler pipeline**: when mail arrives, the daemon checks +the sender and body against the handler's `match` rules, and on a match executes the +handler script, passing the mail body on `stdin` and metadata via environment variables. +A handler can return plain text (sent back as a reply) or a JSON envelope +(`{"action": "forward", "to": "other-agent"}`) to route the message on within the branch +without a round-trip to the Host. + +--- + ## 1. The Trust Model TPS operates on a **Host/Branch** security model. @@ -71,6 +98,8 @@ This ensures that even a compromised Branch Agent cannot forge its identity or f ## 3. Isolation Strategies +![Milton's Sandbox](media/miltons-sandbox.png) + TPS employs "Defense in Depth" using two different isolation technologies. ### Host Isolation: `nono`