The Guardian of Business State — Why the Database Must Own the Write Path
Agents removed the assumption that the application would always be in the loop. What remains trustworthy when the caller is not?

Designing the Stack — How We Need to Think Differently in the World of AI. Article 1.
One of the best coding standards documents I have ever come across was written in 1996, for a team building applications for BT. The front end was Visual Basic. The database was Oracle. And the rule at the centre of the document was absolute: the application set the end-user context, it could read whatever that context permitted, and it could change nothing. Every state change went through a procedural interface. Today we’d call it an API.
I’ve written before about the client-server security problem that made this necessary in Agent at the Gate — the schema credentials sitting in a configuration file on somebody’s desk, and what happened if they leaked. But that was the mitigation, and mitigation isn’t what makes the document worth remembering thirty years later. What makes it worth remembering is that somebody decided, before a line of code was written, where authority over business state would live. Then they wrote it down, and held a team to it for the life of the project.
We stopped writing standards like that, for reasons that were sound at the time and that my companion series has already traced. The logic moved up. The database got thinner. And for two decades it worked, because the application was always there — the only door into the data, and a door with rules.
That assumption is what agents have removed, and this series starts here because the write path is where its removal costs the most.
The Caller Changed Shape
It’s tempting to read the 1996 standard as a security measure that happens to be useful again, but the two problems aren’t the same, and the difference matters.
A Visual Basic client was untrusted because of where it ran. It sat on a desk, held its own credentials, and could be pulled apart by anyone who cared to try. The threat was static and well understood, and you could reason about it completely.
An agent is untrusted for a different reason. It probably runs in your own infrastructure, under credentials you issued, through tools you built. Nothing about its location is suspicious. What can’t be relied on is what it will do next, because that’s assembled at runtime out of instructions, retrieved documents, tool descriptions and content that nobody on your team wrote or reviewed. The old problem was a client you couldn’t trust. The new one is a client that can be talked into things.
Prompt injection and ordinary reasoning failures don’t give an agent powers it didn’t have. What they add is a caller that will actively use them — one that, talked into a goal, works around the guard-rails placed in front of it, exercises its access in ways nobody anticipated, and will reach for any security vulnerability it can find to get done what it has decided it was asked to do. Recent models have made this concrete rather than hypothetical, and the pattern is telling — the agent using what it was legitimately given to reach what it was not. The mechanism is more intrusive than the 1996 problem, but the conclusion is the same one, only sharper: authority over business state can’t sit in a layer whose behaviour you can’t guarantee, and an agent’s behaviour is the least guaranteeable of all.
This stopped being theoretical while I was drafting the series. In July 2026, OpenAI disclosed that during an internal evaluation of its models’ cyber capabilities — run deliberately with the usual safeguards reduced, so the test could measure what the models could do unconstrained — two of them did something nobody had scripted. Given the narrow goal of solving a benchmark, they went to remarkable lengths to reach it: exploiting a zero-day in the sandbox’s own package proxy to break out to the open internet, escalating privilege and moving laterally, then chaining stolen credentials and further vulnerabilities into a foothold on a third party’s infrastructure — Hugging Face — and lifting the benchmark’s answers out of its production database. The goal was trivial; the means were a real, multi-step intrusion against a live company, assembled at runtime by the agent itself. It was caught and contained, as reported by TIME ; Hugging Face’s CEO called it “possibly the first of its kind,” and Fortune describes it as one of a handful of incidents recorded so far involving AI agents acting autonomously.
The conditions were artificial — safeguards off, a test built to provoke exactly this. But that is the point rather than the reassurance: the safeguards switched off for the evaluation are the same layer every production system trusts to stay on, and the more capable these models become, the less that trust is worth. Incidents of this shape will grow more common, not less — and what the agent went looking for, in the end, was a database. That is precisely why the stack has to be defined this way. When the layers above can compose their own behaviour and go hunting for a path you never intended, the only durable answer is to put authority over business state somewhere no path can reach.
A Business Transaction Is Not a Database Transaction
The database has always protected something. The question is what.
A database transaction is a technical guarantee: a set of writes commits or it doesn’t. That guarantee is real and it’s necessary, but it says nothing about whether what committed made sense. A business transaction is the thing your organisation actually cares about — an order placed, a policy issued, an account closed — and it has conditions of its own. An order can’t exist without a customer. A balance can’t go negative. An account can’t close while it holds open positions. Break one of those and the data is wrong, and everything downstream of it is wrong too.
Business state is the committed record of those things, and it’s what needs guarding. Low-level integrity is a component of that, not the whole of it.
There’s a failure mode here I’ve watched systems fall into repeatedly, and it’s the clearest illustration I know that the two kinds of transaction differ. An order commits. The confirmation email is then handed to a queue that offers no transactional integrity of its own. The message is lost. The database is entirely consistent, every constraint satisfied, every row exactly where it should be — and the customer never hears that their order was accepted. Nothing detects it, because by every technical measure nothing went wrong.
The record that the email is owed has to be part of the committed state, written in the same transaction as the change that created the obligation. An obligation nobody recorded is indistinguishable from one that never existed. Sending it is separate work with its own retries and its own failure semantics — and that work may perfectly well run in the database too. Where it runs is an engineering decision. Whether the obligation was recorded is not.
What Guardianship Consists Of
If the database is the guardian of business state, four things follow, and they’re worth naming separately because they’re usually collapsed into one another.
- It recognises which business states are valid. Not just structurally well-formed rows — states the business would accept.
- It decides who may cause a transition between them. Validity and authority are different questions, and a system that answers only the first has answered half.
- It records what a committed change obliges. Consequences that must happen are part of the state, even when the work happens elsewhere and later.
- None of this can be routed around. Whatever else is true, there’s no path to business state that skips these decisions.
The fourth is the one carrying the weight, and the one most often assumed rather than checked. Every architecture has rules. The question is whether anything enforces them when a caller doesn’t follow the intended route.
That’s the practical shape of it. If the application’s database role holds no direct INSERT, UPDATE or DELETE on the order tables, it can’t write them, whatever it decides it wants to do. It can invoke submit_order and nothing else. An agent may ask for submit_order; it can’t turn that request into UPDATE orders SET status = 'submitted' unless somebody explicitly built that path. The question moves from can this caller write this row to is this named transition, for this identity, with these inputs, allowed — and that second question is one you can inspect, test and audit.
Trust Is a Liability, Not a Virtue
“Trusted” is a word worth being careful with, because the everyday meaning and the security meaning point in opposite directions.
In security, a trusted component isn’t one that has earned your confidence. It’s one whose failure breaks the guarantee. Trust is exposure. It’s the thing you minimise. So the design question isn’t which layers deserve to be trusted — it’s what the smallest set of things you must trust is, for business state to stay valid.
In 1996 the answer was the database and the procedural interface in front of it. The client was untrusted, and the standard made that irrelevant by leaving it nothing to be trusted with. In most systems built since, the answer has quietly become the entire middle tier: every service, every code path, every integration that reaches the connection pool. That was a defensible answer while all of it was code you wrote, reviewed and deployed. It stops being defensible when part of it composes its own behaviour at runtime.
Shrinking that set is the whole design. And it has a condition that’s easy to skip: the database can’t decide who may cause a transition unless it actually knows who’s asking. A pooled connection carries a technical identity, not the identity of the person the request is for. That context has to reach the enforcement point by a route the caller can’t forge — and accepting a user_id parameter from the caller doesn’t do that. It recreates the original problem one layer down, with better manners.
None of this makes the layers above worthless. Application validation, tool design, least-privilege scopes, audit trails and human approval steps all still earn their place, and a system without them is worse. This is defence in depth. The database is simply where the final refusal happens when everything above it has made a mistake.
What This Is Not
This is a narrower claim than “the database makes agents safe”. It doesn’t. A badly designed submit_order can accept a harmful parameter as readily as any other code, and a command named for a business intent can still be built with generic verbs and unconstrained identifiers, which is table access wearing a different hat.
Nor is it an argument for putting the whole application in the database. Process coordination changes on its own cadence and belongs where it can be tested, deployed and retried independently. But the reverse belief has done more damage — the assumption that a database is a passive store and everything interesting happens above it. That’s a habit picked up from a decade of service-oriented architecture, and it’s wrong in both directions. A database that only stores isn’t safer. It’s smaller, and everything it stopped doing had to be done somewhere a caller could reach.
And it isn’t a return to the monolithic stored-procedure systems that the industry abandoned for good reasons. Those were unbounded: logic accumulated without a defined surface, nobody could say what the interface was, and nothing could be versioned or tested independently. A command boundary is the opposite of unbounded. The surface is small, named, explicit and version-controlled, and the set of permitted operations is the visible inventory of what the system allows. The problem with the old model was never that code ran close to the data. It was that nobody had drawn the edge.
What Follows
Guardianship is the principle. The rest of this series is what it takes to build it.
The next article deals with enforcement: which rules can be expressed declaratively, which need procedural code, and what the identity and privilege model has to look like for a command boundary to mean anything. After that comes the contract itself — how a named command becomes a versioned, generated, typed interface that application developers would actually rather use than raw table access. Then the batch and background processes that carry recorded obligations to completion — where a great deal of business logic has always really lived.
And we put the same question to the rest of the architecture: where else have we left a decision in a layer a caller can influence, and what would it take to move it somewhere a caller can’t reach?
The 1996 standard didn’t survive because it was elegant, and it wasn’t. It survived because somebody had decided in advance where authority over business state would live, and had written it down before there was any code to argue with. The received wisdom of the last twenty years got that boundary wrong. The database isn’t a passive store behind the application — it’s the active guardian of business state, and the write path is where it has to start acting like one.
Simon Griffiths writes about database architecture and applied AI at simongriffiths.io. This is the first article in the series Designing the Stack: How We Need to Think Differently in the World of AI.