# Server PORT=3000 HOST=0.0.0.0 SITE_URL=http://localhost:3000 # Optional URL path prefix to mount the whole app under (e.g. /blog). Empty = root. # Static assets (/assets, /media) and /health stay at root; everything else # (pages, feeds, sitemap, robots, admin) is served under this prefix. BASE_PATH= SITE_TITLE=EchoPost SITE_DESCRIPTION=Personal content archive and X mirror NODE_ENV=development # Analytics GA_MEASUREMENT_ID= # Google Analytics 4 measurement ID (e.g. G-XXXXXXXXXX); empty = disabled # Database DATABASE_PATH=./data/echopost.db # Security APP_ENCRYPTION_KEY= # 32+ char random string, used for token encryption + visitor hashing SESSION_SECRET= # 32+ char random string, used for session cookie signing ADMIN_PASSWORD= # initial admin password (min 8 chars); hashed on first boot (or use npm run create-admin) ADMIN_IP_ALLOWLIST= # optional: restrict /admin to these IPs/CIDRs (comma-separated). Empty = no restriction. Honors X-Forwarded-For (see TRUSTED_PROXY). # How much of X-Forwarded-For to trust when deriving the client IP (req.ip), which # backs the admin IP allowlist and the login/subscribe rate limits. SECURITY: do # NOT set "true" unless a proxy rewrites XFF, or clients can spoof their IP and # defeat those controls. Use the number of proxy hops in front of the app: # 1 (default) = one reverse proxy (nginx/Caddy); 0/false = exposed directly; # a CIDR list (e.g. 10.0.0.0/8) = trust only those proxy addresses. TRUSTED_PROXY=1 # X (Twitter) API X_CLIENT_ID= X_CLIENT_SECRET= X_BEARER_TOKEN= X_REDIRECT_URI=http://localhost:3000/admin/x/callback X_USERNAME= # the X handle to mirror (without @) X_BACKFILL_BATCH_SIZE=100 # older posts imported per "Sync older posts" batch # WordPress import (npm run import-wordpress) — imports posts as type=blog WORDPRESS_URL= # e.g. https://blog.example.com (its host is auto-allowlisted for media) WORDPRESS_PER_PAGE=100 # items fetched per WP REST API page (max 100) WORDPRESS_CONTENT_TYPES=posts,pages # which WP content types to import (posts, pages, or both) # Media storage MEDIA_STORAGE_DRIVER=local # local | s3 MEDIA_STORAGE_PATH=./data/media MEDIA_PUBLIC_URL=/media MEDIA_EXTRA_ALLOWED_HOSTS= # extra comma-separated hosts allowed for media mirroring (e.g. a WP CDN) MEDIA_MAX_DOWNLOAD_BYTES=104857600 # per-file mirror cap in bytes (default 100 MB; covers X video MP4s) # Outbound links in rendered content to these hosts (and their subdomains) stay # "follow"; every other off-site link gets rel="nofollow". The site's own host is # always included automatically. Comma-separated; list your own properties here. LINK_FOLLOW_HOSTS= S3_ENDPOINT= S3_BUCKET= S3_ACCESS_KEY= S3_SECRET_KEY= S3_PUBLIC_URL= # Email provider: console | resend | postmark | smtp | webhook EMAIL_PROVIDER=console EMAIL_FROM=newsletter@example.com # must be a verified sender (e.g. an AWS SES identity) RESEND_API_KEY= POSTMARK_API_TOKEN= NEWSLETTER_WEBHOOK_URL= # forward confirmed subscribers to an external provider # SMTP (EMAIL_PROVIDER=smtp). Works with AWS SES SMTP, Mailgun, a relay, etc. # AWS SES example: host email-smtp.us-east-1.amazonaws.com, port 587 (STARTTLS). # Use SMTP_SECURE=true only on the TLS-wrapper port (465/2465). SMTP_HOST= # e.g. email-smtp.us-east-1.amazonaws.com SMTP_PORT=587 # 587/2587 STARTTLS, or 465/2465 with SMTP_SECURE=true SMTP_USER= # SES SMTP username (starts with AKIA…) SMTP_PASS= # SES SMTP password (NOT your AWS secret access key) SMTP_SECURE=false # Optional AI (AMA + title generation) LLM_PROVIDER= # anthropic | openai | (empty = heuristic only) ANTHROPIC_API_KEY= OPENAI_API_KEY= OPENAI_MODEL=gpt-5.4-nano # optional, defaults to gpt-5.4-nano OPENAI_BASE_URL= # optional, defaults to https://api.openai.com/v1 # IndexNow (instant Bing/Yandex/Seznam/Naver indexing pings on publish) # Enabled automatically in production; set INDEXNOW_ENABLED=false to opt out # (or =true to test locally). The ownership key is auto-generated and persisted # on first boot — set INDEXNOW_KEY only to pin a specific key (8-128 chars, a-z0-9-). INDEXNOW_ENABLED= INDEXNOW_KEY= # Backups BACKUP_PATH=./backups