SMART functional conformance passes. Inferno is green functionally; HTTPS/TLS closure is the remaining SMART gate. View the roadmap

Architecture

Generated from docs/architecture.mdView source

The one thing that matters

embedded-runtime is the runtime foundation. It is not the product, and it is not the public contract.

Everything Ilavrita publishes — routes, error shapes, search semantics, versioning, tenancy and authorization — belongs to Ilavrita and sits behind its own interfaces. That separation is the reason a PostgreSQL backend can be added later without rewriting FHIR behaviour, and the reason a client written against Ilavrita today keeps working when the storage layer changes.

Layers

Rendering diagram…

Dependencies point downward only. Nothing above packages/storage knows which backend is in use, and nothing below it knows about FHIR.

Rules the layering depends on

  1. Only packages/storage/embedded-runtime imports the embedded-runtime runtime. This is enforced by a depguard rule in .golangci.yml, not by convention alone.

  2. No SQL above the storage backend. Higher layers reason in FHIR terms: resource type, search parameter, modifier. SQL is a detail of one backend.

  3. No embedded-runtime concept reaches /fhir/R4. Not a collection name, not an admin route, not an error shape. Failures are translated to OperationOutcome.

    This is enforced at the process level too: embedded-runtime's own REST API (/api) and admin console (/_) are disabled unless ILAVRITA_EXPOSE_embedded-runtime=true. Registering our routes on its router is not enough — the runtime registers its own, and they would otherwise be served alongside ours.

  4. Administrative and FHIR surfaces are separate. Different routes, different authorization policies.

Search is where a FHIR server usually goes wrong, both in correctness and in performance, so it gets an explicit pipeline rather than ad-hoc query building:

Rendering diagram…

Two consequences follow. Authorization predicates join the query plan rather than filtering results in memory afterwards, and supported parameters are served by typed indexes rather than by scanning resource JSON.

Data integrity

A successful mutation commits the canonical resource, its immutable version, its search indexes, its file references and its audit record together. A failure before commit leaves nothing visible.

Search indexes are derived data. They are physically separate from the canonical JSON and can be rebuilt from it, so losing an index costs a reindex, never a resource.

Projects

A Project is the isolation boundary: the primary tenant, security, configuration and FHIR data boundary. The hierarchy is Instance -> Super Project -> Project -> ProjectMembership -> Profile -> Resource.

Every canonical resource, version, index entry, file reference, audit record and job belongs to exactly one Project, and the identifier is derived server-side rather than read from a client-supplied FHIR field. It is applied in the query layer rather than after results are fetched.

storage.ResourceKey carries the Project as part of the key rather than as an optional argument, so no storage call can be made without it (FR-045).

Cross-Project exposure of health data is the worst failure this system can have, which is why the boundary lives in storage rather than in each service.

Current state

Only the HTTP surface exists. packages/storage defines interfaces with no implementation, and the FHIR services are package outlines. See known limitations.

Source revision 9462213 · dev