openpds
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v70View latest version
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
OpenPDS is a directory of AT Protocol PDS servers with open registration. It runs on Val Town as a serverless app with two entry points: an HTTP server and a daily cron job. Data comes from mary-ext/atproto-scraping and gets enriched with health, geo-IP, version, and trust metadata.
deno task check # Type check entry points deno task test # Run tests (--allow-net --allow-read --allow-import) deno fmt # Format all files deno lint # Lint all .ts files deno task deploy # fmt + lint + check + test + vt push (full deploy pipeline)
Do not use vt push directly — always use deno task deploy which runs
quality checks first.
Runtime: Deno on Val Town. Framework: Hono. Database: Val Town
SQLite. Imports: via esm.sh (not npm/node_modules).
backend/index.http.ts— HTTP trigger. Hono app serving HTML directory (/) and JSON API (/api/servers). Runs migrations on first request.cron/refresh.cron.ts— Cron trigger. Daily refresh: fetch PDS list → sync to DB → fetch latest version → enrich batch of servers.
pds-fetcher.tsdownloads state.json (~2900 PDSes)- Each PDS upserted to SQLite with open/closed status
version-checker.tsgets latest PDS version from GitHub APIpds-enricher.tsenriches a batch (configurable, default 20): DNS resolve → health check → describeServer → listRepos user count → geo-IP batch lookup- Results written back to DB via
updateEnrichment()
backend/database/—migrations.ts(schema),queries.ts(all SQL operations, row-to-object mapping)backend/routes/—pages.ts(server-rendered HTML),api.ts(JSON endpoint)backend/services/— Pure async functions:pds-fetcher,pds-enricher,geo-resolver,version-checkershared/—types.ts(all TypeScript interfaces),constants.ts(config values, country flag helper)cron/— Scheduled job orchestration
5 boolean signals, each worth 20%: contact email, ToS, privacy policy, active users (>5), latest PDS version. Calculated in SQL via CASE expressions.
- Exports use Val Town conventions: HTTP files export
app.fetch, cron files export a default async function - SQLite accessed via
https://esm.town/v/stevekrouse/sqlite - Schema changes: create new table names (append
_2,_3) rather than ALTER TABLE - No
Denonamespace inshared/code (must work in browser context too) - Secrets via
Deno.env.get(), never hardcoded
- Database columns:
snake_case. TypeScript code:camelCase.rowToServer()maps between them. - Per-request timeouts via
AbortSignal.timeout()(5s for PDS endpoints, 10s for geo batch) - Batch operations use
Promise.allSettled()for partial-failure resilience - HTML output uses custom
esc()function for XSS prevention