One Postgres. FHIR underneath, governed context above.

The current core stores canonical FHIR R4 with immutable history, projects SQL-on-FHIR views, enforces practice-scoped policy, returns cited search/context, and ships a framework-free LISTEN/NOTIFY query store. Universal governed-write coverage, browser/React adapters, patient/consent scope, and clean Bundle export are the target contract.

The layered model

Four layers, one source of truth

FHIR was built to move records between distrustful organizations. When you build one product, that model leaks into everything. bonfire flips it: the record stays canonical FHIR R4 (lossless JSONB) in Postgres, and the typed views your app reads are fast projections on top.

1

Canonical FHIR R4 — in Postgres

Every clinical write lands as lossless FHIR R4 (JSONB) in Postgres, with pgvector. This is the one source of truth — referential integrity, transactions, and write semantics work the way an app team expects, not the way a stranger-to-stranger exchange assumes.

2

SQL-on-FHIR projections

The current core materializes conformance-tested flat projections in Postgres. It also has an atomic projected-write primitive; routing every governed mutation through that primitive is still in progress.

3

LISTEN, then tenant-scoped re-query

The current framework-free useClinicalQuery store registers a Postgres listener before its initial load, then re-queries one whitelisted projection through tenant-scoped RLS after matching wake-ups. A browser transport and React adapter are target work.

4

FHIR R4 — canonical; Bundle export planned

Canonical FHIR R4 is shipped. A governed function that assembles a clean patient Bundle for export and interop is on the roadmap.

Sync vs async

The primitive exists; the complete freshness lifecycle is the target.

The core can atomically update a projection with a write and notify subscribers. The next slice is to put every governed mutation behind that path and report the status of projections and heavy indexes explicitly.

Current — atomic projected-write primitive

Code using the primitive updates its projection in the same transaction and emits an invalidation after commit. Not every governed write uses this path yet.

  • notesByPatient
  • timeline
  • latestScores

Target — downstream status

The target receipt reports whether semantic search, agent context, and other derived indexes are fresh or pending. That product-level status contract is not shipped yet.

  • semanticSearch
  • agentContext
  • complex FHIR search

Target freshness lifecycle

The planned response makes projection and downstream-index status explicit. The JSON below is target pseudocode, not a current response shape.

target pseudocode · write-response.json
{
  status: "committed",
  views: {
    notesByPatient: "fresh",
    timeline: "fresh"
  },
  indexes: {
    semanticSearch: "pending",
    agentContext: "pending"
  }
}

Target typed-client experience

The product API below is the intended adapter over the current Postgres primitive; it is not shipped.

target pseudocode · notes.ts
// Target API — not shipped
await clinical.notes.create({ patientId, encounterId, text })
// returns the freshness object above

const notes = useClinicalQuery(api.notes.listByPatient, { patientId })
// reactive, fresh on write
Why one store / no Redis

Postgres-first, with one canonical record.

The current core keeps canonical FHIR, projections, policy, receipts, and cited search rows in Postgres. Its framework-free query store uses LISTEN/NOTIFY as a wake-up signal and an RLS-scoped re-query as the only data path. Browser delivery, React bindings, polling fallback, and workload-specific infrastructure remain future decisions.

1

One canonical store, in Postgres

Clinical writes commit to Postgres as canonical FHIR R4 (with pgvector). One transaction, one place a fact lives. There is no second copy to invalidate, so there is no window where the cache and the database disagree.

2

Atomic projected-write primitive

The existing primitive can maintain a projection inside the write transaction. The known product gap is that governed writes do not all route through it yet.

3

Current store: listen and re-query

The framework-free store listens for a projection notification, re-queries the caller's whitelisted view through tenant-scoped RLS, and emits only when the scoped snapshot changes. It is not a React hook.

4

Target: honest derived-index status

Production embeddings, hybrid ranking, agent-context compilation, and Bundle export remain separate target slices. Their freshness must be reported rather than implied by the canonical write.

The two-database habit

  • Postgres for writes, Redis for fast reads → two copies of the same fact
  • A separate broker for events → its state can drift from the DB’s
  • Cache invalidation wired by hand → the classic source of stale reads
  • More moving parts to run, secure, and put under a BAA

bonfireDB — Postgres-first

  • One canonical FHIR record plus rebuildable projections
  • Current LISTEN/NOTIFY store supports protected re-query
  • Target receipts distinguish canonical commit from derived-index status
  • Add a cache or bus only when a measured hot path forces it

Honest trade-off: Postgres-first isn’t infinitely scalable on its own. The point isn’t “never add a cache” — it’s “don’t start with one.” When a measured hot path actually demands a cache or a dedicated bus, you add it deliberately, against real numbers, instead of inheriting a sync problem on day one.

Get running

Run it locally. Deploy to your AWS. Build.

Same code path the whole way. Start on your laptop, deploy into your own cloud, and write clinical functions in TypeScript.

1

Run it locally

The intended early-access workflow provisions Postgres, pgvector, canonical FHIR, projections, and policy. The CLI experience is still rolling out.

terminal
# local stack, one command — early access
2

Deploy to your AWS

Ship the same stack into your own AWS account. In the OSS tier it runs entirely in your infrastructure.

terminal
# deploy to your AWS — early access
3

Build

Import the typed client and call clinical functions. This typed client and per-write freshness receipt are the target early-access experience.

target pseudocode · app.ts
// Target SDK — early access
import { clinical } from "bonfire"
The two-databases problem, solved

App state and clinical data, in one place

FHIR has nowhere to put drafts, UI state, or workflow status — so teams stand up a second database and inherit a sync problem. And because you can’t query FHIR by arbitrary fields, joins, or aggregates, even a list screen needs a denormalized projection. bonfire ends both.

FHIR-only, the usual way

  • No home for drafts, UI, or workflow state → a second DB
  • Two databases that must stay in sync → sync hell
  • No arbitrary field / join / aggregate queries
  • Every list screen needs a hand-built projection

bonfireDB

  • App state and clinical data live in one canonical Postgres store
  • No second database, no sync layer to operate
  • Query by any field, join, and aggregate — it’s Postgres
  • Current projections can be rebuilt from canonical FHIR
  • Clean patient Bundle export remains target work

Target: governed Bundle export

Because the canonical record is already FHIR R4, a clean Bundle exporter is a bounded next slice — but this function is not shipped yet.

target pseudocode · export.ts
// Planned API — not shipped
await clinical.fhir.export(patientId)

Compliance & where it runs

OSS tier (Apache-2.0 planned, pre-release): bonfire is designed to run entirely in your AWS account. The data never touches our infrastructure, so we are not a Business Associate — there’s nothing for us to sign, because we never handle your PHI.

Managed tier: we host it for you and sign a BAA, backed by our own AWS BAA. Same code path, same freshness model, same FHIR-underneath — you just don’t operate the infrastructure.

bonfireDB is early-stage; this page describes product design and positioning. “FHIR-native” / “FHIR-compatible” are used descriptively. Choose the tier that matches who you want holding the PHI.

You build the app. Bonfire is the clinical data layer underneath.

Canonical FHIR R4, projections, policy, cited context, and governed proposals in the core — with the complete typed programming model still building.

FAQ

Frequently asked questions

FHIR vs Postgres: which should I use for a clinical app backend?

Both — store the canonical record as FHIR R4 (lossless JSONB) in Postgres, and query it through typed read models. Reading raw FHIR directly has no home for drafts, UI, or workflow state, and you can’t query it by arbitrary fields, joins, or aggregates. bonfireDB keeps canonical FHIR in Postgres and projects typed views on top, so you get app-style querying plus clean interop.

Does bonfireDB need Redis or a separate cache?

The current framework-free store uses Postgres LISTEN/NOTIFY as a wake-up signal and re-queries through tenant-scoped RLS; it does not require Redis. Browser delivery, a React adapter, polling fallback, and workload-specific cache decisions remain future work.

How does bonfireDB keep reads fresh on commit?

The current core has an atomic projected-write primitive plus a framework-free LISTEN/NOTIFY query store, but governed writes do not all route through the primitive yet. The target adds universal route coverage, browser bindings, and receipts that distinguish canonical commit from derived-index freshness.

What does a write return in bonfireDB?

The target write response reports committed projection status and whether derived indexes are fresh or pending. That product-level freshness receipt is not shipped yet.

Is bonfireDB a HealthLake or FHIR-server alternative?

bonfireDB is the governed app-and-agent context layer above canonical FHIR R4, not another FHIR server. The current core implements projections, policy, citations, and governed proposals; a complete typed client and clean Bundle export are still early-access target work.