A TanStack Start + React web app for the AT Protocol app store / directory, backed by Postgres (Drizzle ORM, pgvector) and ATProto OAuth.
# 1. Install Postgres + pgvector once (macOS):
brew install postgresql@17 pgvector
brew services start postgresql@17
createdb at_store
# 2. Clone and bootstrap:
git clone https://github.com/<your-fork>/at-store.git
cd at-store
pnpm install
pnpm run setup # copies .env, verifies Postgres, enables pgvector, runs migrations
pnpm dev # http://127.0.0.1:3000| Tool | Why |
|---|---|
| Node.js 20+ (22 LTS recommended) | Runtime for Vite + scripts |
| pnpm 10+ | corepack enable or npm i -g pnpm |
| Postgres 14+ with pgvector | App database (see Install Postgres) |
pg_dump / psql (usually bundled with Postgres) |
Used by pnpm db:backup and direct DB access |
goat (optional) |
Publish/lint ATProto lexicons (brew install goat) |
You only need to do this once per machine.
macOS (Homebrew)
brew install postgresql@17 pgvector
brew services start postgresql@17
createdb at_storeThe default Homebrew install creates a superuser matching your macOS
username with no password, listening on localhost:5432. Update
DATABASE_URL in .env accordingly (see step 3).
macOS (Postgres.app)
- Install Postgres.app and start it.
brew install pgvector(Postgres.app uses Homebrew's extension dir).createdb at_store.
Linux (Debian/Ubuntu)
sudo apt install postgresql-17 postgresql-17-pgvector
sudo -u postgres createdb at_store
sudo -u postgres createuser -s $USER # if you don't already have a rolepnpm run setup enables the vector extension automatically — you don't
need to run CREATE EXTENSION by hand.
pnpm installpnpm run setupThis is idempotent and does the following:
- Copies
.env.example→.env(only if.envdoesn't already exist). - Connects to
DATABASE_URLand prints the Postgres version. - Runs
CREATE EXTENSION IF NOT EXISTS vector. - Runs Drizzle migrations (
pnpm db:migrate). - Seeds a handful of demo listings so the home page renders something
immediately (
pnpm db:seed).
If the connection or pgvector check fails, the script prints the exact install command for your platform.
The default DATABASE_URL in .env.example is
postgresql://postgres:postgres@localhost:5432/at_store. If your local
Postgres uses a different user (e.g. your macOS username with no
password from Homebrew), edit .env to match — for example:
DATABASE_URL=postgresql://yourname@localhost:5432/at_store
The app boots without any API keys, but most interesting features need at
least one of the following. Open .env and add what you have:
| Variable | What it unlocks |
|---|---|
ANTHROPIC_API_KEY |
LLM-powered scrapers, listing copy, taxonomy generation |
GEMINI_API_KEY |
Image generation scripts (generate:*) |
ATSTORE_IDENTIFIER + ATSTORE_APP_PASSWORD |
Publishing listings to the @store ATProto repo, admin actions |
ATPROTO_BASE_URL |
OAuth redirect URI — defaults to http://127.0.0.1:3000 (use 127.0.0.1, not localhost, for ATProto OAuth) |
JETSTREAM_URL / TAP_URL |
Background ingestion consumers (see below) |
.env.example documents every supported variable.
pnpm dev # http://127.0.0.1:3000pnpm db:migrate # apply pending migrations
pnpm db:generate # generate a new migration from schema changes
pnpm db:seed # insert a handful of demo listings (idempotent, local-only)
pnpm db:studio # browse data with Drizzle Studio
pnpm db:backup # pg_dump → ./backups (override with DB_BACKUP_DIR)pnpm db:seed refuses to run against any host other than localhost /
127.0.0.1 unless ALLOW_REMOTE_SEED=1 is set.
If you have a pg_dump -Fc file (e.g. from pnpm db:backup or a teammate):
pg_restore --clean --if-exists -d at_store path/to/file.dumpdropdb at_store && createdb at_store && pnpm run setupThe app is split into one frontend + a few optional background workers. None of the workers are required to develop the UI — they only matter if you want live ATProto data flowing into your local DB.
| Command | What it does | Required for local UI? |
|---|---|---|
pnpm dev |
TanStack Start dev server on :3000 |
Yes |
pnpm tap:consumer |
Ingests fyi.atstore.listing.* records into Postgres (needs a Tap server reachable via TAP_URL) |
No |
pnpm jetstream:consumer |
Tracks Bluesky post mentions → trending scores | No |
Each consumer is meant to run in its own terminal. They are restart-safe: both keep cursor state in Postgres so you can stop and start them freely.
See package.json for the full list of generate:*, backfill:*, and
scrape:* one-off scripts.
src/routes/— TanStack Router file-based routes (_admin-layout/_header-layoutsegments)src/db/— Drizzle schema, queries, and connection setupsrc/integrations/— ATProto OAuth, TanStack Query, external APIssrc/design-system/— Shared UI primitives (autocomplete, popovers, theme)lexicons/— ATProto lexicons underfyi.atstore.*drizzle/— Generated SQL migrations (the source of truth — never edit by hand)scripts/— One-off and recurring CLI scripts (scraping, generation, consumers, dev setup)
pnpm run setup fails with "Postgres is not reachable".
Make sure Postgres is running (brew services list on macOS,
systemctl status postgresql on Linux) and that DATABASE_URL in
.env matches your local user/password/port. Many Homebrew installs
expect postgresql://$USER@localhost:5432/at_store (no password).
pnpm run setup says extension "vector" is not available.
You need pgvector installed alongside Postgres:
- macOS:
brew install pgvector && brew services restart postgresql@17 - Debian/Ubuntu:
sudo apt install postgresql-17-pgvector
OAuth sign-in redirects to localhost and breaks.
ATProto's public-client OAuth requires 127.0.0.1, not localhost.
Visit the dev server at http://127.0.0.1:3000 and make sure
ATPROTO_BASE_URL=http://127.0.0.1:3000 in .env.
This project’s source code is licensed under the Apache License,
Version 2.0 (SPDX: Apache-2.0). See LICENSE. Copyright
notice: NOTICE.
Anyone is welcome to reuse and adapt this codebase under the Apache license (similar to how Bluesky core uses permissive licenses).
This site uses a custom visual design—layout, typography, imagery, and the AT Store wordmark. Please do not reuse that design wholesale for another product or site without asking first.
The AT Store wordmark and related branding are © Atproto Community Collective. They are not licensed for unrestricted reuse the same way the program source is; the distinction between code and visual identity is intentional.