The Beam and the Dark Space:
QDB, a Database That Owns No Data
Every database you have used answers the same question first: where do I put this? Rows in tables, documents in collections, nodes and edges, vectors in an index. The storage model comes first and the query model is built on top of it, which is why moving between them is a migration rather than a decision.
QDB starts somewhere else. It answers what should I be looking at right now? and it owns nothing at all. No tables, no transactions, no durable indexes. Everything durable lives in the geometric memory underneath. QDB is a lens over it.
That sounds like a technicality. It is the whole design, and this article is about what falls out of it: why the relational, graph and vector models stop being three products, why "forget this" is a different operation from "evict this", and why a query can simulate instead of merely traverse.
Contents
1. The beam and the dark space
The metaphor is load-bearing, so it is worth stating precisely rather than decoratively.
A tenant with 30 GB in the substrate and a 15 MB beam budget never sees 30 GB at once. The beam holds a few hundred thousand handles - symbolic links into the dark space, tens of bytes each. Re-aiming the focus re-resolves the handle set. Dimming the beam destroys nothing.
The closest relatives are not ArangoDB or Neo4j. They are memory-mapped working sets, GPU texture streaming, CDN edge caches and, most of all, attention in a transformer: a large immutable substrate plus a small moving window of what currently matters. What makes it a database rather than a cache is that the window is declared by a query and the substrate is the system of record.
2. Districts: urbanising the embedding space
The substrate is not one enormous vector space. It is divided into districts: fixed-width regions, each specialised on a single semantic axis, each with its own embedding, its own metric and its own update cadence. Name is a district. Region is a district. Behaviour, finance and transaction shape are districts.
The obvious objection is that a fixed, modest dimension per district is a limitation next to a single very wide space. It is the opposite, and the reasoning is the most interesting design argument in the system.
A single wide embedding collapses every concept into one geometry with one metric and one training regime. Retraining it perturbs everything at once: improve how the space understands regions and you have also, silently, changed how it understands behaviour. Capacity grows by widening the vector, which is the expensive dimension to grow, and the space becomes progressively harder to reason about as more meanings are folded into the same axes.
Districts grow the other way. Capacity grows by adding a district, not by widening one. Each district can be retrained without perturbing its neighbours - refresh the behaviour district and the region district is untouched. Each can use the metric its content actually deserves. We call it embedding urbanisation: a city gains capacity by adding quartiers, each with its own character, not by widening one street until it is a runway.
3. Exact lookup inside a similarity space
Here is the part that makes QDB a database rather than a vector index with ambitions.
Not every district is semantic. A district may be declared trivial,
meaning its embedding is a deterministic projection - a hash, or a one-hot
over a small enumeration like {active, suspended, closed}.
Identical values land at identical positions. Exactly identical.
In a trivial district, a nearest-neighbour query with a radius of zero and a limit of one is an exact-match lookup. The same primitive that answers "customers similar to this one" answers "the customer whose status is exactly suspended", with the same operation and the same cost model.
This is why relational rows live inside QDB rather than beside it. A system that stores structured records in Postgres and embeddings in a vector store has two consistency models, two backup stories, two failure modes and a synchronisation job between them that is nobody's favourite piece of code. Here "exact" and "similar" are the same query against the same substrate, and the difference is a property of the district, declared once.
4. Entities assembled across districts
An entity - a customer, a device, a reservation - is not a row. It is an assembly of positions across the districts its class participates in, bound together by an identity position that is a structured superposition of them.
The binding is not a foreign key or a join table. It is algebraic: the identity position is composed from the per-district positions in a way that can be inverted, so resolving any one of an entity's addresses reveals the others. Composition and decomposition are operations on positions, not lookups in an index that has to be kept in step.
A class declares which districts it participates in. A customer might live in identity, name, region, behaviour and finance; a device in identity, fleet and telemetry shape. Adding a district to a class extends what can be asked about it without touching the classes that do not participate - the schema-migration problem, reframed as an addressing problem.
5. Edges that can be traversed or fired
QDB has typed edges: OWNS, REFERENCES,
TRANSFERRED_TO, between declared entity classes, carrying
per-edge attributes like an amount, a timestamp, a confidence.
So far, a graph database. The difference is that an edge type also declares its execution semantics - what kind of arc it is in a Petri net, and with what weight - which gives every edge in the store two entirely different readings.
| Mode | What it does | Familiar as |
|---|---|---|
| Declarative (the default) |
Walks adjacency in the beam, folds attributes, sums and counts. Execution semantics are ignored. | Cypher, AQL, Gremlin |
| Behavioural (opt in, explicitly) |
Materialises the matched subgraph as a Petri net using each edge's declared arc type and weight, runs it, returns the resulting state. | Nothing, really |
The same declared schema drives both. MATCH a subgraph of
accounts and transfers and you can ask two different questions of it: what is
the total transferred (declarative), or what happens if these transfers all
execute given the balances and the ordering constraints (behavioural). The
second is a simulation, expressed as a query, over data you did not have to
copy anywhere.
6. Focus: the query decides what stays resident
Focus is what the beam is aimed at, and it is the mechanism that replaces the cache-eviction policy you would otherwise be tuning. Rather than a global LRU guessing at relevance, relevance is declared:
| Focus | Illuminates |
|---|---|
| Spatial | A centre and a radius in one district |
| Temporal | A recency window |
| Trajectory | Everything on an active execution path |
| Relational | A seed entity and N hops along an edge type |
| Semantic | A query position in one district |
| Composite | A weighted blend of the above |
Focus assigns every resident handle a score, and when the beam exceeds its budget the lowest scores go first. The consequence is that a fraud investigator working a case and a batch job scoring a month of history get completely different residency behaviour from the same store, without either of them configuring a cache - because each declared what mattered, and eviction followed.
7. Eviction is not deletion
This distinction is small to state and is the one operational property most worth internalising.
Eviction drops a handle from the beam. The positions remain in the substrate; a future focus re-resolves them. It is lossy at the beam and lossless underneath. It happens constantly, automatically, and is never an event anyone needs to be told about.
Forgetting removes a position from the substrate. It is explicit, it is a different verb, and it is the only operation that destroys anything.
Conflating the two is how systems end up with data they cannot delete and caches they cannot trust. Keeping them separate makes a regulatory erasure request a real operation with a real guarantee, rather than an eviction that quietly comes back the next time something re-resolves it. If you have ever tried to prove that a deletion actually deleted something in a system with layered caches, you already know why this is worth a dedicated verb.
8. Snapshots, diffs and lineage
A snapshot is a frozen, named handle table - the beam at an instant. It is kilobytes, because it is a table of symbolic links rather than a copy of anything.
That cheapness is what makes time queries practical. Two snapshots taken a month apart support a diff: what entered this region of the space, what left, what moved and how far. Follow one entity's positions across snapshots and you have its lineage - not an audit log reconstructed from events, but the actual trail of where it has been.
Because positions are derived from state, "how far did this move" is a measurement rather than a metaphor. A customer whose behaviour position has travelled a long way in a month has changed behaviour by a quantity you can put a number on and sort by, without anyone writing a rule about what constitutes a meaningful change.
9. What this is not
Four honest limits, because an architecture article without them is a brochure.
- It is not a transactional database. QDB owns no transactions, and it is not where you put your ledger of record if you need multi-object ACID semantics. Durability belongs to the substrate; the beam is a projection.
- The beam is lossy by design. A query whose focus is wrong sees less than a query whose focus is right. This is the intended behaviour - it is what makes the memory bounded - but it means focus is a correctness concern, not a tuning knob.
- Districts are a modelling commitment. Deciding which axes deserve a district is real design work, and getting it wrong is felt later. Composition buys evolvability at the cost of requiring you to say what you mean by "similar".
- The measurements are single-node. Every figure we have published is one node with the current integration. The substrate is designed to distribute, and we have not published distributed numbers, so please do not read any of this as a scale-out claim.
Conclusion
The idea underneath QDB is that a working set is a first-class concept rather than an implementation detail of a cache. Once you commit to that, several things that are normally separate products collapse into one:
- Exact and similar are one primitive. A trivial district makes nearest-neighbour an exact lookup, so relational records and embeddings live in one substrate with one consistency story.
- Capacity grows by adding districts, not by widening vectors. Each axis retrains independently, so the model can evolve in pieces.
- A graph query can simulate. Edges declare execution semantics as well as structure, so the same subgraph answers "what is true" and "what would happen".
- Eviction and deletion are different verbs. One is automatic and lossless underneath; the other is explicit and final.
None of this is on the public API surface today - it runs inside the product. Read it as the architecture and the reasoning behind it. The geometric memory article covers what the positions themselves mean and how execution trajectories are compared, and BQL covers the query language that sits on top of this one.