# grokcli-2api — high-concurrency template # 1) cp .env.example .env # 2) docker compose up -d # starts redis + postgres + app # entrypoint auto-runs /app/bin/grok2api-migrate up (schema_migrations + SQL) # 3) Optional one-shot JSON→PG: scripts/upgrade_from_file_backend.sh # # 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 # MiniMax provider (set the API key to enable it). # MINIMAX_API_KEY= # MINIMAX_REGION=global_en # MINIMAX_OPENAI_BASE_URL=https://api.minimax.io/v1 # MINIMAX_ANTHROPIC_BASE_URL=https://api.minimax.io/anthropic # GROK2API_DEFAULT_MODEL=MiniMax-M3 # ── High concurrency (required) ─────────────────────── # auto = max(2, min(8, cpu_count)) when unset; compose default is 2 GROK2API_WORKERS=2 GROK2API_STORE_BACKEND=hybrid # ── Go runtime (default) + Python sidecars ─ # Production default: Go main process + Python sidecars (SSO / registration / captcha). # GROK2API_RUNTIME=go # GROK2API_GO_PUBLIC_READ=1 # GROK2API_GO_CHAT=1 # GROK2API_GO_MESSAGES=1 # GROK2API_GO_RESPONSES=1 # GROK2API_GO_ADMIN_READ=1 # GROK2API_GO_ADMIN_WRITE=1 # GROK2API_GO_MAINTAINER=1 # GROK2API_GO_WRITES=1 # GROK2API_GO_OWNERSHIP_MODE=all # Codex / multi-turn TTFT: # sticky-first = try affinity account alone before failover chain (default on) # GROK2API_STICKY_FIRST_ONLY=1 # Parallel first-byte probes after sticky miss (1-8, default 3). # GROK2API_FIRST_BYTE_PROBE_WORKERS=3 # Codex reasoning clamp (default on): fill missing effort with low for TTFT. # Explicit Codex modes (Low/Base/High/Ultra/Proactive) are honored → Grok low|medium|high. # Set 0 only if you want to disable the clamp entirely. # GROK2API_CODEX_FORCE_REASONING_LOW=1 # See docs/ARCHITECTURE_GO_PYTHON_BOUNDARY.md and docs/PYTHON_SIDECAR.md. # Python-only sidecar (registration / captcha / SSO scripts): # When GROK2API_RUNTIME=go, entrypoint starts: # 1) turnstile-solver on 127.0.0.1:5072 (captcha) # 2) scripts/registration_service.py on 127.0.0.1:18070 # - /internal/registration/v1/* registration machine # - /internal/sso/v1/* SSO conversion jobs # Go admin only proxies those surfaces; captcha/browser/email stay Python. # GROK2API_REGISTRATION_SIDECAR=1 # GROK2API_REGISTRATION_HOST=127.0.0.1 # GROK2API_REGISTRATION_PORT=18070 # GROK2API_REGISTRATION_SERVICE_URL=http://127.0.0.1:18070 # GROK2API_REGISTRATION_TOKEN= # GROK2API_REGISTRATION_READY_WAIT_SEC=30 # GROK2API_INLINE_SOLVER=1 # GROK2API_CAPTCHA_PROVIDER=local # GROK2API_LOCAL_SOLVER_URL=http://127.0.0.1:5072 # See docs/ARCHITECTURE_GO_PYTHON_BOUNDARY.md # See docs/PYTHON_SIDECAR.md # # --- 版本 / 容器内热更新 --- # 默认 docker:容器内 docker pull + compose force-recreate(需挂载 docker.sock + /compose) # GROK2API_HOT_UPDATE_MODE=docker # GROK2API_COMPOSE_DIR=/compose # GROK2API_DOCKER_SERVICE=grokcli-2api # GROK2API_GHCR_IMAGE=ghcr.io/hm2899/grokcli-2api # GROK2API_RELEASE_REPO=HM2899/grokcli-2api # GROK2API_GITHUB_TOKEN= # 自定义命令(优先于 docker 模式,可用 {{TAG}} {{IMAGE}}): # GROK2API_HOT_UPDATE_CMD=docker pull {{IMAGE}}:{{TAG}} && ... # 兼容旧宿主机 watcher:GROK2API_HOT_UPDATE_MODE=request_file 或 GROK2API_HOT_UPDATE_ALLOW_REQUEST_FILE=1 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 — IQ first). # Full tool history is preserved so the model does not re-Read / forget. # Enable only if huge sessions make upstream fail; soft-tier keeps head+tail. # GROK2API_HISTORY_COMPACT=1 GROK2API_HISTORY_COMPACT=0 # Auto-force compact above this messages JSON size. 0 = no global auto. # Codex / OpenAI-native agents still auto-compact at ~200k chars by default # (CodexDefaultAutoChars) so long tool loops don't blow context. Set explicitly # (e.g. 200000) to apply the same threshold to all clients. GROK2API_HISTORY_COMPACT_AUTO_CHARS=0 # If compact is ON: keep last N tool rounds nearly fully (default 32). GROK2API_HISTORY_KEEP_TOOL_ROUNDS=32 # 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=8 # 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 # OpenAI Responses native clients (Codex UA): unlimited tools by default. GROK2API_OUTBOUND_MAX_TOOLS_RESPONSES_NATIVE=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 # Tool-frame gap for Codex / OpenAI-native UA (default 0). GROK2API_OUTBOUND_TOOL_GAP_SEC_NATIVE=0 # 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 # Concurrent OIDC refresh workers (default 8). Each outcome writes last_renew_* to DB. # GROK2API_TOKEN_REFRESH_WORKERS=8 # Concurrent model probes (default 8). Manual full-pool waves may boost up to 16. # GROK2API_MODEL_PROBE_WORKERS=8 # Concurrent billing/quota fetch workers (default 8). Saves last_quota + pool_status. # GROK2API_QUOTA_WORKERS=8 # Base per-cycle batch; refresh list prioritizes near-expiry first. # GROK2API_TOKEN_REFRESH_BATCH=80 # GROK2API_MODEL_PROBE_BATCH=50 # 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=2 # Hard ceiling (seconds) for one background / manual probe cycle hold. # GROK2API_MODEL_PROBE_CYCLE_BUDGET=150 # GROK2API_MODEL_PROBE_MANUAL_BUDGET=150 # Manual full-pool multi-wave job (async by default). Each wave ~budget, up to max waves. # GROK2API_MODEL_PROBE_MANUAL_MAX_WAVES=40 # Admin "全部模型探测" starts async multi-wave job; poll GET /admin/api/model-health .job # Strict non-repeat sweep state lives in Redis (g2a:model_health:sweep:*). # Large-pool tips (token refresh is concurrent; probe is priority/strict sweep): # 1k~5k: TOKEN_INTERVAL=60 TOKEN_BATCH=80 TOKEN_WORKERS=8 PROBE_WORKERS=8 # 1w~2w: TOKEN_INTERVAL=45 TOKEN_BATCH=120 TOKEN_WORKERS=12 PROBE_WORKERS=12 # Quota/model/renew outcomes sync to PostgreSQL immediately (enabled/cooldown/ # quota_disabled/expired/model_blocked + last_quota/last_probe/last_renew_*). # 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 | cfmail | tempmail | cloudmail (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 # Cloud Mail (maillab/cloud-mail — self-hosted Worker origin + admin_email:password): # https://github.com/maillab/cloud-mail # GROK2API_MAIL_PROVIDER=cloudmail # GROK2API_CLOUDMAIL_BASE_URL=https://skymail.example.com # GROK2API_CLOUDMAIL_API_KEY=admin@example.com:your-admin-password # GROK2API_CLOUDMAIL_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 # default when form omits; UI may send 1-4 # GROK2API_REG_MAX_CONCURRENCY=4 # GROK2API_REG_LOCAL_CONCURRENCY=4 # local Camoufox hard cap # GROK2API_REG_GLOBAL_INFLIGHT=6 # GROK2API_REG_MIN_STAGGER_MS=0 # 0 = honor admin 错峰 ms # Cross-batch simultaneous registration cap (default 6). # GROK2API_REG_GLOBAL_INFLIGHT=6 # Durable registration batch/session Redis TTL (seconds). Default 72h so multi- # thousand bulk jobs do not lose progress after the old 6h expiry. # GROK2API_REG_TTL_SEC=259200 # Mid-run self-heal: refresh TTL + reclaim/resume dead runners without restart. # GROK2API_REG_WATCHDOG_SEC=25 # GROK2API_REG_WATCHDOG_STALE_SEC=90 # GROK2API_REG_WATCHDOG_RESUME_MAX=3 # Orphan reclaim grace for solving_turnstile / waiting_email / converting. # Local captcha is single-flight; bulk queues need a long grace (default 15m). # GROK2API_REG_CAPTCHA_STALE_SEC=420 # After create_account succeeds without SSO cookie, retry password login N times # with fresh turnstile + propagation delay (recovers most SSO_COOKIE_MISSING). # GROK2API_REG_SSO_LOGIN_ROUNDS=4 # GROK2API_REG_SSO_PROPAGATE_SEC=3.5 # 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 # MoeMail create retries under 429/5xx during bulk fan-out. # GROK2API_MOEMAIL_CREATE_RETRIES=4 # Device-flow rate-limit retries after SSO is obtained. # GROK2API_SSO_DEVICE_RETRIES=6 # GROK2API_SSO_DEVICE_GAP_SEC=1.2 # 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 # empty model output soft-block duration (seconds) → admin 模型封禁 # Default 240 (4m). Longer shrinks pool during empty storms; shorter may thrash. # GROK2API_EMPTY_OUTPUT_BLOCK_SEC=240