[READ-ONLY] Mirror of https://github.com/flo-bit/contrail. atproto backend in a bottle flo-bit.dev/contrail/
0

Configure Feed

Select the types of activity you want to include in your feed.

Version Packages

github-actions[bot] (Jun 7, 2026, 3:59 PM UTC) 7780dab4 93b448bd

+88 -42
-34
.changeset/feed-prune-bounded-sweep.md
··· 1 - --- 2 - "@atmo-dev/contrail-appview": minor 3 - --- 4 - 5 - fix(feeds): make feed_items pruning bounded so it can't reset the D1 DO 6 - 7 - The hourly feed prune ran a single global `ROW_NUMBER() OVER (PARTITION BY actor)` 8 - window + `(actor, uri) NOT IN (...)` anti-join across the entire `feed_items` 9 - table — O(n) CPU in one statement. Once the table grew large this exceeded D1's 10 - per-query CPU limit and reset the shared Durable Object, taking down any 11 - concurrent read on the same SQLite instance (unrelated user requests 500'd with 12 - `was reset` / `Network connection lost`). Because the statement reset before 13 - completing, caps were never enforced, the table kept growing, and the prune got 14 - more expensive — a death spiral. 15 - 16 - Changes: 17 - 18 - - **Bounded per-actor prune.** Pruning is now an index-backed cutoff delete per 19 - `(actor, collection)` using `idx_feed_actor_coll_time`, cost O(cap), never 20 - O(table). New `pruneActorFeed` / `sweepFeedItems` exports; the ingest loops 21 - run one bounded `sweepFeedItems` slice per tick (`FEED_PRUNE_SWEEP_ACTORS` 22 - actors), which also serves as recovery for already-bloated tables. 23 - - **Persisted prune cursor.** A new `feed_prune_cursor` row tracks the rolling 24 - sweep position, so progress survives the cron isolate recycling that 25 - previously made the in-memory hourly gate a no-op (it pruned on essentially 26 - every tick). The time gate is removed from the cron path; the long-lived 27 - persistent loop keeps a short in-memory throttle. 28 - - **API:** `pruneFeedItems(db, caps)` now accepts only the per-collection 29 - `Map<collection, cap>` (the legacy global-number form is removed) and is 30 - reimplemented as a bounded full-table recovery loop — keep it off the hot 31 - path. 32 - 33 - The follow fan-out's `subject` lookup is already covered by `idx_<follow>_subject`, 34 - so no unbounded statement remains in the ingest path.
+41
packages/contrail-appview/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-appview 2 2 3 + ## 0.9.0 4 + 5 + ### Minor Changes 6 + 7 + - 8f0b87e: fix(feeds): make feed_items pruning bounded so it can't reset the D1 DO 8 + 9 + The hourly feed prune ran a single global `ROW_NUMBER() OVER (PARTITION BY actor)` 10 + window + `(actor, uri) NOT IN (...)` anti-join across the entire `feed_items` 11 + table — O(n) CPU in one statement. Once the table grew large this exceeded D1's 12 + per-query CPU limit and reset the shared Durable Object, taking down any 13 + concurrent read on the same SQLite instance (unrelated user requests 500'd with 14 + `was reset` / `Network connection lost`). Because the statement reset before 15 + completing, caps were never enforced, the table kept growing, and the prune got 16 + more expensive — a death spiral. 17 + 18 + Changes: 19 + 20 + - **Bounded per-actor prune.** Pruning is now an index-backed cutoff delete per 21 + `(actor, collection)` using `idx_feed_actor_coll_time`, cost O(cap), never 22 + O(table). New `pruneActorFeed` / `sweepFeedItems` exports; the ingest loops 23 + run one bounded `sweepFeedItems` slice per tick (`FEED_PRUNE_SWEEP_ACTORS` 24 + actors), which also serves as recovery for already-bloated tables. 25 + - **Persisted prune cursor.** A new `feed_prune_cursor` row tracks the rolling 26 + sweep position, so progress survives the cron isolate recycling that 27 + previously made the in-memory hourly gate a no-op (it pruned on essentially 28 + every tick). The time gate is removed from the cron path; the long-lived 29 + persistent loop keeps a short in-memory throttle. 30 + - **API:** `pruneFeedItems(db, caps)` now accepts only the per-collection 31 + `Map<collection, cap>` (the legacy global-number form is removed) and is 32 + reimplemented as a bounded full-table recovery loop — keep it off the hot 33 + path. 34 + 35 + The follow fan-out's `subject` lookup is already covered by `idx_<follow>_subject`, 36 + so no unbounded statement remains in the ingest path. 37 + 38 + ### Patch Changes 39 + 40 + - @atmo-dev/contrail-base@0.9.0 41 + - @atmo-dev/contrail-authority@0.9.0 42 + - @atmo-dev/contrail-record-host@0.9.0 43 + 3 44 ## 0.8.0 4 45 5 46 ### Minor Changes
+1 -1
packages/contrail-appview/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-appview", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Public-records appview for contrail — jetstream ingestion, backfill, query layer, feeds, labels, profiles, per-collection XRPC routes.", 5 5 "type": "module", 6 6 "sideEffects": false,
+6
packages/contrail-authority/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-authority 2 2 3 + ## 0.9.0 4 + 5 + ### Patch Changes 6 + 7 + - @atmo-dev/contrail-base@0.9.0 8 + 3 9 ## 0.8.0 4 10 5 11 ### Patch Changes
+1 -1
packages/contrail-authority/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-authority", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Default space-authority implementation for contrail — member list, invites, app policy, credential issuance. Contrail's binary-membership ACL flavor; for ladder-style access levels see @atmo-dev/contrail-community.", 5 5 "type": "module", 6 6 "sideEffects": false,
+2
packages/contrail-base/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-base 2 2 3 + ## 0.9.0 4 + 3 5 ## 0.8.0 4 6 5 7 ### Minor Changes
+1 -1
packages/contrail-base/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-base", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "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.", 5 5 "type": "module", 6 6 "sideEffects": false,
+7
packages/contrail-community/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-community 2 2 3 + ## 0.9.0 4 + 5 + ### Patch Changes 6 + 7 + - @atmo-dev/contrail@0.9.0 8 + - @atmo-dev/contrail-base@0.9.0 9 + 3 10 ## 0.8.0 4 11 5 12 ### Minor Changes
+1 -1
packages/contrail-community/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-community", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Community module for contrail — community-owned spaces with tiered access levels (member → moderator → admin), invite tokens, DID provisioning, and the access-level reconciler that keeps spaces_members in sync.", 5 5 "type": "module", 6 6 "sideEffects": false,
+6
packages/contrail-record-host/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-record-host 2 2 3 + ## 0.9.0 4 + 5 + ### Patch Changes 6 + 7 + - @atmo-dev/contrail-base@0.9.0 8 + 3 9 ## 0.8.0 4 10 5 11 ### Patch Changes
+1 -1
packages/contrail-record-host/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-record-host", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Default record-host implementation for contrail — stores records and blobs for permissioned spaces, enforces local enrollment as the host's consent layer.", 5 5 "type": "module", 6 6 "sideEffects": false,
+10
packages/contrail/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail 2 2 3 + ## 0.9.0 4 + 5 + ### Patch Changes 6 + 7 + - Updated dependencies [8f0b87e] 8 + - @atmo-dev/contrail-appview@0.9.0 9 + - @atmo-dev/contrail-base@0.9.0 10 + - @atmo-dev/contrail-authority@0.9.0 11 + - @atmo-dev/contrail-record-host@0.9.0 12 + 3 13 ## 0.8.0 4 14 5 15 ### Minor Changes
+1 -1
packages/contrail/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Index AT Protocol records with typed XRPC endpoints. Cloudflare Workers + D1, SvelteKit, Node.js.", 5 5 "type": "module", 6 6 "sideEffects": false,
+6
packages/lexicons/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-lexicons 2 2 3 + ## 0.4.9 4 + 5 + ### Patch Changes 6 + 7 + - @atmo-dev/contrail@0.9.0 8 + 3 9 ## 0.4.8 4 10 5 11 ### Patch Changes
+1 -1
packages/lexicons/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-lexicons", 3 - "version": "0.4.8", 3 + "version": "0.4.9", 4 4 "description": "Generate atproto lexicon JSON (and optionally TypeScript types via @atcute/lex-cli) from a Contrail config.", 5 5 "type": "module", 6 6 "files": [
+2
packages/sync/CHANGELOG.md
··· 1 1 # @atmo-dev/contrail-sync 2 2 3 + ## 0.9.0 4 + 3 5 ## 0.8.0 4 6 5 7 ## 0.7.0
+1 -1
packages/sync/package.json
··· 1 1 { 2 2 "name": "@atmo-dev/contrail-sync", 3 - "version": "0.8.0", 3 + "version": "0.9.0", 4 4 "description": "Client-side reactive watch-store over contrail's watchRecords endpoints. SSE + WebSocket transports, optimistic updates, optional IndexedDB cache.", 5 5 "type": "module", 6 6 "sideEffects": false,