e3b7a14·Insights·7 min
Write software as if it were already public
There is a test I use when designing a system: if tomorrow the code, the database schema and a copy of the data ended up online, what would be lost? If the answer depends on nobody looking at them, the design already has a problem.
Why “nobody can see it” is no longer a defence
For years a lot of application security rested on an implicit assumption: the code is private, so a mistake stays hidden until someone finds it. Today a model that can read an entire codebase and reason about its paths does in minutes what used to take an experienced person days. You don’t have to believe in extreme scenarios: it is enough to admit that finding a hole costs much less than it used to, for attackers and defenders alike.
The practical consequence is a design rule: no protection should depend on someone not knowing the system. Not the table names, not the shape of the routes, not the belief that “nobody will find this endpoint”.
A real case: isolation that depended on a filter
In June 2025 Asana discovered a bug in its own MCP server that could show one customer’s data to other organisations. The feature was taken offline; according to some sources about a thousand customers were involved. It was not an attack: it was a logic error, present since the feature launched.
It is the typical case of isolation that lives in application code: every page, every endpoint and every export has to remember to add the filter for the right customer, and it takes only one time when it doesn’t.
Five rules that don’t depend on nobody looking
- Isolation lives in the database, not in a filter. If the database itself shows each user only the rows of their own organisation, even a hastily written query returns only what it is entitled to. This holds for the interface, the APIs, AI agents and queued jobs.
- A copy of the data must not grant access. Session keys are stored as a fingerprint (a hash), not in clear: a backup that ends up in the wrong hands doesn’t let anyone sign in as another user, administrators included.
- The history can’t be erased from the application. The application can only append rows, not modify or delete them. Whoever gets in, or a bug in the code, can’t clean up their own tracks.
- Context is not a parameter. The organisation being worked on is written into the connection and is not an argument the caller can change. If hidden text in a document suggested an agent look at another customer, there would be no way to do it.
- No generic tools. No “run a query” or “fetch this address” function: every exposed operation has a name, a permission and a schema. A perimeter that doesn’t exist can’t be widened by mistake.
The cost, and why you pay it once
It isn’t free. The database rules have to be written the right way (a wrong shape can cost a lot in performance) and every extra protection is code to test. But they are costs paid once, in the foundations: a new module inherits them without whoever writes it having to remember anything.
It is the same logic as sorting out the data before buying tools: boring work done early makes everything that comes after reliable. A declaration of interest: these are the rules we are building our new core on, and the ones we apply to client systems, in particular web apps and infrastructure.