CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview#
Acudo is a Rust project that integrates with the AT Protocol (Bluesky's decentralized social networking protocol). The project uses the AT Protocol SDK crates for client operations, identity management, real-time event streaming via Jetstream, and OAuth authentication with Axum web framework integration.
Development Commands#
Building#
cargo build # Build in debug mode
cargo build --release # Build in release mode
Running#
cargo run # Run in debug mode
cargo run --release # Run in release mode
Testing#
cargo test # Run all tests
cargo test <test_name> # Run a specific test
cargo test -- --nocapture # Run tests with stdout output
Code Quality#
cargo fmt # Format code according to Rust style guidelines
cargo fmt -- --check # Check formatting without modifying files
cargo clippy # Run the Rust linter
cargo clippy -- -D warnings # Treat warnings as errors
Dependency Management#
cargo update # Update dependencies to latest compatible versions
cargo tree # Display dependency tree
Architecture Notes#
This project is built around the AT Protocol ecosystem with the following key dependencies:
- atproto-client: Core client functionality for AT Protocol operations
- atproto-identity: Identity resolution and management
- atproto-jetstream: Real-time event streaming from AT Protocol networks
- atproto-oauth: OAuth authentication implementation
- atproto-oauth-aip: AT Protocol Improvement Proposal OAuth implementation
- atproto-oauth-axum: OAuth integration for the Axum web framework
The project uses Rust edition 2024, indicating use of the latest language features.
Project Structure#
The project follows a modular structure with clear separation of concerns:
Storage Layer (src/storage/)#
identity.rs- PostgreSQL implementation ofDidDocumentStoragetrait for persistent DID document storage with pagination supportoauth.rs- PostgreSQL implementation ofOAuthRequestStoragetrait for OAuth request managementrsvp.rs- PostgreSQL implementation for RSVP record storage with optimized queries and paginationrsvp_extras.rs- Storage for additional RSVP metadata on a per-identity basis (deprecated - no longer in use)ticket.rs- PostgreSQL implementation for ticket record storage with pagination supportbadge.rs- PostgreSQL implementations for badge definitions, awards, and award rulesacudo_rule.rs- Storage for badge award rules with JSON-based rule evaluationatpoauth.rs- OAuth session storage implementationevent.rs- Event record storage with CRUD operations and JSON field search capabilities
HTTP Layer (src/http/)#
server.rs- Axum router configuration with CORS, tracing, and timeout middlewarecontext.rs- Web application context and dependency injection using Axum'sFromRefmiddleware_auth.rs- Authentication middleware for extracting user identity from OAuth sessionstemplates.rs- Template engine configuration for Minijinjaerrors.rs- HTTP error handling and conversion utilities- Utilities:
utility_pagination.rs- Pagination utilities for admin pages with URL building and view helpersutility_auth.rs- Authentication helper functionsutility_common.rs- Common HTTP utilities (datetime formatting, admin access checks)utility_rule_evaluation.rs- Badge rule evaluation for tickets
- View Models:
records_view.rs- Data structures for record creation viewsevent_view.rs- Event display view models
handle_*.rs- Individual HTTP handlers for different endpoints
Admin Interface (src/http/handle_admin_*.rs)#
handle_admin.rs- Main admin dashboard with authentication and authorizationhandle_admin_acudo_rules.rs- CRUD operations for badge award rules managementhandle_admin_did_documents.rs- Read-only views for DID document records with paginationhandle_admin_rsvps.rs- Read-only views for RSVP records with paginationhandle_admin_tickets.rs- Read-only views for ticket records with paginationhandle_admin_events.rs- Event management with import capabilities and JSON field search
Badge System (src/badges/)#
service.rs- Badge award service with rule evaluation enginerules.rs- Rule definitions and evaluation logic usingdatalogic-rs
Core Components#
config.rs- Configuration management with environment variable loadingconsumer.rs- Jetstream consumer for real-time AT Protocol event processingerrors.rs- Centralized error handling with standardized error formatsevent.rs- Event management and metadata handlingprocess.rs- Event processing pipeline for RSVP recordsidentity_cache.rs- Caching layer for identity resolution with configurable TTL and size limitstito.rs- Tito ticketing platform integration for webhook processing- AT Protocol Integration (
src/atproto/)auth.rs- Authentication utilities for PDS and AIP OAuth flowsmod.rs- Module exports
Templates (templates/)#
admin_base.html- Base template for admin pages with Bulma CSS and pagination macrosadmin_*.html- Individual admin page templates with responsive designbase.html- Base template for public pages- Public page templates for home, tickets, authentication flows
Database (migrations/)#
- Complete database schema with migrations for all storage types
- Optimized indexes for efficient querying and JSON operations
- Automatic timestamp management with triggers
HTTP Features#
The application provides comprehensive HTTP functionality across public and admin interfaces:
Public Interface#
- Home Page (
GET /) - Displays event information, recent activity, and call to action to get tickets (or view tickets if logged in) - Tickets Page (
GET /tickets) - Where users can purchase or view their tickets (requires authentication)- Evaluates ticket purchases using badge rule engine to determine RSVP eligibility
- Shows pending records (RSVPs and badges) that can be created
- Provides unified record creation interface with signature verification
- Record Creation (
POST /records/create) - Unified endpoint for creating multiple AT Protocol records- Cryptographically verifies all records before creation
- Supports batch creation of RSVPs and badge awards
- Requires valid issuer signatures on all records
- OAuth Authentication Flow:
GET /auth/login- Initiates login flow with PDS or AIP backend selectionPOST /auth/login- Handles login form submissionGET /auth/callback- OAuth callback handler for AT Protocol OAuth or AIP OAuth backendGET /logout- Logout endpoint that clears session
- Tito Integration (
POST /api/webhooks/tito) - Webhook endpoint for Tito event processing - Well-Known Endpoints:
GET /.well-known/did.json- did:web resolution endpointGET /.well-known/atproto-did- AT Protocol DID resolution
- OAuth Metadata (when OAuth server enabled):
GET /oauth/client-metadata.json- OAuth client metadataGET /.well-known/jwks.json- JSON Web Key Set for OAuth
Admin Interface (Authentication Required)#
- Admin Dashboard (
GET /admin) - Main admin panel with navigation to management and viewing tools - Badge Rule Management (Full CRUD):
GET /admin/acudo-rules- List all badge award rules with filteringGET /admin/acudo-rules/new- Create new badge award rule formPOST /admin/acudo-rules- Create new badge award ruleGET /admin/acudo-rules/{id}/edit- Edit existing rule formPOST /admin/acudo-rules/{id}- Update existing rulePOST /admin/acudo-rules/{id}/delete- Delete rule
- Event Management:
GET /admin/events- List events or view specific event detailsGET /admin/events/import- Event import formPOST /admin/events/import- Import events from external sources
- Data Viewing (Read-only with Pagination):
GET /admin/did-documents- List DID documents with paginationGET /admin/did-documents/{did}- View specific DID document detailsGET /admin/rsvps- List RSVP records with paginationGET /admin/rsvps/{aturi}- View specific RSVP record detailsGET /admin/tickets- List ticket records with paginationGET /admin/tickets/{ticket_id}- View specific ticket record details
Storage Implementation#
The project implements a comprehensive storage layer using PostgreSQL with support for AT Protocol storage traits and custom business logic:
Identity Storage (src/storage/identity.rs)#
- PostgresDidDocumentStorage: Thread-safe storage using PostgreSQL connection pool
- Database Schema: DID documents stored as JSONB with GIN indexing for efficient JSON queries
- Implementation:
atproto_identity::storage::DidDocumentStoragetrait - Pagination:
list_documents_paginated(page, page_size)for admin interface - Admin Features:
list_all_documents()for comprehensive document access
OAuth Storage (src/storage/oauth.rs)#
- PostgresOAuthRequestStorage: Thread-safe storage for OAuth authorization requests
- Database Schema: OAuth requests with automatic expiration handling and efficient indexing
- Implementation:
atproto_oauth::storage::OAuthRequestStoragetrait - Security Features: Automatic filtering of expired requests, secure storage of cryptographic keys
RSVP Storage (src/storage/rsvp.rs)#
- PostgresRsvpStorage: Thread-safe storage for RSVP records with optimized queries
- Database Schema: RSVP records stored with JSONB for flexible structure and efficient JSON queries
- Optimized Queries:
list_rsvps_by_identity_and_event()provides efficient filtering at the database level - PostgreSQL Features: Uses PostgreSQL JSON operators (
->and->>) for fast subject URI filtering - Pagination:
list_rsvps_paginated(page, page_size)for admin interface
Event Storage (src/storage/event.rs)#
- PostgresEventStorage: Storage for event records with AT Protocol integration
- Database Schema: Events stored with AT-URI, CID, and JSONB record data
- Query Methods:
search_events_by_json_field()for JSON field searches - Pagination:
list_events_paginated(page, page_size)for admin interface - CRUD Operations: Full lifecycle management of event records
Ticket Storage (src/storage/ticket.rs)#
- PostgresTicketStorage: Storage for ticket purchases and registrations
- Database Schema: Ticket records with optional DID linking and Tito integration
- Query Methods:
list_tickets_by_identity(),list_tickets_by_email() - Pagination:
list_tickets_paginated(page, page_size)for admin interface
Badge System Storage (src/storage/badge.rs)#
- PostgresBadgeDefinitionStorage: Badge definition records
- PostgresBadgeAwardStorage: Badge award records with recipient tracking
- PostgresBadgeAwardRuleStorage: Legacy badge rule storage (deprecated)
AcudoRule Storage (src/storage/acudo_rule.rs)#
- PostgresAcudoRuleStorage: Modern badge award rules with JSON-based evaluation
- Database Schema: Rules with ULID IDs, event AT-URIs, and JSON rule bodies
- CRUD Operations: Full lifecycle management of badge award rules
- Rule Engine: Integration with
datalogic-rsfor flexible rule evaluation
OAuth Session Storage (src/storage/atpoauth.rs)#
- PostgresOAuthSessionStorage: Session management for authenticated users
- Security: Secure session token storage and validation
Migrations: Located in migrations/ directory, run with sqlx migrate run
Testing: All storage implementations include comprehensive test suites using #[sqlx::test] macro
Database Setup#
The storage implementation requires a PostgreSQL database. For testing:
# Set DATABASE_URL environment variable
export DATABASE_URL="postgres://user:password@localhost/database_name"
# Or for tests, it defaults to:
# postgres://postgres:postgres@localhost/acudo_test
Testing#
All storage implementations use the #[sqlx::test] macro for automatic database isolation:
cargo test storage::identity # Run identity storage tests
cargo test storage::oauth # Run OAuth storage tests
cargo test storage::rsvp # Run RSVP storage tests
cargo test storage::event # Run event storage tests
cargo test storage::ticket # Run ticket storage tests
cargo test storage::badge # Run badge storage tests
cargo test storage::acudo_rule # Run AcudoRule storage tests
cargo test storage # Run all storage tests
cargo test badges # Run badge system tests
cargo test http # Run HTTP handler tests
cargo test identity_cache # Run identity cache tests
The test suite includes:
- Storage Tests: Database operations with automatic rollback per test
- Rule Engine Tests: Badge award rule evaluation scenarios
- HTTP Handler Tests: Request/response validation
- Integration Tests: End-to-end functionality validation
AT Protocol Development Considerations#
When developing with AT Protocol:
- OAuth flows typically require environment configuration for client credentials
- Jetstream connections require websocket support for real-time events
- Identity operations may need DID (Decentralized Identifier) resolution
- Client operations interact with PDS (Personal Data Server) instances
- DID document storage requires PostgreSQL database setup and migrations
- OAuth request storage handles sensitive cryptographic material (private keys)
- Implement periodic cleanup of expired OAuth requests for security and performance
Identity Caching#
The application includes a caching layer for identity resolution to improve performance:
- Configurable TTL: Set cache time-to-live with
IDENTITY_CACHE_TTL_MINUTES(default: 10 minutes) - Size Limits: Configure maximum cache size with
IDENTITY_CACHE_SIZE(default: 500 entries) - LRU Eviction: Least recently used entries are evicted when cache is full
- Thread-Safe: Uses Arc for concurrent access
- Transparent: Works seamlessly with existing
IdentityResolverimplementations
Record Creation and Signatures#
The application uses a unified record creation system with cryptographic verification:
- Batch Creation: Users can create multiple AT Protocol records (RSVPs, badges) in a single transaction
- Signature Verification: All records are cryptographically verified using
atproto_record::signature::verify - Issuer Validation: Only records signed by the configured issuer DID are accepted
- Pre-computed Records: Records are generated server-side with proper signatures
- User Selection: Users can choose which records to create via checkboxes
- Security First: Invalid signatures result in immediate rejection before any AT Protocol operations
Error Handling#
All error strings must use this format:
error-acudo-<domain>-<number> <message>: <details>
Example errors:
- error-acudo-resolve-1 Multiple DIDs resolved for method
- error-acudo-plc-1 HTTP request failed: https://google.com/ Not Found
- error-acudo-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.
Badge Award Configuration#
The application supports a comprehensive badge award system that can be configured through JSON files or environment variables. This system allows you to define badges and rules that determine when they should be awarded to users based on their ticket purchases.
Configuration Structure#
The badge configuration consists of three main parts:
- Badge Definitions - Define the badges themselves (name, description, image, etc.)
- Award Rules - Define when badges should be awarded based on conditions
- System Settings - Global configuration for the badge system
Configuration Loading#
Badge configuration is now managed through the database using the admin interface. The legacy file-based configuration has been replaced with a comprehensive admin panel that provides:
- Database Storage: All badge rules are stored in the
acudo_rulestable - Admin Interface: Full CRUD operations through the web admin panel at
/admin/acudo-rules - Dynamic Loading: Rules are loaded from the database when the application starts
- Real-time Management: Changes can be made through the admin interface without application restart
Rule Engine Configuration#
The rule engine now uses datalogic-rs for JSON-based rule evaluation. Rules are stored in the database using the acudo_rules table and loaded automatically when the application starts. Each rule has the following structure:
id: A globally unique ID (ULID) for the ruleaturis: A list of AT-URIs (events and/or badges) that will be created if the rule evaluates to truecreate: The rule logic that determines if the records should be created (evaluated using JSON logic)signature_extra: Optional additional fields to include in the record signatures
Example rule structure:
{
"id": "01BX5ZZKZK6K000000000000",
"aturis": [
"at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/community.lexicon.calendar.event/3luzkrwivzm2a",
"at://did:web:oauth-masterclass.atproto.camp/community.lexicon.calendar.event/3lwtxdryiw22b"
],
"create": {
"and": [
{
"==": [
"Oct 4 Individual Seat (Early Bird)",
{
"val": "ticket_type"
}
]
},
{
"<": [
{
"val": "purchase_at"
},
"2025-08-25T00:00:00Z"
]
}
]
},
"signature_extra": {}
}
The rule engine evaluates tickets with the following data structure:
ticket_type: The type of ticket purchased (e.g., "Oct 4 Individual Seat (Early Bird)")ticket_paid: The amount paid for the ticket (in cents)purchase_at: The ISO 8601 timestamp of when the ticket was purchasedemail: The ticket purchaser's email addressreference: The ticket reference number- Additional fields from the ticket's raw data
When a rule's create condition evaluates to true, the system will prepare AT Protocol records for each URI in the aturis array. These records are then signed by the issuer and presented to the user for selective creation.
Common Rule Patterns#
The JSON Logic engine supports various comparison and logical operators:
Ticket Type Matching#
{"==": ["VIP Pass", {"val": "ticket_type"}]}
Date Comparisons#
{"<": [{"val": "purchase_at"}, "2025-08-01T00:00:00Z"]} // Before date
{">": [{"val": "purchase_at"}, "2025-08-15T00:00:00Z"]} // After date
Complex Conditions#
{
"and": [
{"==": ["early-bird", {"val": "ticket_type"}]},
{"<": [{"val": "purchase_at"}, "2025-08-01T00:00:00Z"]}
]
}
Price-based Rules#
{">": [{"val": "ticket_paid"}, 10000]} // More than $100 (in cents)
Rule Logic#
Rules support complex logic through condition grouping:
all_of- All conditions must be satisfied (AND logic)any_of- At least one condition must be satisfied (OR logic) [Not yet implemented]none_of- None of the conditions must be satisfied (NOT logic) [Not yet implemented]
Example Configuration#
Here's a complete example of an AcudoRule that creates both an RSVP and a badge award for early bird ticket purchases:
{
"id": "01J8XYZ123ABC",
"aturis": [
"at://did:plc:event123/community.lexicon.calendar.event/main-event",
"at://did:plc:issuer123/community.lexicon.badge.award/early-bird"
],
"create": {
"and": [
{"==": ["General Admission (Early Bird)", {"val": "ticket_type"}]},
{"<": [{"val": "purchase_at"}, "2025-08-01T00:00:00Z"]}
]
},
"signature_extra": {
"special_status": "early_supporter"
}
}
This rule will create an RSVP and award an early bird badge when someone purchases a "General Admission (Early Bird)" ticket before August 1st, 2025.
Configuration Validation#
The system automatically validates configurations for:
- All rules reference valid badge IDs
- No duplicate badge IDs
- No duplicate rule names
- Rule conditions are properly formatted
Integration with Existing Systems#
The badge configuration integrates seamlessly with the existing badge storage and rule engine:
- Badge definitions are converted to AT Protocol records
- Rules are converted to the existing
BadgeRuleandBadgeRuleSettypes - The rule engine evaluates configured rules during ticket processing
- Awards are stored in the database and can be displayed to users
Tito Integration#
The application integrates with Tito for ticket management through webhooks:
- Webhook Endpoint:
POST /api/webhooks/titoreceives ticket purchase events - Ticket Processing: Automatically creates ticket records in the database
- DID Linking: Associates tickets with user DIDs when available
- Event Mapping: Maps Tito events to AT Protocol event records
- Badge Evaluation: Triggers badge rule evaluation on new ticket purchases
Admin Interface#
The application provides a comprehensive admin interface for managing system data and configuration:
Authentication and Authorization#
- Admin DIDs: Configure admin access using the
ADMIN_DIDSenvironment variable (semicolon-separated list) - Secure Access: All admin pages require AT Protocol authentication and admin DID verification
- Hidden Access: Returns 404 for unauthorized users to hide admin functionality existence
Admin Dashboard (/admin)#
- Management Tools: CRUD operations for AcudoRules and RsvpExtras
- Data Viewing: Read-only access to DID documents, RSVP records, and ticket records
- Professional UI: Bulma CSS framework with responsive design
- Navigation: Clear separation between management and viewing tools
Pagination Support#
All admin list pages support pagination for efficient handling of large datasets:
- Default Page Size: 20 records per page (configurable 20-100)
- URL Parameters:
?page=N&page_size=Mfor custom pagination - Navigation: Previous/Next buttons with proper disabled states
- Info Display: Shows current page, page size, and total record counts
Management Features#
- AcudoRule Management: Create, edit, and delete badge award rules with JSON validation
- Event Management: Import and manage events with JSON field search capabilities
- Data Viewing: Comprehensive read-only access to all system records
- Search and Filter: Efficient database-level filtering and sorting
Environment Variables#
ADMIN_DIDS- Semicolon-separated list of DID strings for admin access (e.g., "did:plc:abc123;did:plc:def456")DATABASE_URL- PostgreSQL connection string for all storage operationsIDENTITY_CACHE_SIZE- Maximum number of cached identity documents (default: 500)IDENTITY_CACHE_TTL_MINUTES- Cache time-to-live in minutes (default: 10)OAUTH_BACKEND- OAuth backend selection: "pds" or "aip" (AT Protocol Improvement Proposal)ISSUER_DID- DID of the event issuer for record signingISSUER_PRIVATE_KEY- Private key for signing AT Protocol records