Design decisions
Why the API is shaped the way it is — the architecture decision records, indexed by what each one locked in.
Every consequential choice in Postel is written down as an ADR in decisions/. This index is the adopter's cut: what each decision locked in, and where you feel it in the API. The records themselves carry the full context, options considered, and consequences.
The decisions you feel every day
| ADR | What it locked in |
|---|---|
| 0001 — Library shape | No service, no broker, no separate dispatcher — ever. Postel runs in your process against your database. Everything on Why Postel follows from this one. |
| 0002 — Code-first configuration | No YAML, no DSL. Postel({...}) is a typed factory over a literal config — which is why postel.inbound.<source> is typed to your sources and why definePostelConfig exists. |
| 0007 — Storage strategy | The adapter matrix (standalone / client / ORM) and host-transaction passthrough — the { tx } option on send() that makes the transactional outbox work through your connection. |
| 0012 — Package granularity | One @postel/core for both halves rather than split sender/receiver packages; adapters stay separate. Why installing one package gives you inbound and outbound. |
| 0016 — House API idioms | The conventions every surface follows: options-object factories, duration strings ("5m"), typed errors over booleans, readonly shapes. |
| 0017 — Framework-adapter pattern | One framework-agnostic core (@postel/http) + thin per-framework gate bindings — why every adapter behaves identically and a new framework is a ~100-line package. |
| 0015 — Pagination envelope | The { items, nextCursor } page shape and opaque cursor contract every list API and admin route shares. |
The decisions behind the polyglot bet
| ADR | What it locked in |
|---|---|
| 0003 — OpenSpec as the spine | Every behavior is a testable spec scenario before it's code; CI fails if a requirement has no matching test. |
| 0004 — AsyncAPI wire format | The wire format is a machine-readable AsyncAPI 3.0 document, not prose. |
| 0005 — Staged polyglot rollout | TypeScript first, then Go, Python, Rust — each gated on the same compliance suite. See Polyglot. |
| 0008 — Conformance levels | CONTRACT vs PORT-SPECIFIC — the compliance suite is the executable boundary. The reason specs can be narrow and ports can be idiomatic. |
| 0011 — Compliance suite tooling | A Go runner + YAML vectors, deliberately not TypeScript — the oracle must be independent of the implementation it judges. |
| 0014 — Release & versioning flow | Version-matched conformance: a port at X.Y.Z claims conformance by passing compliance@X.Y. |
The full record
The remaining ADRs cover narrower calls — monorepo layout, compliance-suite evolution, TypeScript toolchain, dropping @postel/edge, and why the API reference is hand-curated (with CI drift gates instead of a generator).
A change that would contradict one of these needs a superseding ADR, not a quiet diff — that's the point of writing them down.
Postel vs Svix vs Outpost vs Convoy vs DIY
An honest comparison across footprint, cost, transactionality, portal, and SLAs — including what Postel deliberately doesn't do.
Polyglot
One contract, many languages. TypeScript ships today. Go, Python, and Rust follow. Each port is gated on the same executable compliance suite.