Skip to content
OIDOT

Architecture

Why we built the platform before the product

4 min read

The normal order is to pick a product, build it, and add infrastructure underneath as the product demands. We did the opposite, and it is worth explaining why, because from the outside it can look like indecision.

Oidot is not fixed to one industry or product category. The exact product, the customer, and the business model are open questions we have deliberately not answered yet. That sounds like a reason to build nothing. In practice it is a reason to be precise about what can be built anyway.

What is stable regardless of the product

Every data product needs the same things before it does anything interesting. Someone signs in. They belong to a company. Somebody grants them access. Eventually somebody is billed.

None of that is differentiated. All of it is required. And it is the same work whether the product turns out to be a forecasting tool, an analytics interface, or something we have not thought of.

The cost of rebuilding it is not obvious at first, because the first build is cheap. It is the fourth that hurts: four user tables, four permission models, four billing integrations, each subtly different, none of them the system of record. Users end up with an account per product. Administrators manage access in four places and get one of them wrong.

So the platform owns identity, organisations, product entitlement, and billing. It owns nothing else. It contains no product business logic, by design.

The boundary is the actual decision

A shared platform is only useful if the things built on it stay independent. Otherwise it is a monolith with a nicer name.

The boundary we enforce is this: the platform authenticates a user and states which products their organisation may access. A product answers what that user may do inside it. Two different questions, answered in two different places, neither guessing at the other's job.

The mechanism matters more than the principle. When a user logs in, the platform issues an access token signed with an asymmetric key, carrying their identity and their organisation's entitlements. It publishes the corresponding public key. A product backend verifies that token against the public key and proceeds — without calling us.

That last clause is the whole design.

What offline verification buys

Three things, none of them theoretical.

Products do not inherit our availability. If the platform is down, nobody can log in — but everyone already holding a valid token keeps working. The platform is a dependency of authentication, not of every request in the system.

A compromised product cannot forge identity. Products hold the public key. They can verify a token; they cannot issue one. In a system where several products share an identity layer, this is the difference between one product being breached and every product being breached.

Products stay genuinely independent. Each owns its own database and its own runtime, and can use a different language or framework where the problem calls for it. A product can be rebuilt or dropped without touching the foundation or the other products.

What we deliberately did not build

The platform is one service, one database, one container. No message queue, no cache, no service mesh, no Kubernetes.

This is not a stage we intend to grow out of. Identity, organisations, and entitlement are well-understood problems that fit comfortably in one well-factored codebase, and distributing them early would buy operational complexity in exchange for nothing.

Inside that single service the modules — identity, organisations, entitlement, billing — talk to each other through defined interfaces rather than reaching into each other's tables. The seam is drawn before it is cut. If a module ever genuinely needs to scale separately, the line it separates along already exists.

Each piece of deferred infrastructure has a named trigger rather than a place on a roadmap. Redis arrives when there is a concrete need for caching or rate limiting. A queue arrives when a workload is slow enough and retry-sensitive enough to need one. Writing the trigger down is what stops the decision from being made by habit later.

The part that is genuinely uncomfortable

Building a platform before a product means building something with no users, no feedback, and no way to be told you are wrong by anything except your own reasoning. That is a real risk and worth naming rather than glossing.

What makes it tolerable is that the platform's problems are ones other people have already solved, badly and well, in public. Tenancy, token verification, and entitlement have known failure modes. We are not discovering whether users want organisations; we are deciding how to model them correctly.

The product decisions — which we have not made — are the ones that genuinely need contact with a customer. Keeping them out of the platform is what leaves us free to make them late.