CLAUDE.md#
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview#
xrpcs-emailer is an ATProtocol XRPC service that sends emails in response to webhook events. It's specifically designed for the smokesignal.events platform to handle event notifications, particularly RSVP confirmations. The service provides OAuth-protected webhook endpoints and integrates with SMTP for email delivery.
Repository: https://tangled.sh/@smokesignal.events/xrpcs-emailer
Development Commands#
Build and Run#
# Build the project
cargo build
# Build for release
cargo build --release
# Run the service (requires environment variables)
cargo run
# Run with environment variables
SERVICE_KEY="did:key:your-service-key" \
EXTERNAL_BASE="emailer.example.com" \
SMTP_CREDENTIALS="smtp://username:password@smtp.server:port" \
cargo run
Testing and Quality#
# Run tests
cargo test
# Run tests with output
cargo test -- --nocapture
# Check code without building
cargo check
# Format code
cargo fmt
# Run linter
cargo clippy
# Run linter with all targets
cargo clippy --all-targets --all-features
Architecture#
Core Components#
-
WebContext (
src/main.rs): Central application state containing:- HTTP client for external requests
- LRU cache for DID document storage (3 entries)
- Key provider for service keys
- Service configuration
- SMTP mailer for sending emails
-
Key Management:
SimpleKeyProvider: Manages cryptographic keys- Service key: Used for service identity and authentication
-
HTTP Endpoints:
GET /- Service index page with link to repositoryGET /.well-known/did.json- DID:web documentGET /.well-known/atproto-did- ATProtocol DID identifierGET /.well-known/oauth-protected-resource- OAuth Protected Resource MetadataPOST /xrpc/events.smokesignal.automation.InvokeWebhook- Webhook handler for event processing (OAuth protected)
Webhook Processing Flow#
- Client sends POST request to webhook endpoint with OAuth bearer token
- Service validates:
- Bearer token is present and valid
- Request contains valid webhook payload
- Service processes webhook events:
- Currently handles
rsvp.createdevents - Extracts email address from event context
- Currently handles
- For RSVP events, service:
- Constructs confirmation email with plain text and HTML parts
- Sends email via configured SMTP server
- Logs success or failure
- Returns empty JSON response
Environment Variables#
Required:
SERVICE_KEY: Service identity private key (e.g.,did:key:z42tj...)EXTERNAL_BASE: External hostname for the service (e.g.,emailer.example.com)SMTP_CREDENTIALS: SMTP connection URL (e.g.,smtp://username:password@smtp.server:port)
Optional:
PORT: Server port (default: 8080)PLC_HOSTNAME: PLC directory hostname (default: plc.directory)USER_AGENT: HTTP client user agentDNS_NAMESERVERS: Custom DNS nameservers (comma-separated)CERTIFICATE_BUNDLES: Additional CA certificates (comma-separated paths)
Code Patterns#
- All code is in a single
src/main.rsfile - Uses Axum web framework with Tower middleware
- Async/await patterns with Tokio runtime
- Error handling with custom error types that implement Axum's
IntoResponse - DID resolution with LRU caching
- OAuth bearer token validation
- Email sending via
lettrewith SMTP - Webhook event processing with JSON parsing
- Unsafe code is forbidden via
#![forbid(unsafe_code)]
Key Generation#
Generate service keys using:
goat key generate -t p256
The public key derived from the service key is included in the DID:web document for identity verification.
Email Configuration#
The service sends emails using SMTP. The SMTP_CREDENTIALS environment variable should be a connection URL in the format:
smtp://username:password@smtp.server:portfor standard SMTPsmtps://username:password@smtp.server:portfor SMTP over TLS
Currently, the service sends RSVP confirmation emails with:
- From: "Smoke Signal Emailer" events@smokesignal.events
- Subject: "RSVP'd to event"
- Body: Multipart message with both plain text and HTML versions
Docker Build#
The project includes a multi-stage Dockerfile for containerized deployment. Note that the Dockerfile currently references the old binary name xrpcs-semeion instead of xrpcs-emailer - this should be updated to match the Cargo.toml package name.