A sovereign AI assistant running on infrastructure we own
A long-running agent on a single server we control, with its memory on local disk, its secrets in an encrypted store, scoped access per user, and a documented path to move or shut it down.
Conversation history held by a third-party platform
None
All memory, logs and state sit on disk on a host we own. The model API sees a request and a response, and retains no account of the system.
Restarts survived with state intact
800+
Counted from the service boot counter. Identity, memory and open threads are reconstructed from disk on every start.
Users with scoped access
2
One full-scope, one restricted to a single company's data. Enforced in the application, not by convention.
Context
We build AI systems that have to run inside somebody else’s security boundary. It would be poor form to argue for that while running our own operations on a stack of hosted assistants that hold our client context, our credentials and our internal conversations on infrastructure we cannot inspect.
So we built our own. It has been running continuously for over a year, it does real work, and it is the system we point at when a client asks what sovereign AI looks like in practice rather than on a slide.
The constraint
The requirement was not “no cloud”. Frontier models are worth paying for, and pretending otherwise leads to a worse assistant and a smug architecture diagram. The requirement was narrower and more useful:
The state stays with us. Conversation history, memory, credentials, notes on live commercial matters. None of that goes into a platform whose retention policy and access model can change with a product update.
The model is a replaceable component. Calls go out over an API. Nothing about the system’s identity or memory lives at the other end of that call, so the provider can be swapped without a migration.
The access is scoped per person. Two people use it, and one of them should only ever see one company’s data. That is a permission model, not a request in the prompt.
What we built
A Python service under systemd on a server we rent and administer. Memory sits in two layers: structured markdown files that are readable, editable and version-controlled, and a local vector store for semantic recall across everything it has ever been told. Both are on local disk. Both are in the backup set.
Secrets moved into a Fernet-encrypted vault early, after an audit found API keys sitting in plaintext in memory files. That is the sort of thing that happens when a system grows organically, and the reason we now build the secret store before the feature that needs it.
Access control is enforced at the message boundary. The restricted user’s requests are scoped before the model is ever called, because a permission implemented as an instruction to a language model is not a permission.
Backups run six-hourly on the host with a daily encrypted sync off-site, moving to cold storage after thirty days. There is a written recovery procedure, and it has been used.
How we measured it
Not by accuracy benchmarks, which would tell us very little about an assistant with an open-ended job. We watch three things.
Continuity: does it come back from a restart with its memory and its open threads intact. Roughly eight hundred boots so far, and the failures we have had were in the reconstruction path rather than the storage, which is the correct place for them to be.
Containment: can we demonstrate, on request, where every piece of state lives. We can, and the answer fits on one page.
Usefulness against real work: it manages infrastructure, drafts and files real documents, and runs monitoring. When it goes down we notice within the hour, which is the only benchmark that has ever meant anything.
Outcome
We run our own operations on a system we can open up, move, or switch off. When we tell a client that their AI system should not depend on a vendor’s continued goodwill, we are describing something we did to ourselves first.
It has also been an unusually honest teacher. Every failure mode we now design against in client work, the credential in the wrong file, the permission that was really a suggestion, the fix that sat on disk while the running process carried on with the old code, we met here first, on our own time and our own money.
What we would do differently
We treated memory as a storage problem for far too long. It is a retrieval and selection problem. Writing everything down is easy and nearly useless; deciding what is worth surfacing at the moment it matters is the actual work, and we rebuilt that layer three times before it behaved.
We would also build the observability before the capability. For a long stretch the system was doing things we could only reconstruct afterwards from logs. On a client engagement that gap is not merely inconvenient, it is the thing the audit asks about.
Start with the constraint.
Most of these projects are shaped by what you cannot do rather than what you want. Data that cannot leave the estate, a model you cannot host with a third party, a decision somebody has to justify to a regulator. Tell us yours and we will say honestly whether we can work inside it.