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.
1# QuickDID Configuration Reference
2
3This document provides a comprehensive reference for all configuration options available in QuickDID.
4
5## Table of Contents
6
7- [Required Configuration](#required-configuration)
8- [Network Configuration](#network-configuration)
9- [Caching Configuration](#caching-configuration)
10- [Queue Configuration](#queue-configuration)
11- [Rate Limiting Configuration](#rate-limiting-configuration)
12- [HTTP Caching Configuration](#http-caching-configuration)
13- [Configuration Examples](#configuration-examples)
14- [Validation Rules](#validation-rules)
15
16## Required Configuration
17
18These environment variables MUST be set for QuickDID to start.
19
20### `HTTP_EXTERNAL`
21
22**Required**: Yes
23**Type**: String
24**Format**: Hostname with optional port
25
26The external hostname where this service will be accessible. This is used to generate the service DID and for AT Protocol identity resolution.
27
28**Examples**:
29```bash
30# Production domain
31HTTP_EXTERNAL=quickdid.example.com
32
33# With non-standard port
34HTTP_EXTERNAL=quickdid.example.com:8080
35
36# Development/testing
37HTTP_EXTERNAL=localhost:3007
38```
39
40**Constraints**:
41- Must be a valid hostname or hostname:port combination
42- Port (if specified) must be between 1-65535
43
44## Network Configuration
45
46### `HTTP_PORT`
47
48**Required**: No
49**Type**: String
50**Default**: `8080`
51**Range**: 1-65535
52
53The port number for the HTTP server to bind to.
54
55**Examples**:
56```bash
57HTTP_PORT=8080 # Default
58HTTP_PORT=3000 # Common alternative
59HTTP_PORT=80 # Standard HTTP (requires root/privileges)
60```
61
62### `PLC_HOSTNAME`
63
64**Required**: No
65**Type**: String
66**Default**: `plc.directory`
67
68The hostname of the PLC directory service for DID resolution.
69
70**Examples**:
71```bash
72PLC_HOSTNAME=plc.directory # Production (default)
73PLC_HOSTNAME=test.plc.directory # Testing environment
74PLC_HOSTNAME=localhost:2582 # Local PLC server
75```
76
77### `DNS_NAMESERVERS`
78
79**Required**: No
80**Type**: String (comma-separated IP addresses)
81**Default**: System DNS
82
83Custom DNS nameservers for handle resolution via TXT records.
84
85**Examples**:
86```bash
87# Google DNS
88DNS_NAMESERVERS=8.8.8.8,8.8.4.4
89
90# Cloudflare DNS
91DNS_NAMESERVERS=1.1.1.1,1.0.0.1
92
93# Multiple providers
94DNS_NAMESERVERS=8.8.8.8,1.1.1.1
95
96# Local DNS
97DNS_NAMESERVERS=192.168.1.1
98```
99
100### `USER_AGENT`
101
102**Required**: No
103**Type**: String
104**Default**: `quickdid/{version} (+https://github.com/smokesignal.events/quickdid)`
105
106HTTP User-Agent header for outgoing requests.
107
108**Examples**:
109```bash
110# Custom agent
111USER_AGENT="MyService/1.0.0 (+https://myservice.com)"
112
113# With contact info
114USER_AGENT="quickdid/1.0.0 (+https://quickdid.example.com; admin@example.com)"
115```
116
117### `CERTIFICATE_BUNDLES`
118
119**Required**: No
120**Type**: String (comma-separated file paths)
121**Default**: System CA certificates
122
123Additional CA certificate bundles for TLS connections.
124
125**Examples**:
126```bash
127# Single certificate
128CERTIFICATE_BUNDLES=/etc/ssl/certs/custom-ca.pem
129
130# Multiple certificates
131CERTIFICATE_BUNDLES=/certs/ca1.pem,/certs/ca2.pem
132
133# Corporate CA
134CERTIFICATE_BUNDLES=/usr/local/share/ca-certificates/corporate-ca.crt
135```
136
137## Caching Configuration
138
139### `REDIS_URL`
140
141**Required**: No (recommended for multi-instance production)
142**Type**: String
143**Format**: Redis connection URL
144
145Redis connection URL for persistent caching. Enables distributed caching and better performance.
146
147**Examples**:
148```bash
149# Local Redis (no auth)
150REDIS_URL=redis://localhost:6379/0
151
152# With authentication
153REDIS_URL=redis://user:password@redis.example.com:6379/0
154
155# Using database 1
156REDIS_URL=redis://localhost:6379/1
157
158# Redis Sentinel
159REDIS_URL=redis-sentinel://sentinel1:26379,sentinel2:26379/mymaster/0
160
161# TLS connection
162REDIS_URL=rediss://secure-redis.example.com:6380/0
163```
164
165### `SQLITE_URL`
166
167**Required**: No (recommended for single-instance production)
168**Type**: String
169**Format**: SQLite database URL
170
171SQLite database URL for persistent caching. Provides single-file persistent storage without external dependencies.
172
173**Examples**:
174```bash
175# File-based database (recommended)
176SQLITE_URL=sqlite:./quickdid.db
177
178# With absolute path
179SQLITE_URL=sqlite:/var/lib/quickdid/cache.db
180
181# In-memory database (testing only)
182SQLITE_URL=sqlite::memory:
183
184# Alternative file syntax
185SQLITE_URL=sqlite:///path/to/database.db
186```
187
188**Cache Priority**: QuickDID uses the first available cache:
1891. Redis (if `REDIS_URL` is configured)
1902. SQLite (if `SQLITE_URL` is configured)
1913. In-memory cache (fallback)
192
193### `CACHE_TTL_MEMORY`
194
195**Required**: No
196**Type**: Integer (seconds)
197**Default**: `600` (10 minutes)
198**Range**: 60-3600 (recommended)
199**Constraints**: Must be > 0
200
201Time-to-live for in-memory cache entries in seconds. Used when Redis is not available.
202
203**Examples**:
204```bash
205CACHE_TTL_MEMORY=300 # 5 minutes (aggressive refresh)
206CACHE_TTL_MEMORY=600 # 10 minutes (default, balanced)
207CACHE_TTL_MEMORY=1800 # 30 minutes (less frequent updates)
208CACHE_TTL_MEMORY=3600 # 1 hour (stable data)
209```
210
211**Recommendations**:
212- Lower values: Fresher data, more DNS/HTTP lookups, higher load
213- Higher values: Better performance, potentially stale data
214- Production with Redis: Can use lower values (300-600)
215- Production without Redis: Use higher values (1800-3600)
216
217### `CACHE_TTL_REDIS`
218
219**Required**: No
220**Type**: Integer (seconds)
221**Default**: `7776000` (90 days)
222**Range**: 3600-31536000 (1 hour to 1 year)
223**Constraints**: Must be > 0
224
225Time-to-live for Redis cache entries in seconds.
226
227**Examples**:
228```bash
229CACHE_TTL_REDIS=3600 # 1 hour (frequently changing data)
230CACHE_TTL_REDIS=86400 # 1 day (recommended for active handles)
231CACHE_TTL_REDIS=604800 # 1 week (balanced)
232CACHE_TTL_REDIS=2592000 # 30 days (stable handles)
233CACHE_TTL_REDIS=7776000 # 90 days (default, maximum stability)
234```
235
236### `CACHE_TTL_SQLITE`
237
238**Required**: No
239**Type**: Integer (seconds)
240**Default**: `7776000` (90 days)
241**Range**: 3600-31536000 (1 hour to 1 year)
242**Constraints**: Must be > 0
243
244Time-to-live for SQLite cache entries in seconds. Only used when `SQLITE_URL` is configured.
245
246**Examples**:
247```bash
248CACHE_TTL_SQLITE=3600 # 1 hour (frequently changing data)
249CACHE_TTL_SQLITE=86400 # 1 day (recommended for active handles)
250CACHE_TTL_SQLITE=604800 # 1 week (balanced)
251CACHE_TTL_SQLITE=2592000 # 30 days (stable handles)
252CACHE_TTL_SQLITE=7776000 # 90 days (default, maximum stability)
253```
254
255**TTL Recommendations**:
256- Social media handles: 1-7 days
257- Corporate/stable handles: 30-90 days
258- Test environments: 1 hour
259- Single-instance deployments: Can use longer TTLs (30-90 days)
260- Multi-instance deployments: Use shorter TTLs (1-7 days)
261
262## Queue Configuration
263
264### `QUEUE_ADAPTER`
265
266**Required**: No
267**Type**: String
268**Default**: `mpsc`
269**Values**: `mpsc`, `redis`, `sqlite`, `noop`, `none`
270
271The type of queue adapter for background handle resolution.
272
273**Options**:
274- `mpsc`: In-memory multi-producer single-consumer queue (default)
275- `redis`: Redis-backed distributed queue
276- `sqlite`: SQLite-backed persistent queue
277- `noop`: Disable queue processing (testing only)
278- `none`: Alias for `noop`
279
280**Examples**:
281```bash
282# Single instance deployment
283QUEUE_ADAPTER=mpsc
284
285# Multi-instance or high availability
286QUEUE_ADAPTER=redis
287
288# Single instance with persistence
289QUEUE_ADAPTER=sqlite
290
291# Testing without background processing
292QUEUE_ADAPTER=noop
293
294# Alternative syntax for disabling
295QUEUE_ADAPTER=none
296```
297
298### `QUEUE_REDIS_URL`
299
300**Required**: No
301**Type**: String
302**Default**: Falls back to `REDIS_URL`
303
304Dedicated Redis URL for queue operations. Use when separating cache and queue Redis instances.
305
306**Examples**:
307```bash
308# Separate Redis for queues
309QUEUE_REDIS_URL=redis://queue-redis:6379/2
310
311# With different credentials
312QUEUE_REDIS_URL=redis://queue_user:queue_pass@redis.example.com:6379/1
313```
314
315### `QUEUE_REDIS_PREFIX`
316
317**Required**: No
318**Type**: String
319**Default**: `queue:handleresolver:`
320
321Redis key prefix for queue operations. Use to namespace queues when sharing Redis.
322
323**Examples**:
324```bash
325# Default
326QUEUE_REDIS_PREFIX=queue:handleresolver:
327
328# Environment-specific
329QUEUE_REDIS_PREFIX=prod:queue:hr:
330QUEUE_REDIS_PREFIX=staging:queue:hr:
331
332# Version-specific
333QUEUE_REDIS_PREFIX=quickdid:v1:queue:
334
335# Instance-specific
336QUEUE_REDIS_PREFIX=us-east-1:queue:hr:
337```
338
339### `QUEUE_REDIS_TIMEOUT`
340
341**Required**: No
342**Type**: Integer (seconds)
343**Default**: `5`
344**Range**: 1-60 (recommended)
345**Constraints**: Must be > 0
346
347Redis blocking timeout for queue operations in seconds. Controls how long to wait for new items.
348
349**Examples**:
350```bash
351QUEUE_REDIS_TIMEOUT=1 # Very responsive, more polling
352QUEUE_REDIS_TIMEOUT=5 # Default, balanced
353QUEUE_REDIS_TIMEOUT=10 # Less polling, slower shutdown
354QUEUE_REDIS_TIMEOUT=30 # Minimal polling, slow shutdown
355```
356
357### `QUEUE_REDIS_DEDUP_ENABLED`
358
359**Required**: No
360**Type**: Boolean
361**Default**: `false`
362
363Enable deduplication for Redis queue to prevent duplicate handles from being queued multiple times within the TTL window. When enabled, uses Redis SET with TTL to track handles currently being processed.
364
365**Examples**:
366```bash
367# Enable deduplication (recommended for production)
368QUEUE_REDIS_DEDUP_ENABLED=true
369
370# Disable deduplication (default)
371QUEUE_REDIS_DEDUP_ENABLED=false
372```
373
374**Use cases**:
375- **Production**: Enable to prevent duplicate work and reduce load
376- **High-traffic**: Essential to avoid processing the same handle multiple times
377- **Development**: Can be disabled for simpler debugging
378
379### `QUEUE_REDIS_DEDUP_TTL`
380
381**Required**: No
382**Type**: Integer (seconds)
383**Default**: `60`
384**Range**: 10-300 (recommended)
385**Constraints**: Must be > 0 when deduplication is enabled
386
387TTL for Redis queue deduplication keys in seconds. Determines how long to prevent duplicate handle resolution requests.
388
389**Examples**:
390```bash
391# Quick deduplication window (10 seconds)
392QUEUE_REDIS_DEDUP_TTL=10
393
394# Default (1 minute)
395QUEUE_REDIS_DEDUP_TTL=60
396
397# Extended deduplication (5 minutes)
398QUEUE_REDIS_DEDUP_TTL=300
399```
400
401**Recommendations**:
402- **Fast processing**: 10-30 seconds
403- **Normal processing**: 60 seconds (default)
404- **Slow processing or high load**: 120-300 seconds
405
406### `QUEUE_WORKER_ID`
407
408**Required**: No
409**Type**: String
410**Default**: `worker1`
411
412Worker identifier for queue operations. Used in logs and monitoring.
413
414**Examples**:
415```bash
416# Simple numbering
417QUEUE_WORKER_ID=worker-001
418
419# Environment-based
420QUEUE_WORKER_ID=prod-us-east-1
421QUEUE_WORKER_ID=staging-worker-2
422
423# Hostname-based
424QUEUE_WORKER_ID=$(hostname)
425
426# Pod name in Kubernetes
427QUEUE_WORKER_ID=$HOSTNAME
428```
429
430### `QUEUE_BUFFER_SIZE`
431
432**Required**: No
433**Type**: Integer
434**Default**: `1000`
435**Range**: 100-100000 (recommended)
436
437Buffer size for the MPSC queue adapter. Only used when `QUEUE_ADAPTER=mpsc`.
438
439**Examples**:
440```bash
441QUEUE_BUFFER_SIZE=100 # Minimal memory, may block
442QUEUE_BUFFER_SIZE=1000 # Default, balanced
443QUEUE_BUFFER_SIZE=5000 # High traffic
444QUEUE_BUFFER_SIZE=10000 # Very high traffic
445```
446
447### `QUEUE_SQLITE_MAX_SIZE`
448
449**Required**: No
450**Type**: Integer
451**Default**: `10000`
452**Range**: 100-1000000 (recommended)
453**Constraints**: Must be >= 0
454
455Maximum queue size for SQLite adapter work shedding. When the queue exceeds this limit, the oldest entries are automatically deleted to maintain the specified size limit, preserving the most recently queued work items.
456
457**Work Shedding Behavior**:
458- New work items are always accepted
459- When queue size exceeds `QUEUE_SQLITE_MAX_SIZE`, oldest entries are deleted
460- Deletion happens atomically with insertion in a single transaction
461- Essential for long-running deployments to prevent unbounded disk growth
462- Set to `0` to disable work shedding (unlimited queue size)
463
464**Examples**:
465```bash
466QUEUE_SQLITE_MAX_SIZE=0 # Unlimited (disable work shedding)
467QUEUE_SQLITE_MAX_SIZE=1000 # Small deployment, frequent processing
468QUEUE_SQLITE_MAX_SIZE=10000 # Default, balanced for most deployments
469QUEUE_SQLITE_MAX_SIZE=100000 # High-traffic deployment with slower processing
470QUEUE_SQLITE_MAX_SIZE=1000000 # Very high-traffic, maximum recommended
471```
472
473**Recommendations**:
474- **Small deployments**: 1000-5000 entries
475- **Production deployments**: 10000-50000 entries
476- **High-traffic deployments**: 50000-1000000 entries
477- **Development/testing**: 100-1000 entries
478- **Disk space concerns**: Lower values (1000-5000)
479- **High ingestion rate**: Higher values (50000-1000000)
480
481## Rate Limiting Configuration
482
483### `RESOLVER_MAX_CONCURRENT`
484
485**Required**: No
486**Type**: Integer
487**Default**: `0` (disabled)
488**Range**: 0-10000
489**Constraints**: Must be between 0 and 10000
490
491Maximum concurrent handle resolutions allowed. When set to a value greater than 0, enables semaphore-based rate limiting to protect upstream DNS and HTTP services from being overwhelmed.
492
493**How it works**:
494- Uses a semaphore to limit concurrent resolutions
495- Applied between the base resolver and caching layers
496- Requests wait for an available permit before resolution
497- Helps prevent overwhelming upstream services
498
499**Examples**:
500```bash
501# Disabled (default)
502RESOLVER_MAX_CONCURRENT=0
503
504# Light rate limiting
505RESOLVER_MAX_CONCURRENT=10
506
507# Moderate rate limiting
508RESOLVER_MAX_CONCURRENT=50
509
510# Heavy traffic with rate limiting
511RESOLVER_MAX_CONCURRENT=100
512
513# Maximum allowed
514RESOLVER_MAX_CONCURRENT=10000
515```
516
517**Recommendations**:
518- **Development**: 0 (disabled) or 10-50 for testing
519- **Production (low traffic)**: 50-100
520- **Production (high traffic)**: 100-500
521- **Production (very high traffic)**: 500-1000
522- **Testing rate limiting**: 1-5 to observe behavior
523
524**Placement in resolver stack**:
525```
526Request → Cache → RateLimited → Base → DNS/HTTP
527```
528
529### `RESOLVER_MAX_CONCURRENT_TIMEOUT_MS`
530
531**Required**: No
532**Type**: Integer (milliseconds)
533**Default**: `0` (no timeout)
534**Range**: 0-60000
535**Constraints**: Must be between 0 and 60000 (60 seconds max)
536
537Timeout for acquiring a rate limit permit in milliseconds. When set to a value greater than 0, requests will timeout if they cannot acquire a permit within the specified time, preventing them from waiting indefinitely when the rate limiter is at capacity.
538
539**How it works**:
540- Applied when `RESOLVER_MAX_CONCURRENT` is enabled (> 0)
541- Uses `tokio::time::timeout` to limit permit acquisition time
542- Returns an error if timeout expires before permit is acquired
543- Prevents request queue buildup during high load
544
545**Examples**:
546```bash
547# No timeout (default)
548RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=0
549
550# Quick timeout for responsive failures (100ms)
551RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=100
552
553# Moderate timeout (1 second)
554RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=1000
555
556# Longer timeout for production (5 seconds)
557RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=5000
558
559# Maximum allowed (60 seconds)
560RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=60000
561```
562
563**Recommendations**:
564- **Development**: 100-1000ms for quick feedback
565- **Production (low latency)**: 1000-5000ms
566- **Production (high latency tolerance)**: 5000-30000ms
567- **Testing**: 100ms to quickly identify bottlenecks
568- **0**: Use when you want requests to wait indefinitely
569
570**Error behavior**:
571When a timeout occurs, the request fails with:
572```
573Rate limit permit acquisition timed out after {timeout}ms
574```
575
576## Metrics Configuration
577
578### `METRICS_ADAPTER`
579
580**Required**: No
581**Type**: String
582**Default**: `noop`
583**Values**: `noop`, `statsd`
584
585Metrics adapter type for collecting and publishing metrics.
586
587**Options**:
588- `noop`: No metrics collection (default)
589- `statsd`: Send metrics to StatsD server
590
591**Examples**:
592```bash
593# No metrics (default)
594METRICS_ADAPTER=noop
595
596# Enable StatsD metrics
597METRICS_ADAPTER=statsd
598```
599
600### `METRICS_STATSD_HOST`
601
602**Required**: Yes (when METRICS_ADAPTER=statsd)
603**Type**: String
604**Format**: hostname:port
605
606StatsD server host and port for metrics collection.
607
608**Examples**:
609```bash
610# Local StatsD
611METRICS_STATSD_HOST=localhost:8125
612
613# Remote StatsD
614METRICS_STATSD_HOST=statsd.example.com:8125
615
616# Docker network
617METRICS_STATSD_HOST=statsd:8125
618```
619
620### `METRICS_STATSD_BIND`
621
622**Required**: No
623**Type**: String
624**Default**: `[::]:0`
625
626Bind address for StatsD UDP socket. Controls which local address to bind for sending UDP packets.
627
628**Examples**:
629```bash
630# IPv6 any address, random port (default)
631METRICS_STATSD_BIND=[::]:0
632
633# IPv4 any address, random port
634METRICS_STATSD_BIND=0.0.0.0:0
635
636# Specific interface
637METRICS_STATSD_BIND=192.168.1.100:0
638
639# Specific port
640METRICS_STATSD_BIND=[::]:8126
641```
642
643### `METRICS_PREFIX`
644
645**Required**: No
646**Type**: String
647**Default**: `quickdid`
648
649Prefix for all metrics. Used to namespace metrics in your monitoring system.
650
651**Examples**:
652```bash
653# Default
654METRICS_PREFIX=quickdid
655
656# Environment-specific
657METRICS_PREFIX=prod.quickdid
658METRICS_PREFIX=staging.quickdid
659
660# Region-specific
661METRICS_PREFIX=us-east-1.quickdid
662METRICS_PREFIX=eu-west-1.quickdid
663
664# Service-specific
665METRICS_PREFIX=api.quickdid
666```
667
668### `METRICS_TAGS`
669
670**Required**: No
671**Type**: String (comma-separated key:value pairs)
672**Default**: None
673
674Default tags for all metrics. Added to all metrics for filtering and grouping.
675
676**Examples**:
677```bash
678# Basic tags
679METRICS_TAGS=env:production,service:quickdid
680
681# Detailed tags
682METRICS_TAGS=env:production,service:quickdid,region:us-east-1,version:1.0.0
683
684# Deployment-specific
685METRICS_TAGS=env:staging,cluster:k8s-staging,namespace:quickdid
686```
687
688**Common tag patterns**:
689- `env`: Environment (production, staging, development)
690- `service`: Service name
691- `region`: Geographic region
692- `version`: Application version
693- `cluster`: Kubernetes cluster name
694- `instance`: Instance identifier
695
696## Proactive Refresh Configuration
697
698### `PROACTIVE_REFRESH_ENABLED`
699
700**Required**: No
701**Type**: Boolean
702**Default**: `false`
703
704Enable proactive cache refresh for frequently accessed handles. When enabled, cache entries that have reached the refresh threshold will be queued for background refresh to keep the cache warm.
705
706**Examples**:
707```bash
708# Enable proactive refresh (recommended for production)
709PROACTIVE_REFRESH_ENABLED=true
710
711# Disable proactive refresh (default)
712PROACTIVE_REFRESH_ENABLED=false
713```
714
715**Benefits**:
716- Prevents cache misses for popular handles
717- Maintains consistent response times
718- Reduces latency spikes during cache expiration
719
720**Considerations**:
721- Increases background processing load
722- More DNS/HTTP requests to upstream services
723- Best for high-traffic services with predictable access patterns
724
725### `PROACTIVE_REFRESH_THRESHOLD`
726
727**Required**: No
728**Type**: Float
729**Default**: `0.8`
730**Range**: 0.0-1.0
731**Constraints**: Must be between 0.0 and 1.0
732
733Threshold as a percentage (0.0-1.0) of cache TTL when to trigger proactive refresh. For example, 0.8 means refresh when an entry has lived for 80% of its TTL.
734
735**Examples**:
736```bash
737# Very aggressive (refresh at 50% of TTL)
738PROACTIVE_REFRESH_THRESHOLD=0.5
739
740# Moderate (refresh at 70% of TTL)
741PROACTIVE_REFRESH_THRESHOLD=0.7
742
743# Default (refresh at 80% of TTL)
744PROACTIVE_REFRESH_THRESHOLD=0.8
745
746# Conservative (refresh at 90% of TTL)
747PROACTIVE_REFRESH_THRESHOLD=0.9
748
749# Very conservative (refresh at 95% of TTL)
750PROACTIVE_REFRESH_THRESHOLD=0.95
751```
752
753**Recommendations**:
754- **High-traffic services**: 0.5-0.7 (aggressive refresh)
755- **Normal traffic**: 0.8 (default, balanced)
756- **Low traffic**: 0.9-0.95 (conservative)
757- **Development**: 0.5 (test refresh behavior)
758
759**Impact on different cache TTLs**:
760- TTL=600s (10 min), threshold=0.8: Refresh after 8 minutes
761- TTL=3600s (1 hour), threshold=0.8: Refresh after 48 minutes
762- TTL=86400s (1 day), threshold=0.8: Refresh after 19.2 hours
763
764## Static Files Configuration
765
766### `STATIC_FILES_DIR`
767
768**Required**: No
769**Type**: String (directory path)
770**Default**: `www`
771
772Directory path for serving static files. This directory should contain the landing page and AT Protocol well-known files.
773
774**Directory Structure**:
775```
776www/
777├── index.html # Landing page
778├── .well-known/
779│ ├── atproto-did # Service DID identifier
780│ └── did.json # DID document
781└── (other static assets)
782```
783
784**Examples**:
785```bash
786# Default (relative to working directory)
787STATIC_FILES_DIR=www
788
789# Absolute path
790STATIC_FILES_DIR=/var/www/quickdid
791
792# Docker container path
793STATIC_FILES_DIR=/app/www
794
795# Custom directory
796STATIC_FILES_DIR=./public
797```
798
799**Docker Volume Mounting**:
800```yaml
801volumes:
802 # Mount entire custom directory
803 - ./custom-www:/app/www:ro
804
805 # Mount specific files
806 - ./custom-index.html:/app/www/index.html:ro
807 - ./well-known:/app/www/.well-known:ro
808```
809
810**Generating Well-Known Files**:
811```bash
812# Generate .well-known files for your domain
813HTTP_EXTERNAL=your-domain.com ./generate-wellknown.sh
814```
815
816## HTTP Caching Configuration
817
818### `CACHE_MAX_AGE`
819
820**Required**: No
821**Type**: Integer (seconds)
822**Default**: `86400` (24 hours)
823**Range**: 0-31536000 (0 to 1 year)
824
825Maximum age for HTTP Cache-Control header in seconds. When set to 0, the Cache-Control header is disabled and will not be added to responses. This controls how long clients and intermediate caches can cache responses.
826
827**Examples**:
828```bash
829# Default (24 hours)
830CACHE_MAX_AGE=86400
831
832# Aggressive caching (7 days)
833CACHE_MAX_AGE=604800
834
835# Conservative caching (1 hour)
836CACHE_MAX_AGE=3600
837
838# Disable Cache-Control header
839CACHE_MAX_AGE=0
840```
841
842### `CACHE_STALE_IF_ERROR`
843
844**Required**: No
845**Type**: Integer (seconds)
846**Default**: `172800` (48 hours)
847
848Allows stale content to be served if the backend encounters an error. This provides resilience during service outages.
849
850**Examples**:
851```bash
852# Default (48 hours)
853CACHE_STALE_IF_ERROR=172800
854
855# Extended error tolerance (7 days)
856CACHE_STALE_IF_ERROR=604800
857
858# Minimal error tolerance (1 hour)
859CACHE_STALE_IF_ERROR=3600
860```
861
862### `CACHE_STALE_WHILE_REVALIDATE`
863
864**Required**: No
865**Type**: Integer (seconds)
866**Default**: `86400` (24 hours)
867
868Allows stale content to be served while fresh content is being fetched in the background. This improves perceived performance.
869
870**Examples**:
871```bash
872# Default (24 hours)
873CACHE_STALE_WHILE_REVALIDATE=86400
874
875# Quick revalidation (1 hour)
876CACHE_STALE_WHILE_REVALIDATE=3600
877
878# Extended revalidation (7 days)
879CACHE_STALE_WHILE_REVALIDATE=604800
880```
881
882### `CACHE_MAX_STALE`
883
884**Required**: No
885**Type**: Integer (seconds)
886**Default**: `172800` (48 hours)
887
888Maximum time a client will accept stale responses. This provides an upper bound on how old cached content can be.
889
890**Examples**:
891```bash
892# Default (48 hours)
893CACHE_MAX_STALE=172800
894
895# Extended staleness (7 days)
896CACHE_MAX_STALE=604800
897
898# Strict freshness (1 hour)
899CACHE_MAX_STALE=3600
900```
901
902### `CACHE_MIN_FRESH`
903
904**Required**: No
905**Type**: Integer (seconds)
906**Default**: `3600` (1 hour)
907
908Minimum time a response must remain fresh. Clients will not accept responses that will expire within this time.
909
910**Examples**:
911```bash
912# Default (1 hour)
913CACHE_MIN_FRESH=3600
914
915# Strict freshness (24 hours)
916CACHE_MIN_FRESH=86400
917
918# Relaxed freshness (5 minutes)
919CACHE_MIN_FRESH=300
920```
921
922**Cache-Control Header Format**:
923
924When `CACHE_MAX_AGE` is greater than 0, the following Cache-Control header is added to responses:
925```
926Cache-Control: public, max-age=86400, stale-while-revalidate=86400, stale-if-error=172800, max-stale=172800, min-fresh=3600
927```
928
929**Recommendations**:
930- **High-traffic services**: Use longer max-age (86400-604800) to reduce load
931- **Frequently changing data**: Use shorter max-age (3600-14400)
932- **Critical services**: Set higher stale-if-error for resilience
933- **Performance-sensitive**: Enable stale-while-revalidate for better UX
934- **Disable caching**: Set CACHE_MAX_AGE=0 for real-time data
935
936### `ETAG_SEED`
937
938**Required**: No
939**Type**: String
940**Default**: Application version (from `CARGO_PKG_VERSION`)
941
942Seed value for ETAG generation to allow cache invalidation. This value is incorporated into ETAG checksums, allowing server administrators to invalidate client-cached responses after major changes or deployments.
943
944**How it works**:
945- Combined with response content to generate ETAG checksums
946- Uses MetroHash64 for fast, non-cryptographic hashing
947- Generates weak ETags (W/"hash") for HTTP caching
948- Changing the seed invalidates all client caches
949
950**Examples**:
951```bash
952# Default (uses application version)
953# ETAG_SEED is automatically set to the version
954
955# Deployment-specific seed
956ETAG_SEED=prod-2024-01-15
957
958# Version with timestamp
959ETAG_SEED=v1.0.0-1705344000
960
961# Environment-specific
962ETAG_SEED=staging-v2
963
964# Force cache invalidation after config change
965ETAG_SEED=config-update-2024-01-15
966```
967
968**Use cases**:
969- **Major configuration changes**: Update seed to invalidate all cached responses
970- **Data migration**: Force clients to refetch after backend changes
971- **Security updates**: Ensure clients get fresh data after security fixes
972- **A/B testing**: Different seeds for different deployment groups
973- **Rollback scenarios**: Revert to previous seed to restore cache behavior
974
975**Recommendations**:
976- **Default**: Use the application version (automatic)
977- **Production**: Include deployment date or config version
978- **Staging**: Use environment-specific seeds
979- **After incidents**: Update seed to force fresh data
980- **Routine deployments**: Keep the same seed if no data changes
981
982## Configuration Examples
983
984### Minimal Development Configuration
985
986```bash
987# .env.development
988HTTP_EXTERNAL=localhost:3007
989RUST_LOG=debug
990```
991
992### Standard Production Configuration (Redis)
993
994```bash
995# .env.production.redis
996# Required
997HTTP_EXTERNAL=quickdid.example.com
998
999# Network
1000HTTP_PORT=8080
1001USER_AGENT=quickdid/1.0.0 (+https://quickdid.example.com)
1002
1003# Caching (Redis-based)
1004REDIS_URL=redis://redis:6379/0
1005CACHE_TTL_MEMORY=600
1006CACHE_TTL_REDIS=86400 # 1 day
1007
1008# Queue
1009QUEUE_ADAPTER=redis
1010QUEUE_REDIS_TIMEOUT=5
1011QUEUE_BUFFER_SIZE=5000
1012QUEUE_REDIS_DEDUP_ENABLED=true # Prevent duplicate work
1013QUEUE_REDIS_DEDUP_TTL=60
1014
1015# Rate Limiting (optional, recommended for production)
1016RESOLVER_MAX_CONCURRENT=100
1017RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=5000 # 5 second timeout
1018
1019# Metrics (optional, recommended for production)
1020METRICS_ADAPTER=statsd
1021METRICS_STATSD_HOST=localhost:8125
1022METRICS_PREFIX=quickdid
1023METRICS_TAGS=env:prod,service:quickdid
1024
1025# Proactive Refresh (optional, recommended for high-traffic)
1026PROACTIVE_REFRESH_ENABLED=true
1027PROACTIVE_REFRESH_THRESHOLD=0.8
1028
1029# HTTP Caching (Cache-Control headers)
1030CACHE_MAX_AGE=86400 # 24 hours
1031CACHE_STALE_IF_ERROR=172800 # 48 hours
1032CACHE_STALE_WHILE_REVALIDATE=86400 # 24 hours
1033
1034# Logging
1035RUST_LOG=info
1036```
1037
1038### Standard Production Configuration (SQLite)
1039
1040```bash
1041# .env.production.sqlite
1042# Required
1043HTTP_EXTERNAL=quickdid.example.com
1044
1045# Network
1046HTTP_PORT=8080
1047USER_AGENT=quickdid/1.0.0 (+https://quickdid.example.com)
1048
1049# Caching (SQLite-based for single instance)
1050SQLITE_URL=sqlite:/data/quickdid.db
1051CACHE_TTL_MEMORY=600
1052CACHE_TTL_SQLITE=86400 # 1 day
1053
1054# Queue (SQLite for single instance with persistence)
1055QUEUE_ADAPTER=sqlite
1056QUEUE_BUFFER_SIZE=5000
1057QUEUE_SQLITE_MAX_SIZE=10000
1058
1059# Rate Limiting (optional, recommended for production)
1060RESOLVER_MAX_CONCURRENT=100
1061RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=5000 # 5 second timeout
1062
1063# HTTP Caching (Cache-Control headers)
1064CACHE_MAX_AGE=86400 # 24 hours
1065CACHE_STALE_IF_ERROR=172800 # 48 hours
1066CACHE_STALE_WHILE_REVALIDATE=86400 # 24 hours
1067
1068# Logging
1069RUST_LOG=info
1070```
1071
1072### High-Availability Configuration (Redis)
1073
1074```bash
1075# .env.ha.redis
1076# Required
1077HTTP_EXTERNAL=quickdid.example.com
1078
1079# Network
1080HTTP_PORT=8080
1081DNS_NAMESERVERS=8.8.8.8,8.8.4.4,1.1.1.1,1.0.0.1
1082
1083# Caching (separate Redis instances)
1084REDIS_URL=redis://cache-redis:6379/0
1085CACHE_TTL_MEMORY=300
1086CACHE_TTL_REDIS=3600
1087
1088# Queue (dedicated Redis)
1089QUEUE_ADAPTER=redis
1090QUEUE_REDIS_URL=redis://queue-redis:6379/0
1091QUEUE_REDIS_PREFIX=prod:queue:
1092QUEUE_WORKER_ID=${HOSTNAME:-worker1}
1093QUEUE_REDIS_TIMEOUT=10
1094QUEUE_REDIS_DEDUP_ENABLED=true # Essential for multi-instance
1095QUEUE_REDIS_DEDUP_TTL=120 # Longer TTL for HA
1096
1097# Performance
1098QUEUE_BUFFER_SIZE=10000
1099
1100# Rate Limiting (important for HA deployments)
1101RESOLVER_MAX_CONCURRENT=500
1102RESOLVER_MAX_CONCURRENT_TIMEOUT_MS=10000 # 10 second timeout for HA
1103
1104# Metrics (recommended for HA monitoring)
1105METRICS_ADAPTER=statsd
1106METRICS_STATSD_HOST=statsd:8125
1107METRICS_PREFIX=quickdid.prod
1108METRICS_TAGS=env:prod,service:quickdid,cluster:ha
1109
1110# Proactive Refresh (recommended for HA)
1111PROACTIVE_REFRESH_ENABLED=true
1112PROACTIVE_REFRESH_THRESHOLD=0.7 # More aggressive for HA
1113
1114# Logging
1115RUST_LOG=warn
1116```
1117
1118### Hybrid Configuration (Redis + SQLite Fallback)
1119
1120```bash
1121# .env.hybrid
1122# Required
1123HTTP_EXTERNAL=quickdid.example.com
1124
1125# Network
1126HTTP_PORT=8080
1127
1128# Caching (Redis primary, SQLite fallback)
1129REDIS_URL=redis://redis:6379/0
1130SQLITE_URL=sqlite:/data/fallback.db
1131CACHE_TTL_MEMORY=600
1132CACHE_TTL_REDIS=86400
1133CACHE_TTL_SQLITE=604800 # 1 week (longer for fallback)
1134
1135# Queue
1136QUEUE_ADAPTER=redis
1137QUEUE_REDIS_TIMEOUT=5
1138
1139# Logging
1140RUST_LOG=info
1141```
1142
1143### Docker Compose Configuration (Redis)
1144
1145```yaml
1146# docker-compose.redis.yml
1147version: '3.8'
1148
1149services:
1150 quickdid:
1151 image: quickdid:latest
1152 environment:
1153 HTTP_EXTERNAL: quickdid.example.com
1154 HTTP_PORT: 8080
1155 REDIS_URL: redis://redis:6379/0
1156 CACHE_TTL_MEMORY: 600
1157 CACHE_TTL_REDIS: 86400
1158 QUEUE_ADAPTER: redis
1159 QUEUE_REDIS_TIMEOUT: 5
1160 RUST_LOG: info
1161 ports:
1162 - "8080:8080"
1163 depends_on:
1164 - redis
1165
1166 redis:
1167 image: redis:7-alpine
1168 command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
1169```
1170
1171### Docker Compose Configuration (SQLite)
1172
1173```yaml
1174# docker-compose.sqlite.yml
1175version: '3.8'
1176
1177services:
1178 quickdid:
1179 image: quickdid:latest
1180 environment:
1181 HTTP_EXTERNAL: quickdid.example.com
1182 HTTP_PORT: 8080
1183 SQLITE_URL: sqlite:/data/quickdid.db
1184 CACHE_TTL_MEMORY: 600
1185 CACHE_TTL_SQLITE: 86400
1186 QUEUE_ADAPTER: sqlite
1187 QUEUE_BUFFER_SIZE: 5000
1188 QUEUE_SQLITE_MAX_SIZE: 10000
1189 RUST_LOG: info
1190 ports:
1191 - "8080:8080"
1192 volumes:
1193 - quickdid-data:/data
1194
1195volumes:
1196 quickdid-data:
1197 driver: local
1198```
1199
1200## Validation Rules
1201
1202QuickDID validates configuration at startup. The following rules are enforced:
1203
1204### Required Fields
1205
12061. **HTTP_EXTERNAL**: Must be provided
12072. **HTTP_EXTERNAL**: Must be provided
1208
1209### Value Constraints
1210
12111. **TTL Values** (`CACHE_TTL_MEMORY`, `CACHE_TTL_REDIS`, `CACHE_TTL_SQLITE`):
1212 - Must be positive integers (> 0)
1213 - Recommended minimum: 60 seconds
1214
12152. **Timeout Values** (`QUEUE_REDIS_TIMEOUT`):
1216 - Must be positive integers (> 0)
1217 - Recommended range: 1-60 seconds
1218
12193. **Queue Adapter** (`QUEUE_ADAPTER`):
1220 - Must be one of: `mpsc`, `redis`, `sqlite`, `noop`, `none`
1221 - Case-sensitive
1222
12234. **Rate Limiting** (`RESOLVER_MAX_CONCURRENT`):
1224 - Must be between 0 and 10000
1225 - 0 = disabled (default)
1226 - Values > 10000 will fail validation
1227
12285. **Rate Limiting Timeout** (`RESOLVER_MAX_CONCURRENT_TIMEOUT_MS`):
1229 - Must be between 0 and 60000 (milliseconds)
1230 - 0 = no timeout (default)
1231 - Values > 60000 will fail validation
1232
12336. **Port** (`HTTP_PORT`):
1234 - Must be valid port number (1-65535)
1235 - Ports < 1024 require elevated privileges
1236
1237### Validation Errors
1238
1239If validation fails, QuickDID will exit with one of these error codes:
1240
1241- `error-quickdid-config-1`: Missing required environment variable
1242- `error-quickdid-config-2`: Invalid configuration value
1243- `error-quickdid-config-3`: Invalid TTL value (must be positive)
1244- `error-quickdid-config-4`: Invalid timeout value (must be positive)
1245
1246### Testing Configuration
1247
1248Test your configuration without starting the service:
1249
1250```bash
1251# Validate configuration
1252HTTP_EXTERNAL=test quickdid --help
1253
1254# Test with specific values
1255CACHE_TTL_MEMORY=0 quickdid --help # Will fail validation
1256
1257# Check parsed configuration (with debug logging)
1258RUST_LOG=debug HTTP_EXTERNAL=test quickdid
1259```
1260
1261## Best Practices
1262
1263### Security
1264
12651. Use environment-specific configuration management
12662. Use TLS for Redis connections in production (`rediss://`)
12673. Never commit sensitive configuration to version control
12685. Implement network segmentation for Redis access
1269
1270### Performance
1271
12721. **With Redis**: Use lower memory cache TTL (300-600s)
12732. **With SQLite**: Use moderate memory cache TTL (600-1800s)
12743. **Without persistent cache**: Use higher memory cache TTL (1800-3600s)
12754. **High traffic**: Increase QUEUE_BUFFER_SIZE (5000-10000)
12765. **Multi-region**: Use region-specific QUEUE_WORKER_ID
1277
1278### Caching and Queue Strategy
1279
12801. **Multi-instance/HA deployments**: Use Redis for distributed caching and queuing
12812. **Single-instance deployments**: Use SQLite for persistent caching and queuing
12823. **Development/testing**: Use memory-only caching with MPSC queuing
12834. **Hybrid setups**: Configure both Redis and SQLite for redundancy
12845. **Queue adapter guidelines**:
1285 - Redis: Best for multi-instance deployments with distributed processing
1286 - SQLite: Best for single-instance deployments needing persistence
1287 - MPSC: Best for single-instance deployments without persistence needs
12886. **Cache TTL guidelines**:
1289 - Redis: Shorter TTLs (1-7 days) for frequently updated handles
1290 - SQLite: Longer TTLs (7-90 days) for stable single-instance caching
1291 - Memory: Short TTLs (5-30 minutes) as fallback
1292
1293### Monitoring
1294
12951. Set descriptive QUEUE_WORKER_ID for log correlation
12962. Use structured logging with appropriate RUST_LOG levels
12973. Monitor Redis memory usage and adjust TTLs accordingly
12984. Track cache hit rates to optimize TTL values
1299
1300### Deployment
1301
13021. Use `.env` files for local development
13032. Use secrets management for production configurations
13043. Set resource limits in container orchestration
13054. Use health checks to monitor service availability
13065. Implement gradual rollouts with feature flags
13076. **SQLite deployments**: Ensure persistent volume for database file
13087. **Redis deployments**: Configure Redis persistence and backup
13098. **Hybrid deployments**: Test fallback scenarios (Redis unavailable)