# grokcli-2api — high-concurrency template # 1) cp .env.example .env # 2) docker compose up -d # starts redis + postgres + app # 3) python migrate_json_to_pg.py --data-dir ./data # if migrating old JSON # # Never commit real secrets. # ── Core ────────────────────────────────────────────── # Container / app local timezone (logs, admin timestamps that use localtime) TZ=Asia/Shanghai GROK2API_HOST=0.0.0.0 GROK2API_PORT=3000 GROK2API_OPEN_BROWSER=0 # First-boot seed only — written into DB as password hash on startup if empty. # After that, change password in Admin → 系统设置 (store-backed). GROK2API_ADMIN_PASSWORD=change-me # GROK2API_PUBLIC_BASE_URL= # GROK2API_DATA_DIR=./data GROK2API_DEFAULT_MODEL=grok-4.5 GROK2API_ACCOUNT_MODE=round_robin # ── High concurrency (required) ─────────────────────── # auto = max(2, min(8, cpu_count)) when unset; compose default is 2 GROK2API_WORKERS=2 GROK2API_STORE_BACKEND=hybrid GROK2API_REQUIRE_SHARED_STORES=1 # Dev hot-reload (uvicorn file watch). Production: leave 0 / unset. # Forces single worker. Prefer: ./dev.sh # GROK2API_RELOAD=0 # Optional: comma-separated watch dirs / include / exclude globs # GROK2API_RELOAD_DIRS=.,store,static/js,static/admin # GROK2API_RELOAD_INCLUDES=*.py,*.js,*.html,*.css # GROK2API_RELOAD_EXCLUDES=*/data/*,*/__pycache__/*,*/static/dist/* # Local Python process (DB on host). docker compose overrides these with # service DNS and does NOT publish postgres/redis host ports by default: # REDIS_URL=redis://redis:6379/0 # DATABASE_URL=postgresql://grok2api:grok2api@postgres:5432/grok2api REDIS_URL=redis://127.0.0.1:6379/0 DATABASE_URL=postgresql://grok2api:grok2api@127.0.0.1:5432/grok2api # GROK2API_REDIS_URL= # GROK2API_DATABASE_URL= # GROK2API_MAINTAINER_LEADER=auto # GROK2API_REDIS_PREFIX=g2a # GROK2API_LIMIT_CONCURRENCY=2000 # GROK2API_KEEPALIVE=30 # Optional Fernet passphrase for API key secret at-rest encryption # GROK2API_SECRET_KEY=change-me-long-random # GROK2API_APIKEY_CACHE_TTL=5 # ── Relay / Claude Code / sub2api ───────────────────── # off (recommended): keep reasoning in reasoning_content for Claude/sub2api GROK2API_REASONING_COMPAT=off GROK2API_SSE_KEEPALIVE=4 # Consecutive is_disconnected() hits required before treating the client as # gone (default 2). Starlette can blip true once under write backpressure; # sticky single-hit latch used to hard-cut SSE mid-turn for sub2api/Claude Code. # GROK2API_DISCONNECT_HITS=2 # Upstream SSE idle read timeout (seconds) # GROK2API_HTTP_READ_TIMEOUT=180 # Overall request timeout # GROK2API_TIMEOUT=900 # Long tool-loop history compaction (OFF by default). GROK2API_HISTORY_COMPACT=0 # When compaction is on, keep older rewrites byte-stable across turns so # automatic upstream prompt-prefix cache can hit (same idea as grok-cli). GROK2API_HISTORY_PREFIX_STABLE=1 # Cap tools per assistant turn on Claude-compatible paths # (/v1/messages, /v1/responses via sub2api). Default 1 avoids sub2api multi-block # races ("Content block not found") that make Claude Code stop scheduling. GROK2API_OUTBOUND_MAX_TOOLS=1 # Pure OpenAI /v1/chat/completions default is unlimited (0). Raise only if an # OpenAI secondary relay also needs a hard multi-tool cap. GROK2API_OUTBOUND_MAX_TOOLS_OPENAI=0 # Real delay (seconds) between consecutive tool SSE frames. Keepalive alone is # not enough when sub2api drains a TCP window of multi-tool chunks in one tick. GROK2API_OUTBOUND_TOOL_GAP_SEC=0.08 # Set Claude path to 0 only if client is pure OpenAI (not Claude Code via sub2api) # GROK2API_OUTBOUND_MAX_TOOLS=0 # ── Token maintain / model health ───────────────────── GROK2API_TOKEN_MAINTAIN=1 GROK2API_MODEL_HEALTH=1 # GROK2API_TOKEN_MAINTAIN_INTERVAL=60 # Request-path floor (seconds before expiry). Background auto-widens to ~30–45m. # GROK2API_TOKEN_REFRESH_SKEW=180 # GROK2API_TOKEN_REFRESH_WORKERS=6 # GROK2API_MODEL_PROBE_WORKERS=4 # Base per-cycle batch; background auto-raises under near-expiry pressure (cap 120). # GROK2API_TOKEN_REFRESH_BATCH=50 # GROK2API_MODEL_PROBE_BATCH=30 # Comma-separated models for health probes. Background rotates ONE model per cycle # so a long list will not explode into accounts×models HTTP fan-out. # Prefer a single model for large pools (thousands of accounts). # GROK2API_PROBE_MODELS=grok-4.5 # Manual/admin probes: max models per account in one cycle (default 2). # GROK2API_MODEL_PROBE_MAX_MODELS_PER_ACCOUNT=1 # Large-pool tips (token refresh is adaptive; probe is priority/strict sweep): # 1k~5k: TOKEN_INTERVAL=60 TOKEN_BATCH=50 TOKEN_WORKERS=6 # 1w~2w: TOKEN_INTERVAL=45 TOKEN_BATCH=80 TOKEN_WORKERS=10 # Near-expiry waves auto-boost batch up to ~120 and shorten wait to 20–45s. # Full-pool ETA ≈ ceil(need_refresh / adaptive_batch) * interval. # Live traffic still marks 429/cooldown instantly; background mainly recovers # cooldown + fills unknown + pre-warms access tokens. # Permanent refresh failures (invalid_grant / revoked): hard-delete from pool. # Default 1. Set 0 only if you want soft-disable (keep credentials, leave pool). GROK2API_DELETE_INVALID_REFRESH=1 # ── SSO import performance ──────────────────────────── # Concurrent device-flow converts (network-bound). Default 12. # GROK2API_SSO_IMPORT_WORKERS=12 # Poll token immediately after approve; interval seconds (default ~1) # GROK2API_SSO_POLL_INTERVAL=1 # GROK2API_SSO_POLL_TIMEOUT=45 # GROK2API_SSO_HTTP_TIMEOUT=12 # ── Protocol registration (optional) ────────────────── # Mail provider: moemail | yyds | gptmail (keys are stored separately in DB) # GROK2API_MAIL_PROVIDER=moemail # MoeMail (self-hosted base URL required): # GROK2API_MOEMAIL_API_KEY= # GROK2API_MOEMAIL_BASE_URL=https://moemail.example.com # GROK2API_MOEMAIL_DOMAIN=example.com # YYDS (fixed host https://maliapi.215.im — no URL needed in admin UI): # GROK2API_YYDS_API_KEY=AC-... # GPTMail (fixed host https://mail.chatgpt.org.uk — no URL needed in admin UI): # GROK2API_GPTMAIL_API_KEY=sk-... # Cloudflare Temp Email (self-hosted Workers URL; admin password for /admin/new_address): # https://github.com/dreamhunter2333/cloudflare_temp_email # GROK2API_MAIL_PROVIDER=cfmail # GROK2API_CFMAIL_BASE_URL=https://your-worker.example.workers.dev # GROK2API_CFMAIL_API_KEY=your-admin-password # GROK2API_CFMAIL_DOMAIN=example.com # Captcha provider: local (inline in main container) | yescaptcha # GROK2API_CAPTCHA_PROVIDER=local # Inline solver default (same container loopback): # GROK2API_INLINE_SOLVER=1 # GROK2API_REG_CONCURRENCY=3 # Seconds to wait after a new registration is imported before auto probe. # Also configurable in admin UI as「测活等待秒」. 0 = probe immediately. # GROK2API_REG_PROBE_DELAY_SEC=30 # TURNSTILE_THREAD=3 # TURNSTILE_BROWSER_TYPE=camoufox # Lazy captcha browsers (default on): warm on first solve, reclaim after idle # TURNSTILE_LAZY=1 # TURNSTILE_IDLE_SEC=180 # GROK2API_YESCAPTCHA_KEY= # Single proxy (legacy) or first-line fallback: # GROK2API_XAI_PROXY=http://127.0.0.1:7890 # Multi-line proxy pool (preferred). One proxy per line; also accepts commas. # Used by: protocol registration + account-pool outbound (chat / probe / refresh). # Formats: http://host:port | socks5://user:pass@host:port | host:port:user:pass # GROK2API_XAI_PROXY_POOL= # Shared auth for lines that omit user/pass: # GROK2API_XAI_PROXY_USERNAME= # GROK2API_XAI_PROXY_PASSWORD= # Rotation: round_robin (account sticky-hash) | random | sticky (always first) # GROK2API_XAI_PROXY_STRATEGY=round_robin