A complete location-based social feed generator and web interface built on the AT Protocol for decentralized social networking. Provides both API endpoints for mobile clients and a web interface for viewing and sharing check-ins.
The system consists of 4 main components:
- Ingestion - Real-time check-in data ingestion from AT Protocol Jetstream
- API - RESTful APIs for global, nearby, user, and following feeds
- Web Interface - React-based web frontend for viewing feeds and shareable checkin detail pages
- Social - Social graph sync from Bluesky for following feeds
src/
├── ingestion/ # Data ingestion from AT Protocol
│ └── jetstream-poller.ts
├── api/ # HTTP API endpoints
│ └── anchor-api.ts
├── social/ # Social graph management
│ └── social-graph-sync.ts
└── utils/ # Shared utilities
├── handle-resolver.ts
├── address-resolver.ts
├── address-cache.ts # Val Town blob storage cache
├── profile-resolver.ts # Profile caching and resolution
└── profile-refresh-job.ts # Background profile refresh
frontend/
├── main.tsx # React app entry point and routing
├── components/ # React components
│ ├── CheckinDetail.tsx # Individual checkin detail view with map
│ ├── Feed.tsx # Global feed component
│ └── Layout.tsx # App layout and navigation
└── types/
└── index.ts # TypeScript type definitions
database/
├── database-schema.sql # SQLite schema and indexes
└── add-profile-cache.sql # Profile cache migration
docs/
├── api-documentation.md # Complete API reference for client development
└── deployment-guide.md # Deployment instructions
scripts/
├── deploy.sh # One-click deployment script
├── test.sh # Run complete test suite
├── debug.ts # Debug data availability and API status
└── monitor-api.sh # Monitor deployed API status
tests/
├── unit/ # Unit tests for individual functions
│ ├── handle-resolver.test.ts
│ ├── profile-resolver.test.ts # Profile caching and resolution
│ ├── address-cache.test.ts
│ ├── database.test.ts
│ └── spatial.test.ts
├── integration/ # Integration tests for API endpoints
│ ├── api.test.ts
│ └── api-profiles.test.ts # API endpoints with profile data
└── fixtures/ # Test data and fixtures
└── test-data.ts
# Install Val Town CLI npm install -g @valtown/cli # Login to Val Town vt login # Run tests first ./scripts/test.sh # Deploy all functions ./scripts/deploy.sh # Monitor the deployment ./scripts/monitor-api.sh
# Deploy individual functions vt create cron jetstreamPoller --file src/ingestion/jetstream-poller.ts --schedule "*/5 * * * *" vt create http anchorAPI --file src/api/anchor-api.ts vt create cron socialGraphSync --file src/social/social-graph-sync.ts --schedule "0 2 * * *"
Note: No manual database setup required! Tables are created automatically when functions first run.
Base URL: https://dropanchor.app
GET https://dropanchor.app/api/global?limit=50&cursor=2025-06-29T15:00:00Z
Recent check-ins from all users with pagination.
GET https://dropanchor.app/api/nearby?lat=52.0705&lng=4.3007&radius=5&limit=50
Spatial query for check-ins within specified radius (km).
GET https://dropanchor.app/api/user?did=did:plc:abc123&limit=50
All check-ins from a specific user.
GET https://dropanchor.app/api/following?user=did:plc:abc123&limit=50&cursor=2025-06-29T15:00:00Z
Check-ins from people the specified user follows on Bluesky.
GET https://dropanchor.app/api/checkin/{rkey}
Individual checkin data by record key for detail views and sharing.
GET https://dropanchor.app/api/stats
AppView health metrics and processing statistics.
https://dropanchor.app/
Web interface showing the global feed of check-ins.
https://dropanchor.app/checkin/{rkey}
Individual checkin detail page with interactive map, optimized for sharing on social media with Open Graph meta tags.
For complete API documentation including examples, data models, and SDK code samples, see:
The system uses 5 main SQLite tables:
checkins_v1
- Main check-ins with coordinates and cached address dataaddress_cache_v1
- Resolved venue/address information from strongrefsprofile_cache_v1
- Cached user profile data (display names, avatars)user_follows_v1
- Social graph data for following-based feedsprocessing_log_v1
- Monitoring and operational logging
- Real-time Ingestion: WebSocket polling every 5 minutes from Jetstream
- Address Resolution: Automatic strongref resolution with caching
- Profile Resolution: Automatic profile data fetching and caching
- Spatial Queries: Nearby check-ins using Haversine distance calculations
- Social Integration: Following feeds leveraging Bluesky's social graph
- Performance: SQLite with proper indexing for fast queries
- Error Handling: Comprehensive error tracking and retry logic
- Global Feed View: Browse all check-ins with author profiles and timestamps
- Shareable Checkin Pages: Individual checkin detail pages with interactive maps
- Interactive Maps: OpenStreetMap integration via Leaflet with CartoDB tiles
- Social Sharing: Optimized sharing with Web Share API and copy functionality
- Responsive Design: Mobile-friendly interface using system fonts
- Open Graph Tags: Rich social media previews for shared checkin URLs
- Share URL Collisions: Currently using AT Protocol rkeys as unique
identifiers in share URLs (
/checkin/{rkey}
). While collision risk is negligible at current scale (~32 simultaneous checkins per microsecond for 50% collision probability), rkeys only provide 10 bits of entropy and are not cryptographically guaranteed to be globally unique. Consider migrating to composite identifiers (DID+rkey) or backend-generated UUIDs if collision issues emerge at scale.
The project includes comprehensive tests for all components:
Unit Tests:
profile-resolver.test.ts
- Profile caching, resolution, and refresh logichandle-resolver.test.ts
- Handle resolution from DIDsaddress-cache.test.ts
- Address caching and resolutiondatabase.test.ts
- Database operationsspatial.test.ts
- Spatial calculationsjetstream-ingestion.test.ts
- Event ingestion
Integration Tests:
api-profiles.test.ts
- API endpoints with profile data includedapi.test.ts
- Core API functionality
# Run all tests ./scripts/test.sh # Run specific test suites deno test --allow-all tests/unit/ # Unit tests only deno test --allow-all tests/integration/ # Integration tests only # Run tests in watch mode ./scripts/test.sh --watch # Run with coverage ./scripts/test.sh --coverage
- Use TypeScript for all functions
- Import SQLite:
import { sqlite } from "https://esm.town/v/std/sqlite2"
- ⚠️ IMPORTANT: Always use
sqlite2
, notsqlite
(deprecated) - Use object format:
await sqlite.execute({ sql: "...", args: [...] })
- ⚠️ IMPORTANT: Always use
- Import blob storage:
import { blob } from "https://esm.town/v/std/blob"
- Use
https://esm.sh
for external dependencies - Never hardcode secrets - use
Deno.env.get('keyname')
- Let errors bubble up with full context rather than catching and logging
- SQLite: Primary data (checkins, social graph, processing logs)
- Blob Storage: Caching layer for address resolution with automatic expiry
- Schema Changes: Increment table versions (e.g.,
checkins_v1
→checkins_v2
) - Always create tables with
IF NOT EXISTS
on function startup
- Monitor
/stats
endpoint for system health - Check
processing_log_v1
for ingestion metrics - Use built-in logging and debugging tools
- Monitor SQLite performance as data grows
- All API endpoints include proper CORS headers
- Public APIs only - no authentication required
- Rate limiting built into external service calls
- No sensitive data logged or stored
The AppView is fully compatible with the AT Protocol ecosystem:
- Ingests from Jetstream (official AT Protocol firehose)
- Resolves DIDs using PLC directory
- Fetches records via
com.atproto.repo.getRecord
- Integrates with Bluesky social graph APIs
This implementation is part of the Anchor project for location-based social networking on the AT Protocol.