···11----
22-"@atmo-dev/contrail-appview": patch
33----
44-55-fix(ingest): stop the ingest cycle hanging on a quiet or all-filtered Jetstream
66-77-Race `iterator.next()` against the safety timeout and check the exit conditions before each event, so a low-traffic stream no longer blocks until the caller's hard timeout kills the cycle with the batch and cursor unwritten.
-12
.changeset/sinks-seam.md
···11----
22-"@atmo-dev/contrail-base": minor
33-"@atmo-dev/contrail-appview": minor
44----
55-66-Add a first-class `sinks` config option: write-only, post-commit observers of applied records.
77-88-A `Sink` builds derived state (a search index, an audit log, a webhook fan-out) from every record contrail ingests. Each configured sink's `onRecords(events, { phase })` fires inside `applyEvents()` after the DB commit, on **both** the live and backfill paths, receiving one deduplicated `RecordEvent` per record. Failures are isolated — a throwing sink is logged via the configured logger and never blocks ingestion.
99-1010-Unlike `realtime.pubsub`, a sink is not a subscriber: it serves no reads, requires no ticket secret, and must see backfilled records (where realtime is intentionally silent). Public records only — space-scoped records publish via the publishing adapter and never reach the fan-out.
1111-1212-Purely additive: `realtime` and all existing behavior are unchanged. Runs identically on D1 and Postgres (it is an in-process call after commit, not a database-log consumer).
+23
packages/contrail-appview/CHANGELOG.md
···11# @atmo-dev/contrail-appview
2233+## 0.12.0
44+55+### Minor Changes
66+77+- 6b34d87: Add a first-class `sinks` config option: write-only, post-commit observers of applied records.
88+99+ A `Sink` builds derived state (a search index, an audit log, a webhook fan-out) from every record contrail ingests. Each configured sink's `onRecords(events, { phase })` fires inside `applyEvents()` after the DB commit, on **both** the live and backfill paths, receiving one deduplicated `RecordEvent` per record. Failures are isolated — a throwing sink is logged via the configured logger and never blocks ingestion.
1010+1111+ Unlike `realtime.pubsub`, a sink is not a subscriber: it serves no reads, requires no ticket secret, and must see backfilled records (where realtime is intentionally silent). Public records only — space-scoped records publish via the publishing adapter and never reach the fan-out.
1212+1313+ Purely additive: `realtime` and all existing behavior are unchanged. Runs identically on D1 and Postgres (it is an in-process call after commit, not a database-log consumer).
1414+1515+### Patch Changes
1616+1717+- 91278c6: fix(ingest): stop the ingest cycle hanging on a quiet or all-filtered Jetstream
1818+1919+ Race `iterator.next()` against the safety timeout and check the exit conditions before each event, so a low-traffic stream no longer blocks until the caller's hard timeout kills the cycle with the batch and cursor unwritten.
2020+2121+- Updated dependencies [6b34d87]
2222+ - @atmo-dev/contrail-base@0.12.0
2323+ - @atmo-dev/contrail-authority@0.12.0
2424+ - @atmo-dev/contrail-record-host@0.12.0
2525+326## 0.11.0
427528### Minor Changes
···11# @atmo-dev/contrail-base
2233+## 0.12.0
44+55+### Minor Changes
66+77+- 6b34d87: Add a first-class `sinks` config option: write-only, post-commit observers of applied records.
88+99+ A `Sink` builds derived state (a search index, an audit log, a webhook fan-out) from every record contrail ingests. Each configured sink's `onRecords(events, { phase })` fires inside `applyEvents()` after the DB commit, on **both** the live and backfill paths, receiving one deduplicated `RecordEvent` per record. Failures are isolated — a throwing sink is logged via the configured logger and never blocks ingestion.
1010+1111+ Unlike `realtime.pubsub`, a sink is not a subscriber: it serves no reads, requires no ticket secret, and must see backfilled records (where realtime is intentionally silent). Public records only — space-scoped records publish via the publishing adapter and never reach the fan-out.
1212+1313+ Purely additive: `realtime` and all existing behavior are unchanged. Runs identically on D1 and Postgres (it is an in-process call after commit, not a database-log consumer).
1414+315## 0.11.0
416517## 0.10.0
+1-1
packages/contrail-base/package.json
···11{
22 "name": "@atmo-dev/contrail-base",
33- "version": "0.11.0",
33+ "version": "0.12.0",
44 "description": "Shared infrastructure for the contrail family of packages — interfaces (SpaceAuthority, RecordHost, CommunityIntegration), credential primitives, binding resolvers, realtime infra, schema scaffolding. No routes, no tables of its own.",
55 "type": "module",
66 "sideEffects": false,