CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands#
- Build:
cargo build - Run application:
cargo run --bin showcase - Run tests:
cargo test - Run single test:
cargo test <test_name> - Run all tests with databases:
./scripts/test-all.sh - Run PostgreSQL tests:
./scripts/test-postgres.sh - Run SQLite tests:
./scripts/test-sqlite.sh - Check code:
cargo check - Format code:
cargo fmt - Lint code:
cargo clippy
Project Overview#
This is a Rust application named "showcase" using the 2024 edition. The project is a badge awards showcase application for the AT Protocol community that consumes Jetstream events, validates badge signatures, stores data in multiple database backends (SQLite/PostgreSQL), and provides a web interface to display badge awards.
Architecture#
src/bin/showcase.rs: Main application binary entry pointsrc/lib.rs: Library crate exposing all modules with#![warn(missing_docs)]src/config.rs: Configuration management via environment variablessrc/storage/: Modular storage implementations with multiple backend supportsrc/storage/mod.rs: Storage trait definitions and common typessrc/storage/sqlite.rs: SQLite database implementationsrc/storage/postgres.rs: PostgreSQL database implementationsrc/storage/file_storage.rs: File storage abstraction (local and S3)
src/process.rs: Badge processing logic, signature validation, and image handlingsrc/consumer.rs: Jetstream consumer for AT Protocol badge award eventssrc/http.rs: Axum-based HTTP server with API endpoints and template renderingsrc/errors.rs: Comprehensive error handling usingthiserrorsrc/templates.rs: Template engine integration- With
reloadfeature: UsesAutoReloaderfor live template reloading in development - With
embedfeature: Embeds templates at compile time, takeshttp_externalandversionparameters for production
- With
build.rs: Build script for template embedding when usingembedfeatureDockerfile: Multi-stage Docker build configuration for production deploymenttemplates/: HTML templates (base.html, index.html, identity.html)static/: Static assets (Pico CSS, images, badge storage)
Docker Deployment#
The Dockerfile provides a multi-stage build configuration for production deployment:
Build Stage#
- Uses
rust:1.87-slimbase image with build dependencies (pkg-config,libssl-dev) - Configurable build arguments:
FEATURES(default:embed,postgres,s3): Cargo features to enableTEMPLATES(default:./templates): Source path for template filesSTATIC(default:./static): Source path for static assets
- Builds with
--no-default-featuresand only specified features for production optimization - Template embedding is handled at build time when
embedfeature is enabled
Runtime Stage#
- Uses minimal
gcr.io/distroless/cc-debian12image for security and size optimization - Includes comprehensive OCI metadata labels for container identification
- Exposes port 8080 with configurable environment variables:
HTTP_STATIC_PATH=/app/static: Path to static assetsHTTP_PORT=8080: HTTP server port
- Production-ready configuration with embedded templates and minimal attack surface
Build Commands#
- Build image:
docker build -t showcase . - Run container:
docker run -p 8080:8080 showcase - Custom features:
docker build --build-arg FEATURES=embed,sqlite -t showcase .
Database Schema#
Tables#
badges#
Stores badge definition records from the AT Protocol.
Key columns:
aturi: AT-URI of the badge definitioncid: Content identifier for the badge recordname: Human-readable badge nameimage: Optional image reference (blob CID)record: Full JSON record of the badge definition (added in migration 002)count: Number of awards using this badge- Primary key:
(aturi, cid)
awards#
Stores individual badge awards to users.
Key columns:
aturi: AT-URI of the award record (primary key)cid: Content identifier for the award recorddid: DID of the recipientbadge: AT-URI of the associated badgebadge_cid: Content identifier of the badge recordbadge_name: Human-readable badge namevalidated_issuers: JSON array of validated issuer DIDsrecord: Full JSON record of the award
identities#
Stores resolved DID documents and identity information.
Key columns:
did: Decentralized identifier (primary key)handle: AT Protocol handlerecord: Full DID document JSON
Database Type Differences#
- PostgreSQL: Uses
JSONBfor JSON columns (better performance) andTIMESTAMPTZfor timestamps - SQLite: Uses
JSONfor JSON columns andTIMESTAMPfor timestamps
Error Handling#
All error strings must use this format:
error-showcase-<domain>-<number> <message>: <details>
Example errors:
- error-showcase-resolve-1 Multiple DIDs resolved for method
- error-showcase-plc-1 HTTP request failed: https://google.com/ Not Found
- error-showcase-key-1 Error decoding key: invalid
Errors are defined as enums in src/errors.rs using the thiserror library, organized by domain:
ConfigError: Configuration-related errorsConsumerError: Jetstream consumer errorsHttpError: HTTP server errorsProcessError: Badge processing errorsStorageError: Database and file storage errors
Each error variant follows the naming pattern and includes structured error messages.
Dependencies and Features#
Features#
default = ["reload", "sqlite", "postgres", "s3"]: Development mode with all features enabledembed: Production mode with embedded templates viaminijinja-embedreload: Development mode with live template reloading viaminijinja-autoreloadsqlite: SQLite database backend supportpostgres: PostgreSQL database backend supports3: S3-compatible object storage support for file operations
Key Dependencies#
- Web Framework:
axum0.8 withaxum-templatefor MiniJinja integration - Database:
sqlx0.8 with SQLite, PostgreSQL, JSON, and async support - Template Engine:
minijinja2.7 with conditional embed/reload features - AT Protocol: Released versions
atproto-client,atproto-identity,atproto-record,atproto-jetstream0.6.0 - Image Processing:
image0.25 for badge image handling - Object Storage:
minio0.3 for S3-compatible storage operations - Async Runtime:
tokio1.41 with multi-threaded runtime and signal handling - HTTP Client:
reqwest0.12 with TLS and middleware support,reqwest-chain1.0 for middleware chaining - Cryptography:
k256,p256,ecdsa,elliptic-curve0.13 for signature validation - Serialization:
serde,serde_json,serde_ipld_dagcbor - Utilities:
async-trait0.1 for async trait implementationsbase640.22 for encoding/decodingbytes1.10 for byte manipulationduration-str0.11 for parsing duration stringshickory-resolver0.25 for DNS resolutionlru0.12 for cachingmultibase0.9 for multibase encodingrand0.8 for random number generationsha20.10 for SHA-2 hashingtower-http0.5 for static file servingulid1.2 for ULID generation
Configuration#
The application is configured via environment variables. Key configuration includes:
Environment Files#
.env.dev: SQLite development configuration.env.test: PostgreSQL test configuration
Required Variables#
EXTERNAL_BASE: External hostname for the siteBADGE_ISSUERS: Semicolon-separated list of trusted badge issuer DIDs
Optional Variables (with defaults)#
HTTP_PORT(8080): HTTP server portHTTP_STATIC_PATH: Path to static assets directoryHTTP_TEMPLATES_PATH: Path to templates directoryDATABASE_URL(sqlite://showcase.db): Database connection string (SQLite or PostgreSQL)BADGE_IMAGE_STORAGE(./badges): Badge image storage location- Local filesystem: Path to directory (e.g.,
./badges) - S3-compatible storage:
s3://[key]:[secret]@hostname/bucket[/optional_prefix]
- Local filesystem: Path to directory (e.g.,
PLC_HOSTNAME(plc.directory): PLC server hostnameHTTP_CLIENT_TIMEOUT(10s): HTTP client timeoutJETSTREAM_CURSOR_PATH: Optional path for persisting Jetstream cursor stateCERTIFICATE_BUNDLES: Semicolon-separated list of certificate bundle pathsUSER_AGENT: Custom user agent string for HTTP requestsDNS_NAMESERVERS: Semicolon-separated list of DNS nameserversRUST_LOG(showcase=info,info): Logging configuration
Code Style#
Error Handling#
- Use
ShowcaseErrorenum fromsrc/errors.rsfor all application errors - Implement
Fromtraits for automatic error conversion - Always include detailed error messages following the established format
- Prefer
thiserroroveranyhowfor structured errors
Result Type#
The codebase defines a type alias in src/lib.rs:
pub type Result<T> = std::result::Result<T, ShowcaseError>;
All functions should use this showcase::Result<T> type for consistency, where errors are one of the ShowcaseError variants defined in src/errors.rs.
Logging#
Use tracing for structured logging.
All calls to tracing::error, tracing::warn, tracing::info, tracing::debug, and tracing::trace should be fully qualified.
Do not use the println! macro in library code.
Async calls should be instrumented using the .instrument() that references the use tracing::Instrument; trait.
Async Patterns#
- Use
tokio::spawnfor concurrent tasks withTaskTrackerfor graceful shutdown - Implement proper cancellation token handling for all background tasks
- Use
Arcfor shared state across async boundaries
Storage Operations#
- Use the
Storagetrait for database operations to support multiple backends - Implement the
FileStoragetrait for file operations (local and S3) - Use SQLx with compile-time checked queries where possible
- Implement proper transaction handling for multi-step operations
- Follow the established migration pattern for schema changes
Web Framework#
- Use Axum extractors and response types consistently
- Implement proper error handling with
IntoResponseforShowcaseError - Use
AppStatepattern for dependency injection
Testing#
The project uses standard Rust testing with #[cfg(test)] attributes. Tests should be placed inline with the code they test or in separate test modules within each source file.
Test Scripts#
scripts/test-all.sh: Runs tests against both SQLite and PostgreSQL databasesscripts/test-postgres.sh: Runs tests specifically with PostgreSQL backend using Dockerscripts/test-sqlite.sh: Runs tests specifically with SQLite backend
The PostgreSQL test script automatically starts a PostgreSQL container for testing purposes.