CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview#
Smokesignal is a Rust-based event and RSVP management application built for the AT Protocol ecosystem. It provides decentralized identity management, OAuth authentication, event coordination, real-time event streaming, search capabilities, and email notifications. The application features RSVP acceptance workflows, private event content with conditional display, profile caching, and content storage with S3 support. It supports both standard AT Protocol OAuth (PDS) and AIP (AT Protocol Improvement Proposal) OAuth flows, with backend selection determined by runtime configuration.
Tech Stack#
- Language: Rust (edition 2024, minimum version 1.90)
- Web Framework: Axum with async/await
- Database: PostgreSQL with SQLx migrations
- Caching: Redis/Valkey for sessions, token management, and DID documents
- Search: OpenSearch for full-text event search
- Storage: Filesystem (default) or S3-compatible object storage
- Templates: MiniJinja (server-side rendering with optional reloading, localized structure)
- Frontend: HTMX + Alpine.js + Bulma CSS + FontAwesome
- Frontend Build: Vite 6 + TypeScript 5.7 + Biome 2 + LightningCSS
- Authentication: AT Protocol OAuth with JOSE/JWT (P-256 ECDSA)
- Internationalization: Fluent for i18n support
- Static Assets: Rust Embed for production builds
- HTTP Client: Reqwest with middleware chain, retry logic, and compression
- Cryptography: P-256 ECDSA with elliptic-curve and p256 crates
- Task Management: Tokio with task tracking and graceful shutdown
- DNS Resolution: Hickory resolver with DoH/DoT support
- Real-time: TAP consumer for AT Protocol events with backfill support
- Email: Lettre for SMTP-based email notifications
- Image Processing: Image resizing and optimization for avatars and content
- Rate Limiting: Redis-backed throttling for API endpoints
- Content Processing: HTML sanitization, minification, and CSS inlining
Development Commands#
Building & Development#
# Development build with template reloading (default)
cargo build --bin smokesignal
# Production build with embedded templates
cargo build --bin smokesignal --no-default-features -F embed
# Type checking and linting
cargo check
cargo clippy
# Run tests
cargo test
Running Services#
# Start main application server (development mode)
cargo run --bin smokesignal
# Generate cryptographic keys
cargo run --bin crypto -- key
Database Operations#
# Run database migrations
sqlx migrate run
# Reset database with migrations
sqlx database reset
Frontend Development#
# Install dependencies (from src-js directory)
cd src-js && npm install
# Build frontend assets (outputs to static/js/bundle.js and static/css/bundle.css)
npm run build
# Watch mode for development (rebuilds on file changes)
npm run dev
# Format code
npm run format
# Lint code
npm run lint
# Type check
npm run typecheck
# Run all checks
npm run check
Architecture Overview#
Core Structure#
/src-js/ # Frontend source (TypeScript, CSS)
├── src/ # TypeScript source files
│ ├── main.ts # Entry point
│ ├── types.ts # Shared TypeScript types
│ ├── components/ # Reusable UI components
│ └── features/ # Feature-specific code (maps, events, lfg)
├── styles/ # CSS source files
│ ├── main.css # CSS entry point
│ ├── base/ # Variables, utilities
│ ├── components/ # Component styles
│ └── features/ # Feature styles
├── package.json
├── vite.config.ts
├── tsconfig.json
└── biome.json
/src/
├── atproto/ # AT Protocol integration
│ ├── auth.rs # Authentication utilities
│ ├── utils.rs # AT Protocol utilities
│ └── lexicon/ # AT Protocol lexicon definitions
│ ├── profile.rs # Profile lexicon
│ └── acceptance.rs # Acceptance lexicon
├── bin/ # Executables
│ ├── smokesignal.rs # Main application server
│ └── crypto.rs # Cryptographic key utilities
├── http/ # Web layer
│ ├── errors/ # HTTP error handling
│ ├── handle_*.rs # Route handlers (OAuth, events, RSVPs, admin, XRPC)
│ ├── middleware_*.rs # Request middleware (auth, i18n)
│ ├── auth_utils.rs # Authentication utilities
│ ├── acceptance_utils.rs # RSVP acceptance utilities
│ ├── import_utils.rs # Event/RSVP import utilities
│ ├── event_form.rs # Event form handling
│ ├── event_view.rs # Event view helpers
│ ├── rsvp_form.rs # RSVP form handling
│ ├── templates.rs # Template rendering
│ ├── timezones.rs # Timezone utilities
│ ├── pagination.rs # Pagination helpers
│ └── server.rs # Server configuration
├── storage/ # Data access layer
│ ├── cache.rs # Redis caching
│ ├── identity_profile.rs # User identity management
│ ├── profile.rs # AT Protocol profile storage
│ ├── event.rs # Event data operations
│ ├── acceptance.rs # RSVP acceptance storage
│ ├── oauth.rs # OAuth session management
│ ├── denylist.rs # Handle blocking
│ ├── content.rs # Content storage abstraction (filesystem/S3)
│ ├── private_event_content.rs # Private event content
│ ├── notification.rs # Email notification preferences
│ ├── distributed_lock.rs # Redis-based distributed locking
│ └── atproto.rs # AT Protocol specific storage
├── config.rs # Environment configuration
├── key_provider.rs # JWT key management
├── i18n.rs # Internationalization
├── tap_processor.rs # TAP event consumer and processor
├── processor.rs # Content fetcher for events
├── service.rs # Service document and DID management
├── emailer.rs # Email sending functionality
├── email_templates.rs # Email template rendering
├── email_confirmation.rs # Email confirmation tokens
├── unsubscribe_token.rs # Email unsubscribe tokens
├── identity_cache.rs # Identity/profile caching
├── throttle.rs # Rate limiting interface
├── throttle_redis.rs # Redis-based rate limiting
├── image.rs # Image processing utilities
├── facets.rs # AT Protocol facets parsing
├── task_identity_refresh.rs # Identity refresh task
├── task_oauth_requests_cleanup.rs # OAuth cleanup task
└── task_search_indexer.rs # OpenSearch indexing
Key Patterns#
- Layered Architecture: HTTP → Business Logic → Data Access
- Module-based Organization: Each handler in separate module with error types
- Dependency Injection: Services passed through application context
- Template-driven UI: Server-side rendering with optional reloading
- Background Tasks: Multiple async workers for maintenance and processing
- Event-driven: TAP consumer for real-time AT Protocol events with backfill support
Database Schema#
identity_profiles- User identity and preferences (including discovery settings)identity_notifications- Email notification preferences and confirmationoauth_requests- OAuth flow state with PKCE and DPoP supportoauth_sessions- Active sessions with token managementatproto_oauth_requests- AT Protocol OAuth request storageevents- Calendar events with location and timezone supportrsvps- Event responses and attendance tracking (with email sharing and validation)acceptance_tickets- RSVP acceptance tickets for validationacceptance_records- RSVP acceptance recordsprofiles- Cached AT Protocol profilesprivate_event_content- Conditional event content based on display criteriadenylist- Handle blocking for moderationdid_documents- Cached DID documents for performance
Configuration Requirements#
Required Environment Variables#
HTTP_COOKIE_KEY- 64-character hex key for session encryptionDATABASE_URL- PostgreSQL connection stringREDIS_URL- Redis/Valkey connection stringEXTERNAL_BASE- Public base URL (e.g., https://smokesignal.events)PLC_HOSTNAME- AT Protocol PLC server hostnameADMIN_DIDS- Comma-separated admin user DIDsSERVICE_KEY- Service identity key for did:web supportCONTENT_STORAGE- Storage backend configuration (filesystem path or S3 URL)
OAuth Backend Configuration#
OAUTH_BACKEND- OAuth backend to use: "pds" (default) or "aip"
When OAUTH_BACKEND=pds, the following variable is required:
SIGNING_KEYS- Path to JWK key set file for OAuth signing
When OAUTH_BACKEND=aip, the following variables are required:
AIP_HOSTNAME- AIP OAuth server hostnameAIP_CLIENT_ID- AIP OAuth client IDAIP_CLIENT_SECRET- AIP OAuth client secret
Optional Environment Variables#
RUST_LOG- Logging configuration (default: info)PORT- Server port (default: 3000)BIND_ADDR- Bind address (default: 0.0.0.0)TAP_HOSTNAME- TAP service hostname (default: localhost:2480)TAP_PASSWORD- TAP admin password for authentication (optional)ENABLE_TAP- Enable TAP consumer (default: true)ENABLE_OPENSEARCH- Enable OpenSearch integrationENABLE_TASK_OPENSEARCH- Enable search indexing taskOPENSEARCH_ENDPOINT- OpenSearch server endpointSMTP_HOST- SMTP server hostname for email notificationsSMTP_PORT- SMTP server portSMTP_USERNAME- SMTP authentication usernameSMTP_PASSWORD- SMTP authentication passwordSMTP_FROM- Email sender address
Development Setup#
Using DevContainer (Recommended)#
The project includes a complete DevContainer setup with PostgreSQL, Redis, and all Rust dependencies. Use the provided .devcontainer/ configuration for consistent development environment.
Key Development Features#
- Template reloading in development mode (default
reloadfeature) - Embedded templates for production builds (
embedfeature) - Localized template structure with language-specific directories (e.g.,
templates/en-us/) - SQLx compile-time query checking with migrations
- Cryptographic key generation and management utilities
- Comprehensive error handling with user-friendly messages
- Internationalization support with Fluent
- LRU caching for OAuth requests and DID documents
- Graceful shutdown with task tracking and cancellation tokens
- Real-time event ingestion via TAP with backfill support
- Full-text search with OpenSearch
- Email notifications with HTML templates and confirmation workflow
- RSVP acceptance/validation workflow with tickets and records
- Private event content with conditional display criteria
- Service identity with did:web support
- Content storage abstraction supporting filesystem and S3
- Image processing for avatars and uploaded content
- Redis-based rate limiting and distributed locking
- Profile caching for AT Protocol profiles
Build Features#
default = ["reload", "s3"]- Development mode with S3 supportembed- Production with embedded templatesreload- Development with template reloadings3- S3-compatible storage support
Testing#
- Unit tests embedded in source files using
#[cfg(test)] - Integration tests for database operations and HTTP handlers
- Run with
cargo test - Database tests require running PostgreSQL instance with test database
- OAuth flow tests use fixture data for AT Protocol interactions
- Template rendering tests ensure UI consistency
Security Notes#
- Uses JOSE/JWT with P-256 ECDSA keys for OAuth signing
- PKCE OAuth flow with state parameter for CSRF protection
- DPoP (Demonstration of Proof-of-Possession) support for token binding
- Encrypted session cookies with secure flags
- Forbids unsafe Rust code (
#[forbid(unsafe_code)]) - Input validation and HTML sanitization with Ammonia
- Content Security Policy headers
- Rate limiting and request timeouts
Visibility#
Types and methods should have the lowest visibility necessary, defaulting to private. If public visibility is necessary, attempt to make it public to the crate only. Using completely public visibility should be a last resort.
AT Protocol Integration#
- Full OAuth 2.0 implementation with AT Protocol services using external
atproto-*crates - Support for both standard AT Protocol (PDS) and AIP OAuth flows
- DID-based identity management with PDS discovery
- Integration with PLC (Public Ledger of Credentials) for DID resolution
- Handle resolution and verification with LRU caching
- Decentralized identity workflows with fallback mechanisms
- AT Protocol lexicon support:
- Events and RSVPs (community.lexicon.calendar.*)
- Profiles (events.smokesignal.profile)
- Acceptance records (events.smokesignal.calendar.acceptance)
- Location data (community.lexicon.location)
- TAP consumer for real-time event ingestion from AT Protocol with backfill
- XRPC server endpoints for search and service operations
- Service identity with did:web support for federated interactions
- AT Protocol attestation support for verified claims
- Uses external AT Protocol libraries (from tangled.org):
atproto-identity- Identity resolution and verification with LRU cachingatproto-oauth- OAuth flow implementation with LRU and zeroizeatproto-oauth-axum- Axum integration for OAuthatproto-oauth-aip- AIP OAuth implementationatproto-client- AT Protocol client functionalityatproto-record- Record managementatproto-tap- TAP event streaming with backfill supportatproto-xrpcs- XRPC server implementationatproto-attestation- Attestation verification
Error Handling#
All error strings must use this format:
error-smokesignal-<domain>-<number> <message>: <details>
Example errors:
- error-smokesignal-resolve-1 Multiple DIDs resolved for method
- error-smokesignal-plc-1 HTTP request failed: https://google.com/ Not Found
- error-smokesignal-key-1 Error decoding key: invalid
Errors should be represented as enums using the thiserror library when possible using src/errors.rs as a reference and example.
Avoid creating new errors with the anyhow!(...) macro.