/ .env.example
.env.example
1 # ───────────────────────────────────────────────────────────────────────────── 2 # .env — Core pipeline and operational configuration 3 # ───────────────────────────────────────────────────────────────────────────── 4 # This is the main config file. API keys live in .env.secrets, agent system 5 # config lives in .env.agents. All three are loaded by src/utils/load-env.js. 6 # 7 # Values set in .env take priority over .env.secrets and .env.agents. 8 9 # ─── ZenRows Configuration ────────────────────────────────────────────────── 10 # Vendor limits: Free=5 concurrent/1,000 req/day; Developer=10/unlimited; Business=100/unlimited 11 # Default: 19 (5% below Business plan baseline of 20) — increase for higher-tier plans 12 ZENROWS_CONCURRENCY=19 13 # Daily request quota (Free/Developer: 1,000/day). Set to 0 to disable cap. 14 # ZENROWS_DAILY_LIMIT removed — monthly subscription has no daily limit (confirmed 2026-03-06) 15 # Enable premium proxy for geo-targeting (required for multi-country support) 16 ZENROWS_PREMIUM=false 17 # ZenRows subscription cost for status cost display (flat monthly, not per-request) 18 ZENROWS_MONTHLY_COST=69.99 19 # ZenRows billing cycle day-of-month (billing date each month) 20 ZENROWS_BILLING_DAY=3 21 # Countries needing extended ZenRows timeouts (comma-separated ISO codes) 22 # Known slow: JP (Japan), ID (Indonesia) — add others as observed via logs 23 ZENROWS_SLOW_COUNTRIES=JP,ID 24 ZENROWS_SLOW_TIMEOUT=300000 25 26 # ─── Claude Model Tiers ────────────────────────────────────────────────────── 27 # Update these to upgrade all Claude usage at once (OpenRouter format) 28 CLAUDE_SONNET_MODEL=anthropic/claude-sonnet-4-6 29 CLAUDE_HAIKU_MODEL=anthropic/claude-haiku-4-5 30 CLAUDE_OPUS_MODEL=anthropic/claude-opus-4 31 32 # ─── LLM Model Configuration ──────────────────────────────────────────────── 33 # OpenRouter format: provider/model-name (works with both OpenRouter and Anthropic) 34 SCORING_MODEL=openai/gpt-4o-mini # Initial AI vision scoring of screenshots 35 PROPOSAL_MODEL=anthropic/claude-haiku-4-5 # Generate personalised proposals 36 POLISH_MODEL=google/gemini-2.0-flash-001 # Polish/grammar-check proposals (cheap, fast) 37 ENRICHMENT_MODEL=openai/gpt-4o-mini # Extract contact info from web pages 38 VISION_MODEL=openai/gpt-4o-mini # General-purpose vision model 39 CLASSIFICATION_MODEL=anthropic/claude-haiku-4-5 # Reply classification (fast, cheap) 40 AUDIT_REPORT_MODEL=anthropic/claude-opus-4 # Premium audit reports (Opus + extended thinking) 41 42 # Sage AI Auto-Fix (optional — only needed for automated quality fixes) 43 #SAGE_AUTOFIX_MODEL=claude-sonnet-4-6 44 #SAGE_AUTOFIX_BRANCH=sage-autofix 45 46 # ─── OpenRouter Credit Monitoring ──────────────────────────────────────────── 47 # Alert threshold in USD (warning when low, critical when exhausted) 48 OPENROUTER_CREDIT_THRESHOLD=10.0 49 50 # ─── LLM Cost Guardrails ──────────────────────────────────────────────────── 51 # Daily spending cap in USD (hard limit, blocks calls when exceeded) 52 LLM_DAILY_BUDGET=50 53 # Hourly spending alert threshold in USD (warning only, does not block) 54 LLM_HOURLY_ALERT=10 55 56 # ─── Scoring & Proposals ──────────────────────────────────────────────────── 57 # Sites scoring at or below this are eligible for proposals 58 # B (82) and below: B- (70-81), C (50-69), D (30-49), E (0-29) 59 LOW_SCORE_CUTOFF=82 60 61 # Only mention competitor score if it's at least this many points higher 62 COMPETITOR_SCORE_THRESHOLD=10 63 64 # 'true' = LLM proposals ($0.18/site via OpenRouter), 'false' = template-based (zero cost) 65 USE_LLM_PROPOSALS=true 66 67 # Maximum related keywords to fetch per seed keyword (DataForSEO) 68 KEYWORD_EXPANSION_LIMIT=50 69 70 # ─── Vision & Screenshot Configuration ────────────────────────────────────── 71 # true: Full pipeline with screenshots + vision analysis → assets → scoring → rescoring → enrich 72 # ⚠️ INCURS LLM API COSTS: ~$0.030/site via OpenRouter (outside Claude Max subscription) 73 # false: HTML-only mode, no screenshots, skip rescoring (83% cost savings — recommended) 74 ENABLE_VISION=true 75 76 # LLM Scoring: false = use programmatic rule-based scoring (no API cost) 77 # true = use LLM scoring via OpenRouter (GPT-4o-mini). 78 # Claude Max orchestrator handles the zero-cost path separately. 79 # Default: true (LLM scoring enabled) 80 ENABLE_LLM_SCORING=true 81 82 # LLM Enrichment: false = regex-only contact extraction from HTML (no API cost) 83 # true = use LLM for location/business name extraction via OpenRouter. 84 # Claude Max orchestrator handles the zero-cost path separately. 85 # Default: true (LLM enrichment enabled) 86 ENABLE_ENRICHMENT_LLM=true 87 88 # DEPRECATED: Use ENABLE_VISION instead (ignored if ENABLE_VISION is set) 89 # USE_COMPUTER_VISION_SCORING=true 90 # USE_COMPUTER_VISION_RESCORING=true 91 # USE_COMPUTER_VISION_ENRICHMENT=true 92 # ENABLE_SCREENSHOT_CAPTURE=true 93 94 # ─── API Rate Limits ──────────────────────────────────────────────────────── 95 # Throughput set safely below vendor and regulatory limits. 96 # Twilio + Resend are 30% below vendor limits; others are 2–5% below. 97 # See src/utils/rate-limiter.js for where these are applied. 98 99 # OpenRouter: 200 RPM paid plan; 20 RPM free tier 100 OPENROUTER_REQUESTS_PER_MINUTE=194 101 OPENROUTER_MAX_CONCURRENT=5 102 103 # Twilio SMS: Long code = 1 SMS/sec; Toll-free = 3 SMS/sec; Short code = 100 SMS/sec 104 # TCPA/A2P compliance enforced separately in compliance.js 105 TWILIO_REQUESTS_PER_SECOND=0.7 106 TWILIO_MAX_CONCURRENT=1 107 # Pre-tracking SMS spend not recorded in DB — added to lifetime cost estimate in npm run status 108 # Set to actual spend before tracking began (default $7.30 = Feb spend before tracking started) 109 TWILIO_COST_OFFSET_USD=7.30 110 111 # Resend: 10 req/sec all paid plans 112 RESEND_REQUESTS_PER_SECOND=1.5 # actual plan limit: 2 req/sec (docs say 10 but plan enforces 2) 113 RESEND_MAX_CONCURRENT=1 114 115 # DataForSEO: 2,000 req/min; 100 concurrent 116 DATAFORSEO_REQUESTS_PER_MINUTE=1940 117 DATAFORSEO_MAX_CONCURRENT=97 118 119 # ZeroBounce email validation: ~100 req/sec vendor limit 120 # Set ZEROBOUNCE_ENABLED=false to skip validation (e.g. during testing) 121 # API key goes in .env.secrets — see ZEROBOUNCE_API_KEY entry there 122 ZEROBOUNCE_ENABLED=true 123 ZEROBOUNCE_REQUESTS_PER_SECOND=40 124 ZEROBOUNCE_MAX_CONCURRENT=5 125 ZEROBOUNCE_CACHE_TTL_DAYS=90 126 127 # ─── Brand / White-Label ──────────────────────────────────────────────────── 128 # Used across emails, PDFs, LLM prompts, URL allowlists, and HTTP-Referer headers 129 BRAND_NAME=Audit&Fix 130 BRAND_DOMAIN=auditandfix.com 131 BRAND_URL=https://auditandfix.com 132 133 # ─── Persona / Operator Identity ──────────────────────────────────────────── 134 # The person (or persona) behind outreach — used in proposals, emails, LLM prompts 135 PERSONA_NAME=Marcus Webb 136 PERSONA_FIRST_NAME=Marcus 137 138 # ─── Sender Identity ──────────────────────────────────────────────────────── 139 SENDER_NAME=John Smith 140 SENDER_EMAIL=john@yourcompany.com 141 SENDER_PHONE=+1234567890 142 SENDER_COMPANY=Your Company Name 143 EMAIL_SIGNATURE=Best regards,\nJohn Smith\nYour Company Name 144 145 # CAN-SPAM Physical Address (required for commercial emails to US, CA, AU, NZ, UK, EU) 146 # Format: Street address, City, State/Province, Postal Code, Country 147 CAN_SPAM_PHYSICAL_ADDRESS= 148 149 # ─── Tracking & Unsubscribe ───────────────────────────────────────────────── 150 UNSUBSCRIBE_BASE_URL=https://yourdomain.com/unsubscribe 151 152 # PayPal checkout branding — should match BRAND_NAME 153 PAYPAL_BRAND_NAME=Audit&Fix 154 BASE_URL=http://localhost:3000 155 DEFAULT_PAYER_EMAIL=customer@example.com 156 157 # ─── Autoresponder ─────────────────────────────────────────────────────────── 158 # LLM-powered auto-reply to inbound SMS/email messages (Claude Opus via OpenRouter) 159 # true = auto-send replies without human approval; false = disable autoresponder entirely 160 AUTORESPONDER_ENABLED=true 161 162 # ─── Sales Page ────────────────────────────────────────────────────────────── 163 # Cloudflare Worker — bridges PHP sales page with 333Method 164 API_WORKER_URL=https://auditandfix-api.auditandfix.workers.dev 165 API_WORKER_SECRET=your-shared-secret 166 # BRAND_URL (defined above) used to build /o/{site_id} short order links in SMS replies 167 168 # PayPal live credentials (set in PHP server environment, not here) 169 # PAYPAL_MODE=live 170 # PAYPAL_CLIENT_ID=your-live-client-id 171 # PAYPAL_CLIENT_SECRET=your-live-client-secret 172 173 # PayPal sandbox credentials — used when ?sandbox=1 param is present (E2E testing) 174 PAYPAL_SANDBOX_CLIENT_ID=your-sandbox-client-id 175 PAYPAL_SANDBOX_CLIENT_SECRET=your-sandbox-client-secret 176 PAYPAL_SANDBOX_BUYER_EMAIL=sandbox-buyer@personal.example.com 177 PAYPAL_SANDBOX_BUYER_PASSWORD=sandbox-buyer-password 178 179 # PayPal webhook verification (add as Cloudflare Worker secret: wrangler secret put PAYPAL_WEBHOOK_ID) 180 PAYPAL_WEBHOOK_ID=your-webhook-id 181 182 # PayPal worker internal auth secret (add as Cloudflare Worker secret: wrangler secret put PAYPAL_WORKER_SECRET) 183 # Used to authenticate GET/DELETE /paypal-events.json calls from local pipeline poller 184 PAYPAL_WORKER_SECRET=your-paypal-worker-secret 185 186 # Resend webhook Worker URL (deployed via wrangler) 187 # Prod: https://resend-webhook-worker.auditandfix.workers.dev 188 # Register in Resend dashboard → Settings → Webhooks 189 # Resend webhook verification (add as Cloudflare Worker secret: wrangler secret put RESEND_WEBHOOK_SECRET) 190 # Format: whsec_... (from Resend dashboard → Webhooks → Signing Secret) 191 RESEND_WEBHOOK_SECRET=whsec_your-signing-secret 192 193 # Resend worker internal auth secret (add as Cloudflare Worker secret: wrangler secret put RESEND_WORKER_SECRET) 194 # Used to authenticate GET/POST/DELETE /email-events.json calls from local pipeline 195 RESEND_WORKER_SECRET=your-resend-worker-secret 196 197 # Sender email for purchase confirmation and report delivery 198 SENDER_EMAIL=reports@auditandfix.com 199 200 # Legal contact email (shown in Privacy Policy, Terms, Impressum, Cookie Policy) 201 LEGALS_EMAIL=legals@auditandfix.com 202 203 # Business postal address (shown in Privacy Policy, Terms, Impressum) 204 # Format: Street, Suburb NSW POSTCODE, Australia 205 BUSINESS_ADDRESS=NSW, Australia 206 207 # Legal operator name (shown in Impressum as "Verantwortliche Person / Responsible Person") 208 OPERATOR_NAME=Your Name 209 210 # ─── Database & Storage ───────────────────────────────────────────────────── 211 DATABASE_PATH=./db/sites.db 212 SCREENSHOT_BASE_PATH=./screenshots 213 214 # ─── Cron Circuit Breaker ─────────────────────────────────────────────────── 215 # Fallback if not set in settings table. Dashboard can toggle dynamically. 216 # Set to 'false' to disable all cron jobs (useful for maintenance/debugging). 217 CRON_CIRCUIT_BREAKER_ENABLED=true 218 219 # ─── Pipeline Stage Control ───────────────────────────────────────────────── 220 # Skip stages (comma-separated): serps, assets, enrich, proposals, outreach, replies 221 # Note: scoring and rescoring are orchestrator-only — not valid pipeline stage names. 222 # API quota stages (serps, etc) self-manage via circuit breakers — don't list them here. 223 # Restart service after changing: systemctl --user restart 333method-pipeline 224 SKIP_STAGES= 225 226 # Skip outreach methods (comma-separated): sms, email, form, x, linkedin 227 OUTREACH_SKIP_METHODS= 228 229 # Block outreach to specific countries (comma-separated ISO codes) 230 # Use this to exclude countries with unresolved compliance requirements. 231 # 232 # GDPR countries (need Legitimate Interests Assessment on file): 233 # DE,FR,IT,ES,NL,BE,AT,SE,DK,NO,IE,PL,PT,FI,CZ,HU,RO,BG,HR,SK,SI,LT,LV,EE,LU,MT,CY,GR 234 # UK (UK GDPR / ICO — needs separate LIA under UK GDPR): GB 235 # 236 # Remove countries from this list once their compliance requirements are met. 237 # See docs/09-business/auditandfix-business-plan.md Risk Management section for rationale. 238 OUTREACH_BLOCKED_COUNTRIES=DE,FR,IT,ES,NL,BE,AT,SE,DK,NO,IE,PL,GB 239 240 # Block SMS outreach to specific countries (comma-separated ISO codes) 241 # Separate from OUTREACH_BLOCKED_COUNTRIES which blocks ALL channels. 242 # TCPA: US/CA cold SMS requires prior express written consent for telemarketing. 243 # Facebook v. Duguid (2021) defense may apply but untested for our use case. 244 # Email/form outreach to US/CA is NOT blocked. 245 OUTREACH_BLOCKED_SMS_COUNTRIES=US,CA 246 247 # Sites processed per cycle, per stage. Default: 5. Increase to clear backlogs (50–100). 248 PIPELINE_BATCH_MAX_SIZE=200 249 # PIPELINE_BATCH_MIN_THRESHOLD=5 # skip stage if backlog below this (avoids spin-up overhead) 250 251 # Delay between pipeline stage cycles in milliseconds (default: 1000) 252 PIPELINE_CYCLE_DELAY_MS=1000 253 254 # Interval for checking if pipeline is manually paused in milliseconds (default: 5000) 255 PIPELINE_PAUSE_CHECK_MS=5000 256 257 # ─── Stage Concurrency ────────────────────────────────────────────────────── 258 # Max parallel operations per stage. Can be adjusted live — takes effect after each site. 259 # Browser stages use adaptive scaling (reduces automatically under high load). 260 BROWSER_CONCURRENCY=5 # Max concurrent browser instances for Assets stage 261 ENRICHMENT_CONCURRENCY=5 # Max concurrent browser instances for Enrich stage 262 SCORING_CONCURRENCY=5 # Max concurrent API calls for Scoring stage 263 264 # Screen-aware throttling: tighter thresholds when physical monitor is on (user present) 265 # Screen OFF (AFK): ease=0.4, max=0.8 → Screen ON (user present): defaults below 266 SCREEN_ON_EASE_LOAD=0.2 # Normalised load below which full concurrency is used 267 SCREEN_ON_MAX_LOAD=0.5 # Normalised load above which minimum concurrency is used 268 269 # CPU gate for browser loop: pause all browser work if instantaneous CPU exceeds this 270 # Uses real-time 200ms CPU samples — much faster than loadavg (30-60s lag) 271 BROWSER_CPU_GATE=0.80 272 273 # ─── Browser Configuration ────────────────────────────────────────────────── 274 # Optional: Override Chromium path (auto-detected if not set) 275 # Use ./scripts/chromium-nice to run chromium at low priority (nice -n 19) 276 # CHROMIUM_PATH=./scripts/chromium-nice 277 278 # NixOS: Set custom browser path if Playwright can't write to Nix store 279 # PLAYWRIGHT_BROWSERS_PATH=/home/username/.cache/ms-playwright 280 281 # Separate user data directory prevents lock file conflicts with daily browser 282 # PLAYWRIGHT_USER_DATA_DIR=./.playwright-userdata 283 284 # ─── CAPTCHA Solving (NopeCHA) ─────────────────────────────────────────────── 285 # Optional: auto-solve reCAPTCHA v2, hCaptcha, and Cloudflare Turnstile on contact forms 286 # CAPTCHA solving providers — fastest provider used first (benchmarked every 30min via cron) 287 # If not set, CAPTCHAs require manual solving by the operator 288 # NopeCHA: https://nopecha.com — 100 solves/day free, $5/mth for 2000/day 289 # NOPECHA_API_KEY=your-nopecha-api-key 290 # NOPECHA_API_KEY_2=your-second-nopecha-api-key # round-robins with primary key 291 # CapMonster: https://capmonster.cloud — backup provider, cheaper than anti-captcha 292 # CAPMONSTER_API_KEY=your-capmonster-api-key 293 294 # ─── Browser Profiles (X & LinkedIn persistent sessions) ───────────────────── 295 # Profiles store cookies/storage so you don't need to re-login every run 296 BROWSER_PROFILES_DIR=./.browser-profiles 297 X_PROFILE_COUNT=3 298 LINKEDIN_PROFILE_COUNT=3 299 300 # ─── Timezone & Locale ────────────────────────────────────────────────────── 301 # Timezone should match your IP location to avoid fingerprint inconsistencies 302 TIMEZONE=Australia/Sydney 303 # Examples: en-AU,en;q=0.9 (Australian), en-US,en;q=0.9 (American) 304 ACCEPT_LANGUAGE=en-AU,en;q=0.9 305 306 # ─── X (Twitter) Automation XPath Selectors ────────────────────────────────── 307 # These may change over time as X updates their UI 308 X_LOGIN_BUTTON_XPATH=//div[contains(@style, "position: absolute; bottom: 0px; width: 100%")]//span[text()="Log in"] 309 X_PROFILE_LINK_XPATH=//a[@aria-label="Profile"] 310 X_USERNAME_DIV_XPATH=//div[@data-testid="UserName"] 311 X_MESSAGE_BUTTON_XPATH=//button[@aria-label="Message"] 312 313 # ─── Free Website Scanner (Inbound Funnel) ─────────────────────────────────── 314 # Scoring runs in the Cloudflare Worker (auditandfix-api) — no local server needed. 315 # The NixOS poll daemon (npm run score-api) pulls scans from Worker KV into SQLite. 316 # 317 # API_WORKER_URL and API_WORKER_SECRET are already defined above 318 # (shared with purchase queue — same Worker, same secret). 319 # 320 # Also set in Hostinger .htaccess (same API_WORKER_URL value): 321 # SetEnv API_WORKER_URL https://auditandfix-api.auditandfix.workers.dev 322 323 # ─── Dashboard (optional) ─────────────────────────────────────────────────── 324 DASHBOARD_PORT=8501 325 DASHBOARD_REFRESH_INTERVAL=60 # Auto-refresh interval in seconds (0 = manual) 326 DASHBOARD_CACHE_TTL=300 # Query cache TTL in seconds 327 DASHBOARD_PAGE_SIZE=50 # Pagination size for tables 328 329 # ─── Debug ─────────────────────────────────────────────────────────────────── 330 # Uncomment to enable verbose debug logging in pipeline and agent loggers 331 # DEBUG=true 332 333 # ─── End-to-End Pipeline Test ──────────────────────────────────────────────── 334 # See .env.secrets for TEST_E2E_URL, TEST_E2E_PASSWORD 335 # No Resend/Twilio magic test addresses need configuration — they're hardcoded: 336 # Resend: delivered@resend.dev, bounced@resend.dev, complained@resend.dev 337 # Twilio: +15005550006 (valid), +15005550001 (invalid), +15005550007 (no SMS) 338 TEST_E2E_KEYWORD=site:yoursite.com/terms 339 340 # ─── Loopback SMS E2E Test (npm run test:e2e:sms) ──────────────────────────── 341 # Sends a real SMS from a Twilio number to itself to verify full send→receive loop. 342 # Throttled to once per 24 hours (gate file: /tmp/sms-loopback-gate.json). 343 # Override throttle: SMS_LOOPBACK_FORCE=1 npm run test:e2e:sms 344 # 345 # TWILIO_LOOPBACK_FROM — Twilio number that sends the test SMS (e.g. AU: +61468089949) 346 # TWILIO_LOOPBACK_TO — different Twilio number that receives it (e.g. CA: +18254794242) 347 # Note: FROM and TO must be different — Twilio rejects sending from a number to itself 348 TWILIO_LOOPBACK_FROM=+61468089949 349 TWILIO_LOOPBACK_TO=+18254794242 350 # Inbound email integration tests — point to test subdomain inbox to isolate from prod 351 # TEST_EMAIL_EVENTS_WORKER_URL is auto-applied during inbound E2E tests (reverts when done) 352 TEST_INBOUND_EMAIL=marcus@test.auditandfix.com 353 TEST_EMAIL_EVENTS_WORKER_URL=https://resend-webhook-worker-test.auditandfix.workers.dev