Field note

Building an operator OS for one

A hub, a handful of spokes, and one rule I kept enforcing in the shape of the thing: the scary actions are structurally unavailable. The trust is in the architecture, not the vibes.

The itch came from having too many moving parts. A pile of repos. AI agents running in three or four different tools, each forgetting what the others just did. A real inbox with real people in it. Notifications from everywhere, blurring into one undifferentiated hum. I wanted to operate my own life (track it, automate the boring parts, let an assistant help) without renting the whole thing back from a stack of SaaS dashboards that each own a slice of me and phone home about it.

So I started building, one frustration at a time. I didn't sit down and design a platform. I built one thing, then another, and at some point noticed they were all talking through the same little state layer and the shape had a name.

The shape: a hub and its spokes

What it converged on is a hub and spokes. At the center is a small SQLite-backed state spine, the one place that knows the current truth: what each agent did last, what's handed off to whom, what shipped, what's still open. Around it sit the pieces that do actual work, and they only talk to each other through the hub:

Each piece writes to a narrow surface and reads from a defined source. The hub enforces that ownership: a tool can only write the slices of state it actually owns. The command center reads only from the auditor's output. The router routes but never decides. That was the whole intent: composability, so I can replace one piece without pulling a thread that unravels everything.

It all stays on the machine

The first non-negotiable: it's local-first, and the data never leaves. Everything runs on one laptop. The control plane binds to the loopback interface on purpose; it is not reachable from another machine or my phone, and that isn't a limitation I'm apologizing for, it's the point. Loopback used to be the whole authentication story, and I used to say so flatly. It's now the outer wall: inside it, the daemon issues separate credentials for me, for the assistant, and for the browser console, so "who is asking" is a checked fact rather than an assumption. There's still no account, no tenant, no remote surface to harden, because there's no remote surface at all.

That single decision makes everything downstream calmer. I'm not trusting a vendor's data-handling promises because there's no vendor in the path.

The interesting part is trust

The part I actually care about is trust. The moment you let an assistant touch a system that does real things (send mail, mutate records, talk to the outside world) "the model is usually careful" stops being good enough. Usually isn't a control. So I pushed the trust out of the model's good behavior and into the structure of the system, in three moves.

Read-only by default. The overwhelming majority of what the system exposes to an agent can only read. Status, history, what's open, what's stale: all queryable, none of it dangerous. It is safe to let an agent drive, because the verbs that could hurt me aren't on the table for it to pick up in the first place.

Approval-gated action. For a long time there was exactly one place this system reached into the real world: sending mail. There are now two. The second, materializing a calendar event from a plan I've already accepted, was admitted under a written charter that fixes what the system may ever do on its own, and that list grows only by amending the charter in writing, never by a pull request that quietly ships a new verb. Every action path is split down the middle the same way. The assistant can prepare: pull the context, compose the reply, stage the event. It cannot execute. Executing is a separate, explicit, human step that I take, with a confirmation token the assistant never holds and can't fabricate. Mail is still the only action that can't be undone, which is why it grew this ceremony first; the calendar action inherited the ceremony whole. There is no code path where an agent fires real mail at a real person, or writes to my calendar, on its own.

One honesty note on "can't fabricate," because flat structural claims are exactly what this site says to distrust. The mint refuses assistant-role callers by construction, verified against the code, but "can't" ultimately rests on the operator's own credential staying out of the agent's reach, and a sibling token gate of mine was once defeated at exactly that kind of boundary. So read the claim at its true strength: role-gated at the mint, with one standing assumption I keep re-checking rather than a theorem I get to retire.

The assistant drafts. I send. That sentence is a whole security model, and it's enforced by the wiring, not by anyone remembering to be careful.

Local-model drafting. And those drafts get written by a model running locally, through Ollama. My personal mail doesn't take a trip to a cloud API just to get a first pass. The draft is composed on the same machine the mail lives on, by a model I'm running myself. Cloud models are wonderful and I use them constantly, just not as a default middleman sitting between me and my own correspondence.

Knowing what's fresh before you trust it

The hub federates a read-only view across all the spokes, and the failure mode I worried about most was subtle: a source answering confidently with stale data. Each source carries a freshness envelope, so the hub knows age before it trusts the answer and degrades loudly when a source goes cold. The mechanics live in the freshness-envelope note; here the important point is that the operator OS is allowed to say "I don't actually know right now."

The payoff

Put it together and the thing I rely on isn't the agent choosing to behave. It's that the architecture makes the dangerous moves structurally unavailable, every real-world write human-gated, the private stuff local, and the stale stuff self-announcing.

Trust enforced by shape, not by vibes. I don't have to believe the assistant is careful. I just have to be right about the wiring.

And the quieter, day-to-day win underneath all of it: I stopped having to remember. When an agent asks "what's the state of X," the answer is in the hub. When I want to know what's healthy across my projects, it's in the command center. When something genuinely needs me, it routes to the right place instead of making me scan logs for it. The system carries the state so I don't have to.

I'm still finding gaps, places where two spokes should talk and don't quite yet. That's fine. The goal was never a finished platform. It was a system honest enough, and safe enough, to actually let drive.

Adjacent reading

← Field notes · RSS