[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.

README.md

Contrail — PostgreSQL Example#

A complete example of using Contrail to index AT Protocol calendar events and RSVPs with PostgreSQL. Includes persistent Jetstream ingestion (long-running listener) and user discovery/backfill.

Setup#

# Copy this folder to a new project
cp -r examples/postgres my-contrail-app
cd my-contrail-app

# Install dependencies
npm install

Note: The contrail dependency in package.json points at github:flo-bit/contrail. If you're using a fork with PostgreSQL support that hasn't been merged yet, update the dependency to point at your fork's branch:

npm install github:your-username/contrail#your-branch

Or install from a local checkout:

npm install /path/to/your/contrail

Start PostgreSQL#

Option A: Docker (recommended)

docker compose up -d

This starts PostgreSQL on port 5433 (to avoid conflicts with a local instance) with a contrail database ready to use. Override the port with PG_PORT=5432 docker compose up -d.

Option B: Native PostgreSQL

createdb contrail

Set DATABASE_URL to point at your local instance:

export DATABASE_URL="postgresql://user:password@localhost:5432/contrail"

Configure#

Edit config.ts to define your collections, queryable fields, relations, and references. See the Contrail README for all options.

Run#

1. Discover users and backfill records#

npm run sync

This finds users from ATProto relays and backfills their existing records from PDS. Safe to interrupt and restart — progress is saved per-DID in the database.

2. Start persistent ingestion#

npm run ingest

This opens a long-lived Jetstream connection and continuously indexes new records as they appear on the network. Events are batched and flushed every 5 seconds (or every 50 events, whichever comes first). Handles reconnection automatically.

Press Ctrl+C for graceful shutdown — the current batch is flushed and the cursor is saved so the next run picks up where it left off.

3. Serve the XRPC API#

npm run serve

Your XRPC API is now available at http://localhost:3000:

# List events sorted by RSVP count
/xrpc/rsvp.atmo.event.listRecords?sort=rsvpsCount

# Upcoming events with 10+ going RSVPs
/xrpc/rsvp.atmo.event.listRecords?startsAtMin=2026-03-16&rsvpsGoingCountMin=10

# Single event with hydrated RSVPs and profiles
/xrpc/rsvp.atmo.event.getRecord?uri=at://...&hydrateRsvps=10&profiles=true

# Search events
/xrpc/rsvp.atmo.event.listRecords?search=meetup

# RSVPs for a specific event
/xrpc/rsvp.atmo.rsvp.listRecords?subjectUri=at://...

Running everything together#

In production you'd typically run sync once (or periodically), then keep ingest and serve running as separate processes:

# Initial sync (run once, or periodically to discover new users)
npm run sync

# In separate terminals (or use a process manager)
npm run ingest
npm run serve