Headless Core + Multiple Clients
Headless Core + Multiple Clients
An architectural pattern where the core capability lives in a backend (server, daemon, library) that exposes a stable wire protocol; multiple clients (CLI, TUI, web app, agent skill, plugins, language bindings) consume the same protocol and have equal access. No client is privileged. No client gets backdoor access to internal state. Adding a new client is a small project, not a fork.
This sits one layer above The Local Daemon Pattern — the local daemon is one specific embodiment, but the headless-core idea applies just as well to cloud services, libraries, and embedded engines.
The shape
Client A (CLI) Client B (TUI) Client C (web UI) Client D (agent skill)
│ │ │ │
└─────────────────────┴───────────────────────┴──────────────────────────┘
│
Stable wire protocol
(JSON over socket, gRPC, REST, etc.)
│
▼
[ Core / brain ]
Owns the data, the logic,
the connections to backends.
│
▼
Storage, networks, devices
The contract is the protocol. Everything above it is interchangeable.
Why this pattern works
The M × N → M + N collapse. Without the pattern, M clients × N backends = M·N integrations. With it: M clients + N backends = M + N. Each client speaks the protocol; each backend implements it. Adding a client doesn't touch the backends. Adding a backend doesn't touch the clients.
This is the same insight that made LSP work for editors-and-language-servers, DAP for editors-and-debuggers, MCP for agents-and-tools.
Historical lineage
The pattern is older than software. The newer history (rough order):
- Unix daemons + clients (1970s) —
cron,inetd,sshd. Clients connect to a port; the daemon is the brain. - X11 (1984) — the original "GUI server + many clients" architecture. The X server holds the display; any number of X clients (potentially over a network) talk to it. The first widespread headless-core for end-user software.
- Emacs daemon (1985 onwards) —
emacs --daemon+emacsclient. The same idea applied to a text editor. - The 2002 Bezos API mandate at Amazon — the famous internal directive: every team must expose its data and functionality only via service interfaces. Birthed AWS as a side effect.
- Headless CMS wave (Contentful 2013, Strapi, Sanity) — content backends with no built-in UI; consumed by web/mobile clients via API. Coined "headless" for this in the product world.
- LSP (Microsoft, 2016) — the breakthrough that proved a stable wire protocol could decouple N editors from N languages.
- Kubernetes API server (2014–) — every component (kubectl, controller, dashboard, helm, custom operator) is a client of the same REST API. The cluster has no "official" UI.
- MCP (Anthropic, 2024) — the agent-era restatement: a stable protocol for tools to expose capability to LLMs.
The pattern keeps being rediscovered because the M × N problem keeps being rediscovered.
Properties of a good headless core
- Stable protocol. Once a client depends on a schema, breaking it breaks the client. Versioning + additive changes only.
- No privileged client. The TUI doesn't get methods the CLI doesn't. The agent skill doesn't either. If a feature is in one, it's in all.
- Clear bucketing of operations. Each request fits in one well-defined category; new buckets require deliberate design. (mxr's four buckets: CoreMail / MxrPlatform / AdminMaintenance / ClientSpecific. lazydap's four: Session / Project / Diagnostics / ClientSpecific.)
- Boundary enforcement. Cargo dependency graph in mxr/lazydap. Module boundaries in larger systems. Network boundaries in cloud systems. The constraint is structural, not aspirational.
- Discoverable. Clients can introspect: "what can I do?" mxr exposes commands via clap's
--help; LSP hasinitializereturning capabilities; MCP hastools/list.
What this enables
- Adding clients is cheap. Want an Electron app for Mxr? Spawn the daemon, open the socket, render its JSON. ~1 weekend.
- Replacing clients is cheap. TUI can be rewritten in any framework without touching the core.
- Third parties can build. Anyone — without permission — can write a vim plugin, a web bridge, an MCP wrapper. The contract is public.
- Agents can drive it. Modern LLMs are great at composing shell commands. A CLI-shaped client surface = agent-friendly without bespoke design.
- CI / scripts work. Same surface that humans use; same surface agents use. Composable.
What this costs
- Upfront design discipline. You design the protocol before building anything. This is hard for "just ship the MVP" instincts.
- Versioning rigour. Once the protocol is in production, breaking changes need migration paths.
- Slightly more code than a monolith. A protocol layer + multiple clients is more files than one big GUI app with embedded logic.
The trade pays off as soon as you have two clients. Below two clients, it's overengineering.
When NOT to use this pattern
- Single user, single use case, no automation needs. A monolithic CLI is fine.
- Truly bidirectional, real-time UX (video conferencing, live drawing). Request/response shapes don't fit; you'd need bidirectional streaming throughout.
- You're sure you'll only ever ship one client. (You're rarely sure.)
How mxr and lazydap embody this
Mxr: daemon owns SQLite, Tantivy, providers. Clients (CLI, TUI, agent skill, web bridge) consume IPC. CLI gets no special access. TUI implements features by talking to the same socket. Per-feature non-negotiable: every TUI action has a CLI equivalent.
Lazydap: daemon owns DAP adapter, session, breakpoints. Same client setup — CLI, TUI, agent skill, all peers. Even the AI integrations (future Tier 1+ capabilities) are external clients, not core features.
The pattern is the architecture. The daemon, the protocol, the bucketing, the dependency graph — all in service of "headless core, multiple clients."
Naming this in conversation
The pattern doesn't have one canonical name. Useful labels:
- "Headless core" — emphasises the missing UI in the core
- "Client-agnostic core" — emphasises that the core treats clients equally
- "Daemon + clients" — Unix-old, accurate, slightly archaic
- "API-first design" — emphasises the protocol as the product
- "Protocol-oriented architecture" — emphasises the wire format
- "LSP-style architecture for X" — when X is non-editor (e.g., "DAP is LSP-style for debuggers")
I lean on "headless core + multiple clients" in writing because it spells out both halves. See Client-Agnostic Cores for the synthesis.
See also
- Client-Agnostic Cores — the broader synthesis
- The Local Daemon Pattern — the specific embodiment in mxr/lazydap
- How Daemons Work — synthesis from the daemon side
- Plumbing and Porcelain — adjacent metaphor
- LSP and the X-Protocol Family — the lineage
- Mxr · Lazydap — concrete instances