# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## What This Is 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](https://github.com/mary-ext/atproto-scraping) and gets enriched with health, geo-IP, version, and trust metadata. ## Commands ```bash 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. ## Architecture **Runtime:** Deno on Val Town. **Framework:** Hono. **Database:** Val Town SQLite. **Imports:** via `esm.sh` (not npm/node_modules). ### Entry Points - `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. ### Data Flow (Cron) 1. `pds-fetcher.ts` downloads state.json (~2900 PDSes) 2. Each PDS upserted to SQLite with open/closed status 3. `version-checker.ts` gets latest PDS version from GitHub API 4. `pds-enricher.ts` enriches a batch (configurable, default 20): DNS resolve → health check → describeServer → listRepos user count → geo-IP batch lookup 5. Results written back to DB via `updateEnrichment()` ### Module Layout - `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-checker` - `shared/` — `types.ts` (all TypeScript interfaces), `constants.ts` (config values, country flag helper) - `cron/` — Scheduled job orchestration ### Trust Score 5 boolean signals, each worth 20%: contact email, ToS, privacy policy, active users (>5), latest PDS version. Calculated in SQL via CASE expressions. ## Val Town Specifics - 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 `Deno` namespace in `shared/` code (must work in browser context too) - Secrets via `Deno.env.get()`, never hardcoded ## Conventions - 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