This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Anchor Personal Data Server (PDS) - a specialized ATProto server focused on app.dropanchor.checkin records, built for deployment on Val Town using Deno and SQLite.
This project follows Val Town's specific development guidelines:
- Generate code in TypeScript or TSX with appropriate types
- Never bake secrets into code - always use environment variables via
Deno.env.get('keyname') - Use
https://esm.shfor npm dependencies to ensure server/browser compatibility - Prefer official SDKs over direct API calls
- SQLite: Use
import { sqlite } from "https://esm.town/v/stevekrouse/sqlite"- Table schema changes require new table names (e.g., add
_v2,_v3) - Results accessed via
result.rowsarray format
- Table schema changes require new table names (e.g., add
- Blob Storage:
import { blob } from "https://esm.town/v/std/blob" - Email:
import { email } from "https://esm.town/v/std/email"
- Use Hono for API endpoints:
import { Hono } from "https://esm.sh/hono@3.11.7" - Never use
serveStaticmiddleware - use Val Town utilities instead - Entry point:
export default app.fetch - Always include error unwrapping:
app.onError((err, c) => { throw err; })
- Redirects: Use
new Response(null, { status: 302, headers: { Location: "/path" }}) - Storage: Do NOT use Deno KV module
- Browser APIs: Avoid
alert(),prompt(),confirm() - Error Handling: Let errors bubble up with context rather than catching and logging
The system implements a three-phase architecture:
-
Phase 1: Core Check-In Storage and Feeds
- Authentication via PDS-issued token validation
- SQLite database for check-in records
- Standard ATProto
com.atproto.repo.createRecordendpoints - User and global feed endpoints
-
Phase 2: Enhanced Features
- Geospatial "nearby" queries using bounding box pre-filtering + Haversine distance
- Optional feed cross-posting to user's home PDS (user-controlled setting)
-
Phase 3: Social Interactions
- Likes support via server-side proxy to Bluesky API
- Direct check-in comments using custom
app.dropanchor.commentrecords - On-demand interaction retrieval without caching
- Authentication: Trust PDS-issued tokens with validation via home PDS calls
- Record Creation: Standard ATProto compliance using
com.atproto.repo.createRecord - Federation: Full AT Protocol compliance for external client support
- Database: SQLite with proper indexing for performance (author, timestamp, location)
- Global Feed: Hard limit of 100 records maximum to prevent resource abuse
This is a Deno project configured for Val Town deployment:
- Lint:
deno lint(configured to exclude no-explicit-any rule) - Type Check:
deno check --allow-import <file>(requires import flag for Val Town types) - Run: Deploy to Val Town (no local dev server setup documented)
- Run Tests and Formatting: Always run tests and format code after big changes to ensure code quality and consistency
Following Val Town's recommended directory structure:
backend/
├── database/
│ ├── migrations.ts # Schema definitions with table versioning
│ └── queries.ts # DB query functions with proper type conversion
├── routes/
│ ├── atproto.ts # ATProto standard endpoints
│ └── anchor.ts # Anchor-specific endpoints
└── index.ts # Main Hono app entry point
shared/
├── auth.ts # Authentication utilities
└── types.ts # Shared types and validation functions
Core table structure using Val Town SQLite:
-- Current tables (change table names for schema updates)
anchor_checkins_v1 (
id TEXT PRIMARY KEY, -- record key (rkey)
authorDid TEXT NOT NULL, -- DID of user who created checkin
text TEXT NOT NULL, -- checkin message/venue name
createdAt TEXT NOT NULL, -- ISO timestamp
latitude REAL, -- WGS84 coordinates
longitude REAL,
placeName TEXT, -- venue/place name
address TEXT, -- full address or JSON of address components
uri TEXT NOT NULL, -- full AT URI (at://did/collection/rkey)
cid TEXT -- content hash (optional but recommended)
);
anchor_user_settings_v1 (
did TEXT PRIMARY KEY,
enableFeedPosts BOOLEAN DEFAULT TRUE
);Important: SQLite results use result.rows array format. Type conversion required:
// Convert SQLite values to proper types
id: String(row[0]),
latitude: row[4] ? Number(row[4]) : undefinedcom.atproto.repo.createRecord- Standard record creation (check-ins only)com.atproto.sync.getRecord- Individual record retrieval for federation/xrpc/app.dropanchor.listCheckins- User's own check-ins (authenticated)/xrpc/app.dropanchor.getGlobalFeed- Recent check-ins from all users (max 100)/xrpc/app.dropanchor.getNearbyCheckins- Geospatial queries with lat/lng/radius/xrpc/app.dropanchor.getCheckinLikes- Proxy to Bluesky likes API/xrpc/app.dropanchor.getCheckinComments- Direct check-in comments
- Platform: Val Town (Deno runtime)
- Database: Val Town hosted SQLite service
- Authentication: Token validation via external PDS calls with in-memory caching
- Federation: AT Protocol compliant
- API Framework: Hono with proper error unwrapping
- Entry Point:
backend/index.tsexportsapp.fetch - Production URL: https://anchorpds.val.run/
This project uses the Val Town CLI (vt) for local development and deployment:
- Create and edit files locally in this directory structure
- Use
deno check --allow-import <file>for type checking - Use
deno lintfor code linting - Test logic locally where possible
- Install:
npm install -g @valtown/vtordeno install -A -f https://esm.town/v/std/vt - Login:
vt loginto authenticate with Val Town - Create Val:
vt create <filename>to create a new val from local file - Update Val:
vt update <val-name> <filename>to update existing val - Push Changes:
vt pushto sync local directory with Val Town
- Develop and test locally using this directory structure
- Use
vt create backend/index.tsto deploy main HTTP endpoint - Database initialization runs automatically on startup
- Environment variables managed through Val Town dashboard
- CORS handled automatically by Val Town platform
- Monitor via Val Town dashboard logs and analytics
- Entry point:
backend/index.tsexportsapp.fetch - No local development server - deploy to Val Town to test HTTP endpoints
- Use Val Town dashboard for environment variables and secrets
- All imports must be compatible with Deno runtime