A fullstack app for indexing standard.site documents
8
fork

Configure Feed

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

AT Feeds#

A monorepo for indexing and displaying Standard.site documents from the AT Protocol, powered by Cloudflare Workers, D1, and Queues.

Architecture#

┌─────────────────────────────────────────────────────────────┐
│                     Cloudflare                               │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  ┌──────────────┐     ┌──────────────┐     ┌─────────────┐  │
│  │    Pages     │────▶│   Worker     │────▶│     D1      │  │
│  │   (Client)   │     │   (API)      │     │  (Database) │  │
│  └──────────────┘     └──────────────┘     └─────────────┘  │
│                              ▲                    ▲          │
│                              │                    │          │
│                       ┌──────┴───────┐    ┌──────┴───────┐  │
│                       │    Queue     │    │     Cron     │  │
│                       │  (Resolver)  │    │  (Refresh)   │  │
│                       └──────┬───────┘    └──────────────┘  │
│                              │                              │
└──────────────────────────────┼──────────────────────────────┘
                               │ POST /webhook/tap
                    ┌──────────┴───────────┐
                    │   Tap Instance       │
                    │   (External VPS)     │
                    └──────────────────────┘

Components:

  1. Tap Indexer (External) - Subscribes to the AT Protocol firehose and sends webhook events
  2. Server (packages/server) - Cloudflare Worker with Hono API, D1 database, and Queue consumer
  3. Client (packages/client) - Vite + React app deployed to Cloudflare Pages

Quick Start#

Prerequisites#

  • Bun installed
  • Wrangler CLI installed and authenticated
  • A tap instance running somewhere (VPS, Fly.io, etc.)

Setup#

  1. Install dependencies:
bun install
  1. Create the D1 database:
bun run db:create

Copy the database ID and update packages/server/wrangler.toml.

  1. Create the queue:
wrangler queues create document-resolution
  1. Run database migrations:
# Local development
bun run db:migrate

# Production
bun run db:migrate:prod
  1. (Optional) Set webhook secret:
bun run secret:set
  1. Deploy the worker:
bun run deploy
  1. Configure your tap instance:
TAP_WEBHOOK_URL=https://your-worker.workers.dev/webhook/tap
TAP_SIGNAL_COLLECTION=site.standard.document
TAP_COLLECTION_FILTERS=site.standard.document
  1. Trigger initial resolution of existing records:
curl -X POST https://your-worker.workers.dev/admin/resolve-all

Local Development#

  1. Start the worker locally:
bun run dev:server

The API will run on http://localhost:8787.

  1. Start the client (in a separate terminal):
bun run dev:client

The client will run on http://localhost:5173.

API Endpoints#

Health & Stats#

Endpoint Method Description
/health GET Health check
/stats GET Database statistics

Feed Endpoints#

Endpoint Method Description
/feed GET Pre-resolved documents (fast)
/feed-raw GET Raw record references (for client-side resolution)
/records/:did GET Records by DID

Webhook#

Endpoint Method Description
/webhook/tap POST Receives events from tap
/webhook/tap/debug POST Debug endpoint (echoes payload)

Admin#

Endpoint Method Description
/admin/resolve-all POST Queue unresolved records for processing

How It Works#

  1. Tap subscribes to the AT Protocol firehose and filters for site.standard.document records
  2. Webhook receives events and stores record references in D1, then pushes to the resolution queue
  3. Queue consumer resolves each document (PDS lookup → record fetch → publication URL) and stores in resolved_documents
  4. Cron job (every 15 min) refreshes stale documents and processes any missed records
  5. /feed endpoint reads directly from resolved_documents for instant responses

Project Structure#

.
├── package.json            # Root workspace config
└── packages/
    ├── server/             # Cloudflare Worker
    │   ├── wrangler.toml   # Worker configuration
    │   ├── schema.sql      # D1 database schema
    │   ├── package.json
    │   └── src/
    │       └── index.ts    # API + Queue consumer + Cron handler
    └── client/             # Vite + React app
        ├── package.json
        ├── vite.config.ts
        └── src/
            ├── main.tsx
            └── App.tsx

Scripts#

# Development
bun run dev              # Run all packages in dev mode
bun run dev:server       # Run worker locally
bun run dev:client       # Run client locally

# Deployment
bun run deploy           # Deploy worker to Cloudflare
bun run deploy:client    # Deploy client to Cloudflare Pages

# Database
bun run db:create        # Create D1 database
bun run db:migrate       # Run migrations (local)
bun run db:migrate:prod  # Run migrations (production)

# Secrets
bun run secret:set       # Set TAP_WEBHOOK_SECRET

Resources#

License#

MIT