This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
This is the Anchor AppView - a location-based social feed generator and OAuth authentication system for the Anchor app, built on the AT Protocol (Bluesky) infrastructure. The system ingests check-in data from the decentralized AT Protocol network, provides spatial and social feeds, and includes a complete OAuth WebView implementation for mobile app integration.
main.tsx
(Val Town HTTP function with
// @val-town anchordashboard
comment)https://dropanchor.app
(configurable via ANCHOR_BASE_URL
env
var)HTTP Server (main.tsx) - Unified Hono server handling all routes:
OAuth Authentication System (backend/oauth/
) - Complete OAuth 2.0 +
DPoP implementation:
anchor-app://auth-callback
AppView Data Layer (backend/api/anchor-api.ts
) - AT Protocol compliant
feed system:
React Dashboard (frontend/
) - Web interface for authentication and feed
management
The system uses five main tables:
checkins_v1
- Main check-ins with coordinates and cached address dataaddress_cache_v1
- Cached venue/address information from StrongRef
resolutionprofile_cache_v1
- Cached user profile data (display names, avatars)user_follows_v1
- Social graph data for following-based feedsoauth_sessions
- OAuth session storage with DPoP keys and user profilesprocessing_log_v1
- Monitoring and operational loggingThe project includes several Deno tasks for common development workflows:
deno task quality
- Run formatter, linter, type checking, and all tests in
sequencedeno task deploy
- Deploy to Val Town (runs quality checks first)deno task fmt
- Format code using deno fmtdeno task lint
- Run linter to check code qualitydeno task check
- TypeScript type checking for all source filesdeno task test
- Run all tests with --allow-all
permissionsdeno task test:unit
- Run unit tests onlydeno task test:integration
- Run integration tests onlydeno task test:watch
- Run tests in watch mode for development./scripts/test.sh
- Run complete test suite (unit + integration)deno test --allow-all
- Run all tests directly./scripts/deploy.sh
- One-click deployment to Val Town using CLIvt create cron|http <name> --file <path>
- Deploy individual functionsnpm install -g @valtown/cli
deno run --allow-net scripts/debug.ts
- Check data availability and API
statusdeno run --allow-net scripts/debug.ts --check-data
- Check AT Protocol
records onlydeno run --allow-net scripts/debug.ts --test-api
- Test API endpoints onlyBase URL: https://dropanchor.app
/api/global
- Recent check-ins from all users with pagination/api/nearby
- Spatial queries for check-ins within specified radius/api/user
- User-specific check-ins/api/following
- Check-ins from followed users (requires social graph sync)/api/stats
- AppView health and statistics/api/places/nearby
- OpenStreetMap POI discovery via Overpass API/api/places/categories
- Complete category system for mobile app consumptionThe /api/places/categories
endpoint provides comprehensive category data
designed for mobile app consumption, enabling apps to cache and use the complete
category system locally.
Response Format:
{ "categories": [ { "id": "amenity_restaurant", "name": "Restaurant", "icon": "🍽️", "group": "FOOD_AND_DRINK", "osmTag": "amenity=restaurant" } // ... 56 total categories ], "defaultSearch": [ "amenity=restaurant", "amenity=cafe" // ... 37 categories optimized for default search queries ], "sociallyRelevant": [ "amenity=restaurant", "tourism=attraction" // ... 37 categories suitable for social check-ins ], "metadata": { "totalCategories": 56, "defaultSearchCount": 37, "sociallyRelevantCount": 37 } }
Usage Notes:
/client-metadata.json
- OAuth client metadata endpoint/api/auth/start
- Initiate OAuth flow (POST with handle)/oauth/callback
- Complete OAuth token exchange/api/auth/session
- Session validation for web and mobile (auto-extends
session lifetime)/api/auth/logout
- Session cleanup/api/auth/validate-mobile-session
- Mobile token validation with automatic
token refresh/api/auth/refresh-mobile-token
- Explicit mobile token refresh endpoint/mobile-auth
- Mobile-optimized OAuth login page for WebView integration/api/feed
- Dashboard feed data/api/admin/stats
- Administrative statistics/api/admin/backfill
- Manual data backfill operations/api/admin/discover-checkins
- Comprehensive checkin discovery/api/admin/backfill-profiles
- Profile data backfill/api/admin/resolve-addresses
- Address resolution operationsThe system includes a complete OAuth authentication flow designed for mobile app WebView integration with extended session duration (30+ days):
https://dropanchor.app/mobile-auth
anchor-app://auth-callback
with tokens/mobile-auth
endpointThe mobile callback URL includes all necessary authentication data:
anchor-app://auth-callback?access_token=...&refresh_token=...&did=...&handle=...&session_id=...&avatar=...&display_name=...
anchor-app
in Info.plistThe project is structured in phases:
The system deploys as a single unified HTTP function on Val Town:
anchordashboard
(main.tsx) - Unified Hono server handling
all routesdeno task deploy
- Runs quality checks and pushes to
Val Townvt push
) for deploymentserveFile
ANCHOR_BASE_URL
Deno.env.get('keyname')
import { sqlite } from "https://esm.town/v/std/sqlite2"
sqlite2
not sqlite
(the old path is deprecated)sqlite2
versionimport { blob } from "https://esm.town/v/std/blob"
https://esm.sh
for external dependenciescheckins_v1
→
checkins_v2
)IF NOT EXISTS
The new sqlite2 API uses object format for queries:
// CORRECT (sqlite2 format)
await sqlite.execute({
sql: "SELECT * FROM users WHERE id = ?",
args: [userId],
});
// INCORRECT (old format - will not work)
await sqlite.execute("SELECT * FROM users WHERE id = ?", [userId]);
The sqlite2 API returns { columns, rows }
where rows are arrays. Use helper
function:
// Helper function (already implemented in session.ts and storage-provider.ts)
function rowsToObjects(
columns: string[],
rows: any[][],
): Record<string, any>[] {
return rows.map((row) => {
const obj: Record<string, any> = {};
columns.forEach((column, index) => {
obj[column] = row[index];
});
return obj;
});
}
// Usage
const result = await sqlite.execute({ sql: "SELECT * FROM users", args: [] });
const objects = rowsToObjects(result.columns, result.rows);
(req: Request)
parameterIf you encounter Module not found
errors with SQLite:
https://esm.town/v/std/sqlite2
(not
sqlite
){ sql: "...", args: [...] }
rowsToObjects()
helper for objects// Common error patterns and fixes:
// ❌ WRONG - old import path
import { sqlite } from "https://esm.town/v/std/sqlite";
// ✅ CORRECT - new import path
import { sqlite } from "https://esm.town/v/std/sqlite2";
// ❌ WRONG - old API format
await sqlite.execute("SELECT * FROM users WHERE id = ?", [id]);
// ✅ CORRECT - new API format
await sqlite.execute({ sql: "SELECT * FROM users WHERE id = ?", args: [id] });
addressRef
StrongRefs that must be resolved to separate address records and cached{ "uri": "at://did:plc:example/app.dropanchor.checkin/abc123", "value": { "text": "Just testing my new check-in records", "$type": "app.dropanchor.checkin", "createdAt": "2025-07-06T15:03:15Z", "coordinates": { "$type": "community.lexicon.location.geo", "latitude": "52.080178", "longitude": "4.3578971" }, "addressRef": { "uri": "at://did:plc:example/community.lexicon.location.address/venue123", "cid": "bafyreicv3pecq6fuua22xcoguxep76otivb33nlaofzl76fpagczo5t5jm" } } }
When a check-in is created via the mobile app:
This eliminates the need for periodic crawling or real-time ingestion systems, providing the best user experience with instant feed updates.
coordinates
and addressRef
StrongRefaddress_ref_uri
and address_ref_cid
fieldscached_address_*
fields for fast API responsescoordinates
field
and resolves addressRef
StrongRefs to cache complete address dataThe project includes comprehensive testing:
assertAlmostEquals
for floating-point spatial calculationsThe system provides complete OAuth authentication for the Anchor iOS app:
anchor-app://auth-callback
with
all required parameterspds_url
in mobile callback
(line 486 in endpoints.ts
)anchor-app://auth-callback?access_token=...&refresh_token=...&did=...&handle=...&session_id=...&pds_url=...&avatar=...&display_name=...
deno task quality
to verify all tests passdeno task deploy
pds_url
parameter is included in mobile
redirectoauth_sessions
table for proper session storageThe system returns ISO8601 timestamps in two formats:
"2025-08-11T18:34:55.966Z"
(real-time data)"2025-08-11T18:34:55Z"
(legacy/processed
data)Ensure consistent date formatting for iOS client consumption:
The system is designed to scale within Val Town's resource limits while maintaining full AT Protocol compatibility for decentralized social networking.