QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.

QuickDID - Development Guide for Claude#

Overview#

QuickDID is a high-performance AT Protocol identity resolution service written in Rust. It provides handle-to-DID resolution with Redis-backed caching and queue processing.

Common Commands#

Building and Running#

# Build the project
cargo build

# Run in debug mode
cargo run

# Run tests
cargo test

# Type checking
cargo check

# Run with environment variables
HTTP_EXTERNAL=localhost:3007 SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK cargo run

Development with VS Code#

The project includes a .vscode/launch.json configuration for debugging with Redis integration. Use the "Debug executable 'quickdid'" launch configuration.

Architecture#

Core Components#

  1. Handle Resolution (src/handle_resolver.rs)

    • BaseHandleResolver: Core resolution using DNS and HTTP
    • CachingHandleResolver: In-memory caching layer
    • RedisHandleResolver: Redis-backed persistent caching with 90-day TTL
    • Uses binary serialization via HandleResolutionResult for space efficiency
  2. Binary Serialization (src/handle_resolution_result.rs)

    • Compact storage format using bincode
    • Strips DID prefixes for did:web and did:plc methods
    • Stores: timestamp (u64), method type (i16), payload (String)
  3. Queue System (src/queue_adapter.rs)

    • Supports MPSC (in-process) and Redis adapters
    • HandleResolutionWork items processed asynchronously
    • Redis uses reliable queue pattern (LPUSH/RPOPLPUSH/LREM)
  4. HTTP Server (src/http/)

    • XRPC endpoints for AT Protocol compatibility
    • Health check endpoint
    • DID document serving via .well-known

Key Technical Details#

DID Method Types#

  • did:web: Web-based DIDs, prefix stripped for storage
  • did:plc: PLC directory DIDs, prefix stripped for storage
  • Other DID methods stored with full identifier

Redis Integration#

  • Caching: Uses MetroHash64 for key generation, stores binary data
  • Queuing: Reliable queue with processing/dead letter queues
  • Key Prefixes: Configurable via QUEUE_REDIS_PREFIX environment variable

Handle Resolution Flow#

  1. Check Redis cache (if configured)
  2. Fall back to in-memory cache
  3. Perform DNS TXT lookup or HTTP well-known query
  4. Cache result with appropriate TTL
  5. Return DID or error

Environment Variables#

Required#

  • HTTP_EXTERNAL: External hostname for service endpoints (e.g., localhost:3007)
  • SERVICE_KEY: Private key for service identity (DID format)

Optional#

  • HTTP_PORT: Server port (default: 8080)
  • PLC_HOSTNAME: PLC directory hostname (default: plc.directory)
  • REDIS_URL: Redis connection URL for caching
  • QUEUE_ADAPTER: Queue type - 'mpsc' or 'redis' (default: mpsc)
  • QUEUE_REDIS_PREFIX: Redis key prefix for queues (default: queue:handleresolver:)
  • QUEUE_WORKER_ID: Worker ID for Redis queue (auto-generated if not set)
  • RUST_LOG: Logging level (e.g., debug, info)

Error Handling#

All error strings must use this format:

error-quickdid-<domain>-<number> <message>: <details>

Example errors:

  • error-quickdid-resolve-1 Multiple DIDs resolved for method
  • error-quickdid-plc-1 HTTP request failed: https://google.com/ Not Found
  • error-quickdid-key-1 Error decoding key: invalid

Errors should be represented as enums using the thiserror library.

Avoid creating new errors with the anyhow!(...) or bail!(...) macro.

Testing#

Running Tests#

# Run all tests
cargo test

# Run with Redis integration tests
TEST_REDIS_URL=redis://localhost:6379 cargo test

# Run specific test module
cargo test handle_resolver::tests

Test Coverage Areas#

  • Handle resolution with various DID methods
  • Binary serialization/deserialization
  • Redis caching and expiration
  • Queue processing logic
  • HTTP endpoint responses

Development Patterns#

Error Handling#

  • Uses anyhow::Result for error propagation
  • Graceful fallbacks when Redis is unavailable
  • Detailed tracing for debugging

Performance Optimizations#

  • Binary serialization reduces storage by ~40%
  • MetroHash64 for fast key generation
  • Connection pooling for Redis
  • Configurable TTLs for cache entries

Code Style#

  • Follow existing Rust idioms and patterns
  • Use tracing for logging, not println!
  • Prefer Arc for shared state across async tasks
  • Handle errors explicitly, avoid .unwrap() in production code

Common Tasks#

Adding a New DID Method#

  1. Update DidMethodType enum in handle_resolution_result.rs
  2. Modify parse_did() and to_did() methods
  3. Add test cases for the new method type

Modifying Cache TTL#

  • For in-memory: Pass TTL to CachingHandleResolver::new()
  • For Redis: Modify RedisHandleResolver::ttl_seconds()

Debugging Resolution Issues#

  1. Enable debug logging: RUST_LOG=debug
  2. Check Redis cache: redis-cli GET "handle:<hash>"
  3. Monitor queue processing in logs
  4. Verify DNS/HTTP connectivity to AT Protocol infrastructure

Dependencies#

  • atproto-identity: Core AT Protocol identity resolution
  • bincode: Binary serialization
  • deadpool-redis: Redis connection pooling
  • metrohash: Fast non-cryptographic hashing
  • tokio: Async runtime
  • axum: Web framework