The fastest
line down the channel.
Thalweg carries records from streaming and batch sources into analytical and operational stores — with the whole path, from wire to table, memory- and type-safe in one language.
thal·weg /ˈtɑːlveɡ/ — the line of fastest flow, the deepest continuous channel down a watercourse. In law, the boundary drawn along a river: the line that carries the authority.
TWG — short for Thalweg, and read as Throughput With Guarantees: move a great deal of data, and never lose any of it.
*Two documented exceptions, both opt-in and off by default. Every other path is pure Rust with no C dependency on the hot path.
01How it works
A record enters from a source — a broker, a file, an Arrow stream. It is decoded once into a shared columnar form, checked against its data contract, transformed by SQL if you want it to be, and written to one or more sinks. Along the way every record carries its origin, so if a destination falls behind it can be rebuilt from the durable copy rather than re-read from the source.
Every box on the channel is a stage you can observe independently — its throughput, its memory, and whether it's healthy or building backpressure — at /health and /stats.
02What's supported
Thalweg is pre-implementation: the architecture is decided and the workspace is scaffolded, but code has not yet been written. The table below is honest about that. It shows where each capability stands today, and what is planned but not yet started — for example RabbitMQ, NATS, and other transports are planned, not built.
Sources
| Capability | Status | Notes |
|---|---|---|
| Apache Kafka | Scaffolded | Pure-Rust client; consumer groups incl. cooperative-sticky |
| Apache Pulsar | Scaffolded | Fresh binary-protocol client; staged subscription and memory-aware shedding designed in |
| Batch files | Scaffolded | Parquet, CSV, JSON, XML, Excel, Avro OCF |
| Arrow Flight (gRPC) | Scaffolded | Already-Arrow, zero decode |
| Arrow IPC (file & stream) | Scaffolded | Non-Flight raw IPC |
| Delta Sharing (recipient) | Scaffolded | Cross-org read via pre-signed URLs |
| Arrow C Data Interface | Scaffolded | In-process zero-copy handoff from PyArrow/Go/C++/Java |
| RabbitMQ | Planned | Not yet started |
| NATS / JetStream | Planned | Not yet started |
| AWS Kinesis | Planned | Not yet started |
| MQTT | Planned | Not yet started |
| REST (poll / webhook) | Planned | Noted for future consideration |
| WebSocket (WSS) | Planned | Noted for future consideration |
Payload formats
| Format | Status | Notes |
|---|---|---|
| Avro (registry + OCF) | Scaffolded | Confluent / Glue / Apicurio wire framing; OCF shares decode |
| Protobuf | Under review | Decode strategy open: zero-copy descriptor-driven parser, gated on licence + benchmark |
| JSON | Scaffolded | Schema-driven + inferred |
| XML | Scaffolded | quick-xml + bespoke XSD/contract-driven mapping |
| Custom binary (WASM) | Scaffolded | Batch-oriented WASM decoder; native compile-in exception |
Sinks & table formats
| Target | Status | Notes |
|---|---|---|
| PostgreSQL | Scaffolded | Binary COPY; Postgres 18+; primary-raw eligible |
| Databricks (Zerobus) | Scaffolded | Arrow Flight → Delta; primary-raw eligible where the table has Iceberg reads enabled, read back via the catalog's Iceberg REST endpoint |
| Parquet (object store) | Scaffolded | Plain files; primary-raw eligible, no catalog needed |
| Apache Iceberg | Scaffolded | Read + write, catalog-mediated. The one open table format carried; reader feeds recovery |
| Arrow Flight / IPC | Scaffolded | First-class out-transports |
| DuckLake | Planned | Source & sink; treated like other DB targets |
| REST / WebSocket | Planned | Push/forward sink; noted for future consideration |
Platform capabilities
| Capability | Status | Notes |
|---|---|---|
| Data contracts (ODCS) | Scaffolded | Schema authority + quality-rule merge; local/HTTP/object-store |
| DQ audit trail | Scaffolded | Full-grain per-record per-rule; durable sink; on by default |
| Raw-anchored recovery | Design complete | Bounded windowed replay, dedup on record ID; runs as a separate scale-to-zero role |
| SQL transforms (DataFusion) | Scaffolded | DAG with interior sinks, materialised intermediates |
| Health & observability | Scaffolded | Per-stage health tree, OTLP, Prometheus, /stats |
| K8s / GitOps config | Scaffolded | Verbatim TOML delivery; secret enumeration |
| Pulsar flow control | Design complete | Staged subscription, topic-spread shedding, byte-budget admission, pressure-as-scale-signal |
| Java / Python facades (C ABI) | Design complete | Reserved; deferred (design-for, don't-build) |
03Design principles
Five ideas shape every decision in Thalweg. They are worth reading before the architecture, because the rest follows from them.
No C-linked broker client on the hot path. Codec and auth crates carry no async runtime, so they are reusable anywhere — a Lambda, a query engine, a transport not yet written.
Raw bytes, a neutral value model, and Apache Arrow — none privileged. Pass through untouched, decode per-record, or decode straight to columnar batches.
Kafka and Pulsar implement the same Source/Sink interface, so flow control, health, retry, and recovery are written once and shared across every transport.
Any replayable sink can hold the primary-raw role. The source is acknowledged only once raw is durable; lagging sinks rebuild from raw, never by re-reading the source.
Forty-four architecture decision records, a scenario catalogue, and encoded regression tests — the hard-won lessons are permanent, not tribal knowledge.
The layering, the single Arrow version, the confinement of unsafe code — all enforced in CI, so the architecture cannot erode silently under delivery pressure.
04The layers
Crates are grouped into layers with a strictly downward dependency direction, enforced in CI. Nothing in the codec layer may depend on a transport; nothing in the codec or SASL layer may depend on an async runtime.
Reading bottom to top: raw bytes come in through transport primitives, up through sources into the shared connector contract, get decoded and validated, transformed, and written out. The codec-core crate holds the single Arrow re-export — the one place the Arrow version is named, so the whole workspace pins one version.
05Crate reference
Forty-three crates. Filter by layer, or read straight through. Every crate is currently a scaffold — the descriptions are the contract each will fulfil.
06Recovery & guarantees
The correctness backbone is one rule: the source offset advances only after a sink has durably acknowledged the record — never on send, never on buffered. In a multi-sink setup, the sink that gates the source is the one holding the primary-raw role. Everything else recovers from raw.
durable ack
Server-side ack (Zerobus), transaction commit (Postgres), or flush+close (Parquet) — before the source advances.
deterministic IDs
Each record hashes to a stable ID including its source cluster, so replay deduplicates perfectly.
bounded windows
Recovery replays in time- and row-bounded windows, so catch-up never runs memory away.
unified DLQ
Decode, contract, and quality failures land in one dead-letter path, tagged by reason — never mixed with dropped test traffic.
07Decision records
Every significant choice is recorded as an ADR, so the reasoning survives the people. Four remain genuinely open, pending trade studies or production input; the rest are decided and awaiting drafting into full prose.
The full index of all 44 records lives in docs/adr/README.md. Each will be drafted in the house ADR style and synced to Confluence.
08Why this name
The thalweg is the line of fastest flow — the deepest continuous channel down a watercourse, the path water takes when it has a choice. For an engine whose job is the efficient path from source to destination, that is the thesis in one word.
It carries a second meaning that happens to describe a real design decision. In international law, the thalweg is the boundary drawn along a river: the line that carries the authority. That is precisely how this system treats its primary-raw lane — one sink holds the authoritative copy, the source is acknowledged against it, and everything else is reconciled back to it.
The project was drafted under the working name Millrace. That name turned out to be published on crates.io already — by another streaming event system, a direct collision in this project's own domain — so it was changed before anything was pushed or published. Thalweg is also a watercourse term, which is why the channel diagrams, the sluice-gate stages, and the weathered-copper palette on this page all survived the rename intact.
What else was considered
Other water-engineering candidates were checked for availability and brand collisions. penstock had the best literal meaning — the pressurised conduit feeding a turbine — but collides with a healthcare vendor whose product is audit software, an unfortunate neighbour given this system's own audit trail. headrace collides with an HR-tech company. adit was free but reads as a mis-typing of "audit". sluice, weir, leat, grist, winnow, spillway, braid and riffle were all already published crates.
A bare three-letter acronym with no parent word was considered and rejected: the available combinations were either colliding — trc and drc are smart-pointer crates, tde is Transparent Data Encryption, avc is H.264 video — or characterless. Deriving the prefix from a real word gives both a memorable name and a short prefix. Within that, thw was rejected in favour of twg: it is the well-known abbreviation for Technisches Hilfswerk, Germany's federal civil-protection agency, an awkward adjacency for a German-named organisation.
Crates are prefixed twg-; the command is twg, with twg stream, twg batch and twg config as subcommands.
09Document map
Where to find things in the repository.
| You want… | Look in |
|---|---|
| This overview & navigation | docs/site/index.html |
| Why a decision was made | docs/adr/ |
| The delivery plan & phases | docs/blueprints/ |
| How config & GitOps work | docs/operations/CONFIG.md |
| Health & observability model | docs/operations/HEALTH.md |
| Batch reader library choices | docs/operations/BATCH-READERS.md |
| Test scenarios & regressions | docs/testing/ |
| Working here as an agent | AGENTS.md |