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.
···83838484## Minimum Configuration
85858686-QuickDID requires the following environment variables to run. Configuration is validated at startup, and the service will exit with specific error codes if validation fails.
8686+QuickDID requires minimal configuration to run. Configuration is validated at startup, and the service will exit with specific error codes if validation fails.
87878888### Required
89899090- `HTTP_EXTERNAL`: External hostname for service endpoints (e.g., `localhost:3007`)
9191-- `SERVICE_KEY`: Private key for service identity in DID format (e.g., `did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK`)
92919392### Example Minimal Setup
94939594```bash
9696-HTTP_EXTERNAL=localhost:3007 \
9797-SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK \
9898-cargo run
9595+HTTP_EXTERNAL=localhost:3007 cargo run
9696+```
9797+9898+### Static Files
9999+100100+QuickDID serves static files from the `www` directory by default. This includes:
101101+- Landing page (`index.html`)
102102+- AT Protocol well-known files (`.well-known/atproto-did` and `.well-known/did.json`)
103103+104104+Generate the `.well-known` files for your deployment:
105105+106106+```bash
107107+HTTP_EXTERNAL=your-domain.com ./generate-wellknown.sh
99108```
100109101110This will start QuickDID with:
···155164- `PROACTIVE_REFRESH_ENABLED`: Enable proactive cache refreshing (default: false)
156165- `PROACTIVE_REFRESH_THRESHOLD`: Refresh when TTL remaining is below this threshold (0.0-1.0, default: 0.8)
157166167167+#### Static Files
168168+- `STATIC_FILES_DIR`: Directory for serving static files (default: www)
169169+158170#### Logging
159171- `RUST_LOG`: Logging level (e.g., debug, info, warn, error)
160172···163175#### Redis-based with Metrics (Multi-instance/HA)
164176```bash
165177HTTP_EXTERNAL=quickdid.example.com \
166166-SERVICE_KEY=did:key:yourkeyhere \
167178HTTP_PORT=3000 \
168179REDIS_URL=redis://localhost:6379 \
169180CACHE_TTL_REDIS=86400 \
···183194#### SQLite-based (Single-instance)
184195```bash
185196HTTP_EXTERNAL=quickdid.example.com \
186186-SERVICE_KEY=did:key:yourkeyhere \
187197HTTP_PORT=3000 \
188198SQLITE_URL=sqlite:./quickdid.db \
189199CACHE_TTL_SQLITE=86400 \
···4040**Constraints**:
4141- Must be a valid hostname or hostname:port combination
4242- Port (if specified) must be between 1-65535
4343-- Used to generate service DID (did:web:{HTTP_EXTERNAL})
4444-4545-### `SERVICE_KEY`
4646-4747-**Required**: Yes
4848-**Type**: String
4949-**Format**: DID private key
5050-**Security**: SENSITIVE - Never commit to version control
5151-5252-The private key for the service's AT Protocol identity. This key is used to sign responses and authenticate the service.
5353-5454-**Examples**:
5555-```bash
5656-# did:key format (Ed25519)
5757-SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK
5858-5959-# did:plc format
6060-SERVICE_KEY=did:plc:xyz123abc456def789
6161-```
6262-6363-**Constraints**:
6464-- Must be a valid DID format
6565-- Must include the private key component
6666-- Should be stored securely (e.g., secrets manager, encrypted storage)
67436844## Network Configuration
6945···785761- TTL=3600s (1 hour), threshold=0.8: Refresh after 48 minutes
786762- TTL=86400s (1 day), threshold=0.8: Refresh after 19.2 hours
787763764764+## Static Files Configuration
765765+766766+### `STATIC_FILES_DIR`
767767+768768+**Required**: No
769769+**Type**: String (directory path)
770770+**Default**: `www`
771771+772772+Directory path for serving static files. This directory should contain the landing page and AT Protocol well-known files.
773773+774774+**Directory Structure**:
775775+```
776776+www/
777777+├── index.html # Landing page
778778+├── .well-known/
779779+│ ├── atproto-did # Service DID identifier
780780+│ └── did.json # DID document
781781+└── (other static assets)
782782+```
783783+784784+**Examples**:
785785+```bash
786786+# Default (relative to working directory)
787787+STATIC_FILES_DIR=www
788788+789789+# Absolute path
790790+STATIC_FILES_DIR=/var/www/quickdid
791791+792792+# Docker container path
793793+STATIC_FILES_DIR=/app/www
794794+795795+# Custom directory
796796+STATIC_FILES_DIR=./public
797797+```
798798+799799+**Docker Volume Mounting**:
800800+```yaml
801801+volumes:
802802+ # Mount entire custom directory
803803+ - ./custom-www:/app/www:ro
804804+805805+ # Mount specific files
806806+ - ./custom-index.html:/app/www/index.html:ro
807807+ - ./well-known:/app/www/.well-known:ro
808808+```
809809+810810+**Generating Well-Known Files**:
811811+```bash
812812+# Generate .well-known files for your domain
813813+HTTP_EXTERNAL=your-domain.com ./generate-wellknown.sh
814814+```
815815+788816## HTTP Caching Configuration
789817790818### `CACHE_MAX_AGE`
···958986```bash
959987# .env.development
960988HTTP_EXTERNAL=localhost:3007
961961-SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK
962989RUST_LOG=debug
963990```
964991···968995# .env.production.redis
969996# Required
970997HTTP_EXTERNAL=quickdid.example.com
971971-SERVICE_KEY=${SECRET_SERVICE_KEY} # From secrets manager
972998973999# Network
9741000HTTP_PORT=8080
···10151041# .env.production.sqlite
10161042# Required
10171043HTTP_EXTERNAL=quickdid.example.com
10181018-SERVICE_KEY=${SECRET_SERVICE_KEY} # From secrets manager
1019104410201045# Network
10211046HTTP_PORT=8080
···10501075# .env.ha.redis
10511076# Required
10521077HTTP_EXTERNAL=quickdid.example.com
10531053-SERVICE_KEY=${SECRET_SERVICE_KEY}
1054107810551079# Network
10561080HTTP_PORT=8080
···10971121# .env.hybrid
10981122# Required
10991123HTTP_EXTERNAL=quickdid.example.com
11001100-SERVICE_KEY=${SECRET_SERVICE_KEY}
1101112411021125# Network
11031126HTTP_PORT=8080
···11281151 image: quickdid:latest
11291152 environment:
11301153 HTTP_EXTERNAL: quickdid.example.com
11311131- SERVICE_KEY: ${SERVICE_KEY}
11321154 HTTP_PORT: 8080
11331155 REDIS_URL: redis://redis:6379/0
11341156 CACHE_TTL_MEMORY: 600
···11571179 image: quickdid:latest
11581180 environment:
11591181 HTTP_EXTERNAL: quickdid.example.com
11601160- SERVICE_KEY: ${SERVICE_KEY}
11611182 HTTP_PORT: 8080
11621183 SQLITE_URL: sqlite:/data/quickdid.db
11631184 CACHE_TTL_MEMORY: 600
···11831204### Required Fields
11841205118512061. **HTTP_EXTERNAL**: Must be provided
11861186-2. **SERVICE_KEY**: Must be provided
12071207+2. **HTTP_EXTERNAL**: Must be provided
1187120811881209### Value Constraints
11891210···1228124912291250```bash
12301251# Validate configuration
12311231-HTTP_EXTERNAL=test SERVICE_KEY=test quickdid --help
12521252+HTTP_EXTERNAL=test quickdid --help
1232125312331254# Test with specific values
12341255CACHE_TTL_MEMORY=0 quickdid --help # Will fail validation
1235125612361257# Check parsed configuration (with debug logging)
12371237-RUST_LOG=debug HTTP_EXTERNAL=test SERVICE_KEY=test quickdid
12581258+RUST_LOG=debug HTTP_EXTERNAL=test quickdid
12381259```
1239126012401261## Best Practices
1241126212421263### Security
1243126412441244-1. **Never commit SERVICE_KEY** to version control
12451245-2. Use environment-specific key management (Vault, AWS Secrets, etc.)
12461246-3. Rotate SERVICE_KEY regularly
12471247-4. Use TLS for Redis connections in production (`rediss://`)
12651265+1. Use environment-specific configuration management
12661266+2. Use TLS for Redis connections in production (`rediss://`)
12671267+3. Never commit sensitive configuration to version control
124812685. Implement network segmentation for Redis access
1249126912501270### Performance
···12801300### Deployment
12811301128213021. Use `.env` files for local development
12831283-2. Use secrets management for production SERVICE_KEY
13031303+2. Use secrets management for production configurations
128413043. Set resource limits in container orchestration
128513054. Use health checks to monitor service availability
128613065. Implement gradual rollouts with feature flags
+18-14
docs/production-deployment.md
···4242# - localhost:3007 (for testing only)
4343HTTP_EXTERNAL=quickdid.example.com
44444545-# Private key for service identity (DID format)
4646-# Generate a new key for production using atproto-identity tools
4747-# SECURITY: Keep this key secure and never commit to version control
4848-# Example formats:
4949-# - did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK
5050-# - did:plc:xyz123abc456
5151-SERVICE_KEY=did:key:YOUR_PRODUCTION_KEY_HERE
5252-5345# ----------------------------------------------------------------------------
5446# NETWORK CONFIGURATION
5547# ----------------------------------------------------------------------------
···304296PROACTIVE_REFRESH_THRESHOLD=0.8
305297306298# ----------------------------------------------------------------------------
299299+# STATIC FILES CONFIGURATION
300300+# ----------------------------------------------------------------------------
301301+302302+# Directory path for serving static files (default: www)
303303+# This directory should contain:
304304+# - index.html (landing page)
305305+# - .well-known/atproto-did (service DID identifier)
306306+# - .well-known/did.json (DID document)
307307+# In Docker, this defaults to /app/www
308308+# You can mount custom files via Docker volumes
309309+STATIC_FILES_DIR=/app/www
310310+311311+# ----------------------------------------------------------------------------
307312# PERFORMANCE TUNING
308313# ----------------------------------------------------------------------------
309314···512517 container_name: quickdid-sqlite
513518 environment:
514519 HTTP_EXTERNAL: quickdid.example.com
515515- SERVICE_KEY: ${SERVICE_KEY}
516520 HTTP_PORT: 8080
517521 SQLITE_URL: sqlite:/data/quickdid.db
518522 CACHE_TTL_MEMORY: 600
···713717714718### 1. Service Key Protection
715719716716-- **Never commit** the `SERVICE_KEY` to version control
720720+- **Never commit** sensitive configuration to version control
717721- Store keys in a secure secret management system (e.g., HashiCorp Vault, AWS Secrets Manager)
718722- Rotate keys regularly
719723- Use different keys for different environments
···758762docker logs quickdid
759763760764# Verify environment variables
761761-docker exec quickdid env | grep -E "HTTP_EXTERNAL|SERVICE_KEY"
765765+docker exec quickdid env | grep -E "HTTP_EXTERNAL|HTTP_PORT"
762766763767# Test Redis connectivity
764768docker exec quickdid redis-cli -h redis ping
···943947### Required Fields
944948945949- **HTTP_EXTERNAL**: Must be provided
946946-- **SERVICE_KEY**: Must be provided
950950+- **HTTP_EXTERNAL**: Must be provided
947951948952### Value Constraints
949953···982986983987```bash
984988# Validate configuration without starting service
985985-HTTP_EXTERNAL=test SERVICE_KEY=test quickdid --help
989989+HTTP_EXTERNAL=test quickdid --help
986990987991# Test with specific values (will fail validation)
988992CACHE_TTL_MEMORY=0 quickdid --help
989993990994# Debug configuration parsing
991991-RUST_LOG=debug HTTP_EXTERNAL=test SERVICE_KEY=test quickdid
995995+RUST_LOG=debug HTTP_EXTERNAL=test quickdid
992996```
993997994998## Support and Resources
+59
generate-wellknown.sh
···11+#!/bin/bash
22+33+# Script to generate .well-known static files based on QuickDID configuration
44+# Usage: HTTP_EXTERNAL=quickdid.smokesignal.tools ./generate-wellknown.sh
55+#
66+# Note: Since we no longer process SERVICE_KEY, you'll need to manually
77+# add the public key to the did.json file if you need DID document support.
88+99+set -e
1010+1111+# Check required environment variables
1212+if [ -z "$HTTP_EXTERNAL" ]; then
1313+ echo "Error: HTTP_EXTERNAL environment variable is required"
1414+ echo "Usage: HTTP_EXTERNAL=example.com ./generate-wellknown.sh"
1515+ exit 1
1616+fi
1717+1818+# Ensure www/.well-known directory exists
1919+mkdir -p www/.well-known
2020+2121+# Generate service DID from HTTP_EXTERNAL
2222+if [[ "$HTTP_EXTERNAL" == *":"* ]]; then
2323+ # Contains port - URL encode the colon
2424+ SERVICE_DID="did:web:${HTTP_EXTERNAL//:/%3A}"
2525+else
2626+ SERVICE_DID="did:web:$HTTP_EXTERNAL"
2727+fi
2828+2929+echo "Generating .well-known files for $SERVICE_DID"
3030+3131+# Write atproto-did file
3232+echo "$SERVICE_DID" > www/.well-known/atproto-did
3333+echo "Created: www/.well-known/atproto-did"
3434+3535+# Create a basic did.json template
3636+# Note: You'll need to manually add the publicKeyMultibase if you need DID document support
3737+3838+cat > www/.well-known/did.json <<EOF
3939+{
4040+ "@context": [
4141+ "https://www.w3.org/ns/did/v1",
4242+ "https://w3id.org/security/multikey/v1"
4343+ ],
4444+ "id": "$SERVICE_DID",
4545+ "verificationMethod": [],
4646+ "service": [
4747+ {
4848+ "id": "${SERVICE_DID}#quickdid",
4949+ "type": "QuickDIDService",
5050+ "serviceEndpoint": "https://${HTTP_EXTERNAL}"
5151+ }
5252+ ]
5353+}
5454+EOF
5555+5656+echo "Created: www/.well-known/did.json"
5757+echo ""
5858+echo "Note: The did.json file is a basic template. If you need DID document support,"
5959+echo "you'll need to manually add the verificationMethod with your public key."
+1-24
src/bin/quickdid.rs
···11use anyhow::Result;
22use atproto_identity::{
33 config::{CertificateBundles, DnsNameservers},
44- key::{identify_key, to_public},
54 resolve::HickoryDnsResolver,
65};
76use quickdid::{
···2322 sqlite_schema::create_sqlite_pool,
2423 task_manager::spawn_cancellable_task,
2524};
2626-use serde_json::json;
2725use std::sync::Arc;
2826use tokio::signal;
2927use tokio_util::{sync::CancellationToken, task::TaskTracker};
···7977 println!(" -V, --version Print version information");
8078 println!();
8179 println!("ENVIRONMENT VARIABLES:");
8282- println!(" SERVICE_KEY Private key for service identity (required)");
8380 println!(
8481 " HTTP_EXTERNAL External hostname for service endpoints (required)"
8582 );
···191188 config.validate()?;
192189193190 tracing::info!("Starting QuickDID service on port {}", config.http_port);
194194- tracing::info!("Service DID: {}", config.service_did);
195191 tracing::info!(
196192 "Cache TTL - Memory: {}s, Redis: {}s, SQLite: {}s",
197193 config.cache_ttl_memory,
···225221226222 // Create DNS resolver
227223 let dns_resolver = HickoryDnsResolver::create_resolver(dns_nameservers.as_ref());
228228-229229- // Process service key
230230- let private_service_key_data = identify_key(&config.service_key)?;
231231- let public_service_key_data = to_public(&private_service_key_data)?;
232232- let public_service_key = public_service_key_data.to_string();
233233-234234- // Create service DID document
235235- let service_document = json!({
236236- "@context": vec!["https://www.w3.org/ns/did/v1", "https://w3id.org/security/multikey/v1"],
237237- "id": config.service_did.clone(),
238238- "verificationMethod": [{
239239- "id": format!("{}#atproto", config.service_did),
240240- "type": "Multikey",
241241- "controller": config.service_did.clone(),
242242- "publicKeyMultibase": public_service_key
243243- }],
244244- "service": []
245245- });
246224247225 // Create DNS resolver Arc for sharing
248226 let dns_resolver_arc = Arc::new(dns_resolver);
···543521544522 // Create app context with the queue adapter
545523 let app_context = AppContext::new(
546546- service_document,
547547- config.service_did.clone(),
548524 handle_resolver.clone(),
549525 handle_queue,
550526 metrics_publisher,
551527 config.etag_seed.clone(),
552528 config.cache_control_header.clone(),
529529+ config.static_files_dir.clone(),
553530 );
554531555532 // Create router
+9-32
src/config.rs
···1313//! ```bash
1414//! # Minimal configuration
1515//! HTTP_EXTERNAL=quickdid.example.com \
1616-//! SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK \
1716//! quickdid
1817//!
1918//! # Full configuration with Redis and custom settings
2019//! HTTP_EXTERNAL=quickdid.example.com \
2121-//! SERVICE_KEY=did:key:z42tmZxD2mi1TfMKSFrsRfednwdaaPNZiiWHP4MPgcvXkDWK \
2220//! HTTP_PORT=3000 \
2321//! REDIS_URL=redis://localhost:6379 \
2422//! CACHE_TTL_MEMORY=300 \
···3836pub enum ConfigError {
3937 /// Missing required environment variable or command-line argument
4038 ///
4141- /// Example: When SERVICE_KEY or HTTP_EXTERNAL are not provided
3939+ /// Example: When HTTP_EXTERNAL is not provided
4240 #[error("error-quickdid-config-1 Missing required environment variable: {0}")]
4341 MissingRequired(String),
4442···9795/// config.validate()?;
9896///
9997/// println!("Service running at: {}", config.http_external);
100100-/// println!("Service DID: {}", config.service_did);
10198/// # Ok(())
10299/// # }
103100/// ```
···112109 /// External hostname for service endpoints (e.g., "quickdid.example.com")
113110 pub http_external: String,
114111115115- /// Private key for service identity (e.g., "did:key:z42tm...")
116116- pub service_key: String,
117117-118112 /// HTTP User-Agent for outgoing requests (e.g., "quickdid/1.0.0 (+https://...)")
119113 pub user_agent: String,
120120-121121- /// Derived service DID (e.g., "did:web:quickdid.example.com")
122122- /// Automatically generated from http_external with proper encoding
123123- pub service_did: String,
124114125115 /// Custom DNS nameservers, comma-separated (e.g., "8.8.8.8,8.8.4.4")
126116 pub dns_nameservers: Option<String>,
···250240 /// For example, 0.8 means refresh when an entry has lived for 80% of its TTL.
251241 /// Default: 0.8 (80%)
252242 pub proactive_refresh_threshold: f64,
243243+244244+ /// Directory path for serving static files.
245245+ /// When set, the root handler will serve files from this directory.
246246+ /// Default: "www" (relative to working directory)
247247+ pub static_files_dir: String,
253248}
254249255250impl Config {
···257252 ///
258253 /// This method:
259254 /// 1. Reads configuration from environment variables
260260- /// 2. Validates required fields (HTTP_EXTERNAL and SERVICE_KEY)
261261- /// 3. Generates derived values (service_did from http_external)
262262- /// 4. Applies defaults where appropriate
255255+ /// 2. Validates required fields (HTTP_EXTERNAL)
256256+ /// 3. Applies defaults where appropriate
263257 ///
264258 /// ## Example
265259 ///
···270264 /// // Parse from environment variables
271265 /// let config = Config::from_env()?;
272266 ///
273273- /// // The service DID is automatically generated from HTTP_EXTERNAL
274274- /// assert!(config.service_did.starts_with("did:web:"));
275267 /// # Ok(())
276268 /// # }
277269 /// ```
···280272 ///
281273 /// Returns `ConfigError::MissingRequired` if:
282274 /// - HTTP_EXTERNAL is not provided
283283- /// - SERVICE_KEY is not provided
284275 pub fn from_env() -> Result<Self, ConfigError> {
285276 // Required fields
286277 let http_external = env::var("HTTP_EXTERNAL")
···288279 .filter(|s| !s.is_empty())
289280 .ok_or_else(|| ConfigError::MissingRequired("HTTP_EXTERNAL".to_string()))?;
290281291291- let service_key = env::var("SERVICE_KEY")
292292- .ok()
293293- .filter(|s| !s.is_empty())
294294- .ok_or_else(|| ConfigError::MissingRequired("SERVICE_KEY".to_string()))?;
295295-296282 // Generate default user agent
297283 let default_user_agent = format!(
298284 "quickdid/{} (+https://github.com/smokesignal.events/quickdid)",
299285 env!("CARGO_PKG_VERSION")
300286 );
301287302302- // Generate service DID from http_external
303303- let service_did = if http_external.contains(':') {
304304- let encoded_external = http_external.replace(':', "%3A");
305305- format!("did:web:{}", encoded_external)
306306- } else {
307307- format!("did:web:{}", http_external)
308308- };
309309-310288 let mut config = Config {
311289 http_port: get_env_or_default("HTTP_PORT", Some("8080")).unwrap(),
312290 plc_hostname: get_env_or_default("PLC_HOSTNAME", Some("plc.directory")).unwrap(),
313291 http_external,
314314- service_key,
315292 user_agent: get_env_or_default("USER_AGENT", None).unwrap_or(default_user_agent),
316316- service_did,
317293 dns_nameservers: get_env_or_default("DNS_NAMESERVERS", None),
318294 certificate_bundles: get_env_or_default("CERTIFICATE_BUNDLES", None),
319295 redis_url: get_env_or_default("REDIS_URL", None),
···350326 metrics_tags: get_env_or_default("METRICS_TAGS", None),
351327 proactive_refresh_enabled: parse_env("PROACTIVE_REFRESH_ENABLED", false)?,
352328 proactive_refresh_threshold: parse_env("PROACTIVE_REFRESH_THRESHOLD", 0.8)?,
329329+ static_files_dir: get_env_or_default("STATIC_FILES_DIR", Some("www")).unwrap(),
353330 };
354331355332 // Calculate the Cache-Control header value if enabled
···11+# QuickDID Static Files Directory
22+33+This directory contains static files that are served by QuickDID. By default, QuickDID serves files from the `www` directory, but this can be configured using the `STATIC_FILES_DIR` environment variable.
44+55+## Directory Structure
66+77+```
88+www/
99+├── .well-known/
1010+│ ├── atproto-did # AT Protocol DID identifier
1111+│ └── did.json # DID document
1212+├── index.html # Landing page
1313+└── README.md # This file
1414+```
1515+1616+## Files
1717+1818+### `.well-known/atproto-did`
1919+Contains the service's DID identifier (e.g., `did:web:example.com`). This file is used by AT Protocol clients to discover the service's DID.
2020+2121+### `.well-known/did.json`
2222+Contains the DID document with verification methods and service endpoints. This is a JSON-LD document following the W3C DID specification.
2323+2424+### `index.html`
2525+The landing page shown when users visit the root URL. This provides information about the service and available endpoints.
2626+2727+## Customization
2828+2929+### Using the Generation Script
3030+3131+You can generate the `.well-known` files for your deployment using the provided script:
3232+3333+```bash
3434+HTTP_EXTERNAL=your-domain.com ./generate-wellknown.sh
3535+```
3636+3737+This will create the appropriate files based on your domain.
3838+3939+### Manual Customization
4040+4141+1. **Update `.well-known/atproto-did`**: Replace with your service's DID
4242+2. **Update `.well-known/did.json`**: Add your public key to the `verificationMethod` array if needed
4343+3. **Customize `index.html`**: Modify the landing page to match your branding
4444+4545+### Docker Deployment
4646+4747+When using Docker, you can mount custom static files:
4848+4949+```yaml
5050+volumes:
5151+ - ./custom-www:/app/www:ro
5252+```
5353+5454+Or just override specific files:
5555+5656+```yaml
5757+volumes:
5858+ - ./custom-index.html:/app/www/index.html:ro
5959+ - ./custom-wellknown:/app/www/.well-known:ro
6060+```
6161+6262+### Environment Variable
6363+6464+You can change the static files directory using:
6565+6666+```bash
6767+STATIC_FILES_DIR=/path/to/custom/www
6868+```
6969+7070+## Security Notes
7171+7272+- Static files are served with automatic MIME type detection
7373+- The `.well-known` files are crucial for AT Protocol compatibility
7474+- Ensure proper permissions on mounted volumes in production