Aigarth runs as 12 services. The way to think about that is 7 primitives. Each primitive is a single idea. Each service implements one or two of them. This post is about why we drew the lines where we did.
The 7 primitives
The whole platform can be described as the sum of seven things. The boxed equation, paraphrased:
- Adaptive Intelligence
- + Memory
- + Evolution
- + Experimentation
- + Distributed Computation
- + Verification
- + External Reality
In plain English:
- Adaptive Intelligence is the Organism. A single addressable thing that learns, mutates, and forks.
- Memory is the episodic and long-term store an Organism writes to and reads from.
- Evolution is the fitness ledger and the lineage. Every variant ranked, every generation tracked.
- Experimentation is the Work Item. The envelope that says "try this with these constraints, here is the reward."
- Distributed Computation is the Worker. The four tiers from a local Docker runner up to a Qubic on-chain processor.
- Verification is the proof that a Worker actually did the work. Replication, challenge, reputation.
- External Reality is the boundary with the outside world. Oracle Machines read; the OC processor writes.
Why each primitive is its own service
Four reasons, in this order of how much they matter.
1. Testability
A service that does one thing is a service you can test without spinning up the rest. The Work Runtime has 38 vitest cases. None of them touch the ANN service. None of them touch billing. None of them touch identity. If we had bundled the Work Runtime into the ANN service, every test would have needed a Postgres + NATS + an ANN schema. The test would have been 10x slower and 5x more brittle.
2. Blast radius
A primitive that breaks should break its own service and nothing else. When the Work Runtime had a race condition in its lease-expiry sweeper, the only thing that went down was the Work Runtime. The ANNs kept serving decisions. The billing kept emitting events. Identity kept issuing tokens. If we had put the Work Runtime inside the ANN service, a single race condition would have taken down the entire intelligence layer.
3. Scaling
Different primitives have different load shapes. Identity is read-heavy and bursty. ANN is read-heavy and steady. Billing is write-heavy. The Work Runtime is both, with spikes. They do not share a scaling profile. When they are in one service, you scale them all at once. When they are split, you scale each one to its own load.
4. Ownership
A primitive is a unit of design. When it lives in one service, the ADR that defines it is a single, readable document. When it is split across three services, the primitive is a constraint that has to be enforced in every place it touches. We have three ADRs (005, 006, 007) that name the three core primitives. They are readable in one sitting because the primitives they describe live in well-bounded places.
The hard calls
Two boundaries almost went the other way. Both are good examples of what a wrong call would have looked like.
The Work Runtime almost lived inside the Tissue service
The first version of the design put the Work Runtime in tissue. Tissues were already the "combine multiple ANNs into a single decision" primitive, and the Work Runtime looked like a natural extension. We rejected the bundling for a specific reason: the Tissue service is stateless. A tissue call comes in, fans out to ANNs, combines, returns. A Work Item is stateful across its whole lifecycle (queued, running, verified, failed, or disputed). Bundling the two would have forced the Tissue service to track per-Work-Item state, breaking the stateless invariant that downstream consumers rely on. The Work Runtime got its own service (port 7012, 5 tables, 13 routes). The Tissue service stayed stateless.
The Organism almost lived inside the ANN service
The first draft of the Organism primitive was a new endpoint on the ANN service. An Organism has a genome, and a genome looks a lot like an ANN version. We rejected the bundling because an Organism is a unit of evolution, not a unit of inference. An ANN answers questions. An Organism forks, mutates, and improves across generations. They have different lifecycles, different access patterns, and different users. The Organism got its own tables in the ANN database (so we could reuse the auth + billing integration) but its own routes, services, and ADRs.
The cost of getting it wrong
Coupled primitives are brittle primitives. Two examples from the v0.2 evolution PEP that we named explicitly so we do not forget.
- If Verification (the Work Runtime's job) lived inside Experimentation (the Work Item envelope), every change to the verification stack would be a change to every work item. That is the wrong direction. Verification should be replaceable (replication today, TEE tomorrow, ZK someday) without changing the work item.
- If Memory (the Organism's episodic store) lived inside Adaptive Intelligence (the Organism itself), a memory write would be a genome mutation. The two have different audit trails, different retention rules, and different consumers. Bundling them would couple those rules together.
What we would do differently
Two things.
- The OC processor (External Reality, ADR 007) should have been its own service from day one, not a sidecar in the Qubic service. We deferred that call to keep v0.2 small. It is the next big refactor on the list.
- The Datasets service should be merged with the Training service. The boundary between them is thin. They share most of their access patterns. We will likely do this in the next refactor.
What is next in this series
Post 03 will cover how we test a 12-service platform with 600+ tests and a 4-minute CI loop. It is a long post because the test architecture is the most underrated part of the platform.