selfhost.directory

The index of self-hostable projects — ranked, categorized and served with live updates.
Own your data and stop giving it to big tech.

WindsurfAPI logo

Self-host WindsurfAPI

v3.141.592657

Windsurf/Devin API gateway with zero dependencies

ReleaseOpen SourceMIT
Updated7 hours ago
LanguageJavaScript
Setup difficultyEasy
Stars
3 108
Port
3003 (HTTP API server)
Docker
ghcr.io/dwgx/windsurf-api
Install with Docker Compose

What is WindsurfAPI?

WindsurfAPI is a zero-dependency Node.js server that converts Windsurf and Devin Cloud's AI models into standard API formats. It exposes four compatible protocols simultaneously - OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, and Google Gemini - allowing clients like Claude Code, Cursor, Cline to connect directly. The service maintains an account pool with automatic load balancing, rate limiting, and failover while translating requests through Windsurf's internal protocol stack.

Should you self-host WindsurfAPI?

WindsurfAPI scores 30/100 on selfhost.directory (how scoring works) and ranks #122 of 125 in AI. It has 3 108 GitHub stars.

It shipped 8 stable releases in the past year. The latest, v3.141.592657, came out on 26 October 2026.

An official Docker image is available. It is MIT-licensed and written mostly in JavaScript.

Its closest alternative on selfhost.directory is ComfyUI (93/100, 143 701 stars). It scores higher overall, so compare the two before you pick. See WindsurfAPI vs ComfyUI side by side.

Features

  • Account pool management
  • Claude Code integration

WindsurfAPI README

How to install WindsurfAPI

Install WindsurfAPI with Docker Compose

Environment variables
DEVIN_CONNECT, PORT, API_KEY, DASHBOARD_PASSWORD

git clone https://github.com/dwgx/WindsurfAPI.git cd WindsurfAPI cp .env.example .env node src/index.js

Zero npm dependencies

How to configure WindsurfAPI

  • ALLOW_PRIVATE_PROXY_HOSTS========== Security ========== Allow private/internal hosts (e.g., 192.168.x.x, 10.x.x.x, localhost) in proxy tests. Set to 1 for local deployments where you need to test proxies on private networks. Leave empty or set to 0 for public-facing deployments (default: only public hosts allowed).

    ========== Security ========== Allow private/internal hosts (e.g., 192.168.x.x, 10.x.x.x, localhost) in proxy tests. Set to 1 for local deployments where you need to test proxies on private networks. Leave empty or set to 0 for public-facing deployments (default: only public hosts allowed).

  • API_KEYChat/REST caller key. Leave empty only if you also set the opt-in above.

    Chat/REST caller key. Leave empty only if you also set the opt-in above.

  • ASTRAFLOW_API_KEY========== Astraflow (OpenAI-compatible, 200+ models — https://astraflow.ucloud-global.com) ========== Global endpoint (env: ASTRAFLOW_API_KEY) — base URL: https://api-us-ca.umodelverse.ai/v1

    ========== Astraflow (OpenAI-compatible, 200+ models — https://astraflow.ucloud-global.com) ========== Global endpoint (env: ASTRAFLOW_API_KEY) — base URL: https://api-us-ca.umodelverse.ai/v1

  • ASTRAFLOW_CN_API_KEYChina endpoint (env: ASTRAFLOW_CN_API_KEY) — base URL: https://api.modelverse.cn/v1

    China endpoint (env: ASTRAFLOW_CN_API_KEY) — base URL: https://api.modelverse.cn/v1

  • BIND_HOST---- 绑定地址 ---- BIND_HOST 是 HOST 的别名(HOST 优先)。打包版(exe)在两者都未设时会把 HOST 兜底成 127.0.0.1,以免首次运行就把无鉴权网关暴露到局域网 —— 而只设 BIND_HOST 会满足那个条件判断却不提供 loopback 值,绑定仍是 0.0.0.0。 想限制在本机请设 HOST,不要用 BIND_HOST。

    ---- 绑定地址 ---- BIND_HOST 是 HOST 的别名(HOST 优先)。打包版(exe)在两者都未设时会把 HOST 兜底成 127.0.0.1,以免首次运行就把无鉴权网关暴露到局域网 —— 而只设 BIND_HOST 会满足那个条件判断却不提供 loopback 值,绑定仍是 0.0.0.0。 想限制在本机请设 HOST,不要用 BIND_HOST。

  • CASCADE_COMPACT_CLAUDE_SYSTEMCascade 路径上把长 system prompt 压缩成事实清单(标题生成那类短指令除外)。 设 0 只做身份改写、不压缩。长 prompt 在 Cascade 上更容易触发上游限制,所以关掉它 之后看到的失败同样不会指向这个开关。

    Cascade 路径上把长 system prompt 压缩成事实清单(标题生成那类短指令除外)。 设 0 只做身份改写、不压缩。长 prompt 在 Cascade 上更容易触发上游限制,所以关掉它 之后看到的失败同样不会指向这个开关。

  • CASCADE_POOL_MAX500Max pool entries. Single-user setups can set to 1-5.

    Max pool entries. Single-user setups can set to 1-5.

  • CASCADE_REUSE_BY_CALLER0========== Cascade Reuse (single-user Claude Code optimization) ========== Caller-based fallback: when fingerprint misses, reuse the latest cascade for the same caller+model. Set to 1 for single-user Claude Code setups.

    ========== Cascade Reuse (single-user Claude Code optimization) ========== Caller-based fallback: when fingerprint misses, reuse the latest cascade for the same caller+model. Set to 1 for single-user Claude Code setups.

  • CASCADE_REUSE_HASH_SYSTEMHash system prompt into the reuse fingerprint. Default ON; set =0 to opt out (higher hit rate when Claude Code's system prompt drifts every turn, weaker isolation across different system prompts).

    Hash system prompt into the reuse fingerprint. Default ON; set =0 to opt out (higher hit rate when Claude Code's system prompt drifts every turn, weaker isolation across different system prompts).

  • CODEIUM_API_KEY========== Codeium Auth ========== Option 1: Direct API key from Windsurf

    ========== Codeium Auth ========== Option 1: Direct API key from Windsurf

  • CODEIUM_API_URLhttps://server.self-serve.windsurf.com========== Advanced ==========

    ========== Advanced ==========

  • CODEIUM_AUTH_TOKENOption 2: Token from windsurf.com/show-auth-token

    Option 2: Token from windsurf.com/show-auth-token

  • DASHBOARD_ALLOW_API_KEY_AS_PASSWORDLet the chat API_KEY double as the dashboard password on a verified-local client (single-secret convenience). Default OFF = the dashboard requires a real DASHBOARD_PASSWORD. A shared chat key granting full operator access is a footgun, and a reverse-proxy misconfig can make a remote client look local — so this is opt-in. If you enable it, also set TRUST_PROXY_X_FORWARDED_FOR=1 behind a proxy.

    Let the chat API_KEY double as the dashboard password on a verified-local client (single-secret convenience). Default OFF = the dashboard requires a real DASHBOARD_PASSWORD. A shared chat key granting full operator access is a footgun, and a reverse-proxy misconfig can make a remote client look local — so this is opt-in. If you enable it, also set TRUST_PROXY_X_FORWARDED_FOR=1 behind a proxy.

  • DASHBOARD_ALLOWED_HOSTSExtra Host header names accepted by the dashboard mutation gate, CSV. Loopback names (localhost/127.0.0.1/[::1]) are always accepted on a loopback bind; this is for a public dashboard hostname behind a reverse proxy whose Host header is not the bind address. DNS-rebinding defence: a loopback bind rejects every other Host unless listed here (or supplied as a trusted X-Forwarded-Host with TRUST_PROX

    Extra Host header names accepted by the dashboard mutation gate, CSV. Loopback names (localhost/127.0.0.1/[::1]) are always accepted on a loopback bind; this is for a public dashboard hostname behind a reverse proxy whose Host header is not the bind address. DNS-rebinding defence: a loopback bind rejects every other Host unless listed here (or supplied as a trusted X-Forwarded-Host with TRUST_PROX

  • DASHBOARD_ALLOW_HARD_RESETWhen =1, /dashboard/api/self-update may hard-reset (git reset --hard) even if it would drop unpushed local commits. Default: refuse and report. Opt-in only.

    When =1, /dashboard/api/self-update may hard-reset (git reset --hard) even if it would drop unpushed local commits. Default: refuse and report. Opt-in only.

  • DASHBOARD_ALLOW_NO_AUTHOpt back into the old open-on-localhost convenience (single-user dev only). When =1, a localhost bind with no secret configured is treated as authed. Discouraged; leave unset for fail-closed. Never enable on an internet bind.

    Opt back into the old open-on-localhost convenience (single-user dev only). When =1, a localhost bind with no secret configured is treated as authed. Discouraged; leave unset for fail-closed. Never enable on an internet bind.

  • DASHBOARD_CORS_ORIGINSCSV allowlist of origins reflected in Access-Control-Allow-Origin for the dashboard API. Unset = no cross-origin (same-origin only). Do NOT use "*".

    CSV allowlist of origins reflected in Access-Control-Allow-Origin for the dashboard API. Unset = no cross-origin (same-origin only). Do NOT use "*".

  • DASHBOARD_PASSWORD========== Dashboard ========== Dashboard password — protects /dashboard and all /dashboard/api/* endpoints. FAILS CLOSED by default: missing DASHBOARD_PASSWORD is 401 even on localhost. API_KEY does NOT double as the dashboard secret unless you also set DASHBOARD_ALLOW_API_KEY_AS_PASSWORD=1 (verified-local convenience, default OFF).

    ========== Dashboard ========== Dashboard password — protects /dashboard and all /dashboard/api/* endpoints. FAILS CLOSED by default: missing DASHBOARD_PASSWORD is 401 even on localhost. API_KEY does NOT double as the dashboard secret unless you also set DASHBOARD_ALLOW_API_KEY_AS_PASSWORD=1 (verified-local convenience, default OFF).

  • DATA_DIRDirectory for persisted JSON state and logs (Docker 推荐用 /data). Leave empty and it defaults to the repo/app root — set a writable path on bare-source installs so accounts.json / stats.json / logs don't scatter there.

    Directory for persisted JSON state and logs (Docker 推荐用 /data). Leave empty and it defaults to the repo/app root — set a writable path on bare-source installs so accounts.json / stats.json / logs don't scatter there.

  • DEFAULT_MODEL不传 model 时用哪个。默认(不设此项)是 claude-sonnet-4.6。 必须是 DEVIN_CONNECT 能解析的名字。旧 Cascade 别名 claude-4.5-sonnet-thinking 在 connect 上 mapped:false —— 默认 WINDSURFAPI_STRICT_MODEL=1 会 400 model_not_found;设 0 才静默降到 免费 selector swe-1-6-slow。改动后用 GET /v1/models 确认名字在列表里。

    不传 model 时用哪个。默认(不设此项)是 claude-sonnet-4.6。 必须是 DEVIN_CONNECT 能解析的名字。旧 Cascade 别名 claude-4.5-sonnet-thinking 在 connect 上 mapped:false —— 默认 WINDSURFAPI_STRICT_MODEL=1 会 400 model_not_found;设 0 才静默降到 免费 selector swe-1-6-slow。改动后用 GET /v1/models 确认名字在列表里。

  • DEVIN_ACP_CLIENT_NAMEACP initialize clientInfo.name. Default is WindsurfAPI. Set windsurf only when the CLI rejects a custom client name (team-settings 403 on enterprise).

    ACP initialize clientInfo.name. Default is WindsurfAPI. Set windsurf only when the CLI rejects a custom client name (team-settings 403 on enterprise).

  • DEVIN_ACP_CONFIG_OPTIONSOptional ACP native config (session/set_config_option); default off.

    Optional ACP native config (session/set_config_option); default off.

  • DEVIN_ACP_VISION---- ACP 视觉路径 ---- 带图片的请求改走 Devin CLI 的 ACP 通道,而不是上面的原生 Connect #10 直连路径。适合需要完整 Devin CLI 多模态会话语义的部署,需要本机装好 devin CLI。 注意:这条重路由会在 connect 分支的前置校验**之前**离开该分支。

    ---- ACP 视觉路径 ---- 带图片的请求改走 Devin CLI 的 ACP 通道,而不是上面的原生 Connect #10 直连路径。适合需要完整 Devin CLI 多模态会话语义的部署,需要本机装好 devin CLI。 注意:这条重路由会在 connect 分支的前置校验**之前**离开该分支。

  • DEVIN_BACKEND_TOKENDevin REST backend token for dashboard billing/usage reads (falls back to DEVIN_CONNECT_TOKEN, then WINDSURF_API_KEY).

    Devin REST backend token for dashboard billing/usage reads (falls back to DEVIN_CONNECT_TOKEN, then WINDSURF_API_KEY).

  • DEVIN_CLI_ALLOW_CLIENT_TOOLSPrint mode refuses caller-local tools/media by default; ACP bridging should be used before enabling these in production.

    Print mode refuses caller-local tools/media by default; ACP bridging should be used before enabling these in production.

  • DEVIN_CLI_ALLOW_MEDIA
  • DEVIN_CLI_ENABLED
  • DEVIN_CLI_MODE
  • DEVIN_CLI_PATH
  • DEVIN_CLI_USE_ACCOUNT_POOLPass the checked-out Windsurf account apiKey to Devin CLI as WINDSURF_API_KEY. Set to 0 only when the CLI has its own login/session and you understand that the HTTP server still expects a normal WindsurfAPI account pool.

    Pass the checked-out Windsurf account apiKey to Devin CLI as WINDSURF_API_KEY. Set to 0 only when the CLI has its own login/session and you understand that the HTTP server still expects a normal WindsurfAPI account pool.

  • DEVIN_CONNECT========== DEVIN_CONNECT (pure-HTTP Devin backend — POST-CASCADE SURVIVAL) ========== Cascade (the legacy LS-driven chat backend) is the 184-model default arm of the code (backend-router.js:150). DEVIN_CONNECT is the pure-HTTP alternative: it talks directly to the Devin cloud GetChatMessage endpoint using the pooled session tokens (accounts.json) — NO language_server binary and NO local `devin` CL

    ========== DEVIN_CONNECT (pure-HTTP Devin backend — POST-CASCADE SURVIVAL) ========== Cascade (the legacy LS-driven chat backend) is the 184-model default arm of the code (backend-router.js:150). DEVIN_CONNECT is the pure-HTTP alternative: it talks directly to the Devin cloud GetChatMessage endpoint using the pooled session tokens (accounts.json) — NO language_server binary and NO local `devin` CL

  • DEVIN_CONNECT_ACTUAL_MODEL_TAG--- Optional wire-tag decoders (frame-verified 2026-07-05; default off) --- actual_model_uid: report the concrete model that served a request (differs from the requested selector for router models). Frame-verified at metadata #7.9.

    --- Optional wire-tag decoders (frame-verified 2026-07-05; default off) --- actual_model_uid: report the concrete model that served a request (differs from the requested selector for router models). Frame-verified at metadata #7.9.

  • DEVIN_CONNECT_ASSIGN_MODEL--- Router models (adaptive / arena-*) — built, gated OFF, tags uncalibrated --- Resolve a router name to a concrete model via AssignModel before dispatch. Default off because the wire tags below are INFERRED from frame shape, not confirmed on a paid round-trip. See docs/DEVIN-CONNECT-CUTOVER.md §8.

    --- Router models (adaptive / arena-*) — built, gated OFF, tags uncalibrated --- Resolve a router name to a concrete model via AssignModel before dispatch. Default off because the wire tags below are INFERRED from frame shape, not confirmed on a paid round-trip. See docs/DEVIN-CONNECT-CUTOVER.md §8.

  • DEVIN_CONNECT_ASSIGN_TAGSTag overrides for that round-trip. Built-in defaults (post-86d5543, see src/devin-connect-catalog.js:521-527): req_model_uid=2, req_cascade_id=3, resp_assignment=1, asg_model_uid=2, asg_jwt=1, asg_harness=3. The pre-86d5543 set (asg_model_uid=1, asg_jwt=2, no cascade_id) is WRONG: it points model_uid at the JWT field and omits the required cascade_id.

    Tag overrides for that round-trip. Built-in defaults (post-86d5543, see src/devin-connect-catalog.js:521-527): req_model_uid=2, req_cascade_id=3, resp_assignment=1, asg_model_uid=2, asg_jwt=1, asg_harness=3. The pre-86d5543 set (asg_model_uid=1, asg_jwt=2, no cascade_id) is WRONG: it points model_uid at the JWT field and omits the required cascade_id.

  • DEVIN_CONNECT_AUTO_RELOGIN--- Auto-recovery (recommended for unattended prod) --- Session tokens are opaque session_ids with no expiry/refresh — when one dies the ONLY recovery is a fresh email/password Auth1 login. Enable auto re-login + an encrypted credential store so a dead token self-heals. Accounts added via the email/password login path auto-store their password in this store.

    --- Auto-recovery (recommended for unattended prod) --- Session tokens are opaque session_ids with no expiry/refresh — when one dies the ONLY recovery is a fresh email/password Auth1 login. Enable auto re-login + an encrypted credential store so a dead token self-heals. Accounts added via the email/password login path auto-store their password in this store.

  • DEVIN_CONNECT_BILLING_TAGSBilling / cache-usage decode (metadata #7 varints). DEFAULT: cache_read_tokens=5 — CONFIRMED 2026-07-23 on a paid teams account (miss dumped {2,3,6}, hit {2,3,5,6}; tag5=3840 + tag2=436 == 4276 == the miss request's prompt_tokens), so cached input shows up as prompt_tokens_details.cached_tokens instead of being billed as fresh in the dashboard (#220). Harmless on free accounts: the counter is zero

    Billing / cache-usage decode (metadata #7 varints). DEFAULT: cache_read_tokens=5 — CONFIRMED 2026-07-23 on a paid teams account (miss dumped {2,3,6}, hit {2,3,5,6}; tag5=3840 + tag2=436 == 4276 == the miss request's prompt_tokens), so cached input shows up as prompt_tokens_details.cached_tokens instead of being billed as fresh in the dashboard (#220). Harmless on free accounts: the counter is zero

  • DEVIN_CONNECT_CATALOG_TTL_MSSuccessful live model-catalog cache TTL, per account. Default 300000 (5 min), minimum 10000. Empty/failed refreshes keep the last-known-good catalog and do not advance the success timestamp.

    Successful live model-catalog cache TTL, per account. Default 300000 (5 min), minimum 10000. Empty/failed refreshes keep the last-known-good catalog and do not advance the success timestamp.

  • DEVIN_CONNECT_COLLAPSE_SYSTEMCollapse system-role content into the next user turn as a <system> block. Default off. This avoids the stricter upstream content-policy path on request field #2 while preserving message order. With native tools and no remaining field-#2 content, the existing benign tools placeholder is still emitted.

    Collapse system-role content into the next user turn as a <system> block. Default off. This avoids the stricter upstream content-policy path on request field #2 while preserving message order. With native tools and no remaining field-#2 content, the existing benign tools placeholder is still emitted.

  • DEVIN_CONNECT_CRED_FILEDEVIN_CONNECT_CRED_KEY=<32+ char secret — required for auto-relogin to work>

    DEVIN_CONNECT_CRED_KEY=<32+ char secret — required for auto-relogin to work>

  • DEVIN_CONNECT_DEBUG_META--- Diagnostics (verbose; none of these are for steady-state production) --- Dump the metadata varint map of every response. This is the tool the cutover runbook points at for calibrating billing / ACU tags on a PAID token (#239).

    --- Diagnostics (verbose; none of these are for steady-state production) --- Dump the metadata varint map of every response. This is the tool the cutover runbook points at for calibrating billing / ACU tags on a PAID token (#239).

  • DEVIN_CONNECT_DUMP_RAW
  • DEVIN_CONNECT_EAGER_PRIMESend the role-priming chunk eagerly (pre-2.0.146 behavior). Default defers it until the first real delta so first-connect transient errors keep recovery armed.

    Send the role-priming chunk eagerly (pre-2.0.146 behavior). Default defers it until the first real delta so first-connect transient errors keep recovery armed.

  • DEVIN_CONNECT_FAILOVER_MAXCross-account failover: max pooled-account hops after a dead session token. Default 2; set 0 to disable failover (same-account re-login still applies).

    Cross-account failover: max pooled-account hops after a dead session token. Default 2; set 0 to disable failover (same-account re-login still applies).

  • DEVIN_CONNECT_IDLE_TIMEOUT_MS--- Timeouts --- Idle (no-activity) socket timeout — resets on every byte. Default 120000.

    --- Timeouts --- Idle (no-activity) socket timeout — resets on every byte. Default 120000.

  • DEVIN_CONNECT_IMAGE_INNER_TAGSOverride the inner ImageData field tags. POSITIONAL, exactly `base64,mime` (anything else is ignored and the default kept). Default `1,2` matches the schema, the native SWE-1.7 capture, and the Cascade path. Only change this against a fresh capture showing an upstream wire migration.

    Override the inner ImageData field tags. POSITIONAL, exactly `base64,mime` (anything else is ignored and the default kept). Default `1,2` matches the schema, the native SWE-1.7 capture, and the Cascade path. Only change this against a fresh capture showing an upstream wire migration.

  • DEVIN_CONNECT_IMAGE_TAG--- Vision / images over DEVIN_CONNECT (native wire; default tag = 10) --- Field carrying repeated images inside each ChatMessage. The extracted Devin schema and a native SWE-1.7 request/response independently confirm #10, with ImageData {base64_data #1, mime_type #2}. Images stay on their original source message; no synthetic read tool call/tool result/ToolDef is invented. Unset = 10 (enabled). S

    --- Vision / images over DEVIN_CONNECT (native wire; default tag = 10) --- Field carrying repeated images inside each ChatMessage. The extracted Devin schema and a native SWE-1.7 request/response independently confirm #10, with ImageData {base64_data #1, mime_type #2}. Images stay on their original source message; no synthetic read tool call/tool result/ToolDef is invented. Unset = 10 (enabled). S

  • DEVIN_CONNECT_LIVENESS_INTERVAL_MS
  • DEVIN_CONNECT_LIVENESS_PROBE--- Liveness probe (recommended) --- Zero-billable GetUserStatus sweep that detects + pre-emptively recovers dead tokens before a real request hits them. Off by default.

    --- Liveness probe (recommended) --- Zero-billable GetUserStatus sweep that detects + pre-emptively recovers dead tokens before a real request hits them. Off by default.

  • DEVIN_CONNECT_MAX_ERROR_BODY_BYTESCeiling on a non-200 upstream error body, in bytes. Default 8MiB. 这是**安全上限,不是调优旋钮**:错误体只被 classifyUpstreamError 读一个 code、 一条 message 和一个可选 reset 窗口,body 再大也只是诊断信息。而它跑在 stream 的 'end' 监听器里 —— 那里抛出的异常没人接,会变成 uncaughtException 直接结束进程, 所以一个坏上游就能把所有租户一起断线(同类防护见 grpc.js 的 decodeURIComponent)。 超过上限会截断并追加可见标记,不静默丢弃。设成非正整数会**回落**到默认值, 而不是变成"无上限" —— 无上限正是这个 bug。

    Ceiling on a non-200 upstream error body, in bytes. Default 8MiB. 这是**安全上限,不是调优旋钮**:错误体只被 classifyUpstreamError 读一个 code、 一条 message 和一个可选 reset 窗口,body 再大也只是诊断信息。而它跑在 stream 的 'end' 监听器里 —— 那里抛出的异常没人接,会变成 uncaughtException 直接结束进程, 所以一个坏上游就能把所有租户一起断线(同类防护见 grpc.js 的 decodeURIComponent)。 超过上限会截断并追加可见标记,不静默丢弃。设成非正整数会**回落**到默认值, 而不是变成"无上限" —— 无上限正是这个 bug。

  • DEVIN_CONNECT_MAX_INFLIGHT_PER_ACCOUNTOptional hard cap on a single account's concurrent in-flight requests. Default 0 = OFF (selection unchanged). When set to N > 0, account selection skips any account already at N in-flight requests (sticky bindings included), so a burst spreads out or 429s instead of stacking on one account. A leaked counter cannot pin an account out: the stale-inflight sweep reclaims it after the deadline-derived

    Optional hard cap on a single account's concurrent in-flight requests. Default 0 = OFF (selection unchanged). When set to N > 0, account selection skips any account already at N in-flight requests (sticky bindings included), so a burst spreads out or 429s instead of stacking on one account. A leaked counter cannot pin an account out: the stale-inflight sweep reclaims it after the deadline-derived

  • DEVIN_CONNECT_MAX_QUEUE_BYTESByte cap for the same queue (estimated text*2 + hex + 96 bytes/event). Default 8MiB. Non-positive values fall back to the default, never "no cap".

    Byte cap for the same queue (estimated text*2 + hex + 96 bytes/event). Default 8MiB. Non-positive values fall back to the default, never "no cap".

  • DEVIN_CONNECT_MAX_QUEUE_FRAMES--- Bounded queues (slow consumer / hung re-login holder) --- Decoded frames buffered between res.on('data') and the stream consumer. At half the cap the socket is paused (TCP backpressure); crossing it fails the stream with QUEUE_OVERFLOW instead of growing without bound. Default 4096.

    --- Bounded queues (slow consumer / hung re-login holder) --- Decoded frames buffered between res.on('data') and the stream consumer. At half the cap the socket is paused (TCP backpressure); crossing it fails the stream with QUEUE_OVERFLOW instead of growing without bound. Default 4096.

  • DEVIN_CONNECT_MODEL_CONFIG_STABLEStable ModelConfig (#15.1/#15.2) keyed on the session from DEVIN_CONNECT_SESSION_REUSE. Genuine devin.exe keeps #15.1 constant within a session and increments #15.2 per turn (calibrated on capture 9501aa2c). Requires DEVIN_CONNECT_SESSION_REUSE. Default 0 (stateless: fresh uuid + turn 1, byte-identical to pre-feature wire).

    Stable ModelConfig (#15.1/#15.2) keyed on the session from DEVIN_CONNECT_SESSION_REUSE. Genuine devin.exe keeps #15.1 constant within a session and increments #15.2 per turn (calibrated on capture 9501aa2c). Requires DEVIN_CONNECT_SESSION_REUSE. Default 0 (stateless: fresh uuid + turn 1, byte-identical to pre-feature wire).

  • DEVIN_CONNECT_PROSE_INTENT_DETECTLog-only detector for the #236 "form A" stall: a model NARRATES a tool call in visible text, finishes with stop and zero tool calls, and the client waits for a call that never comes. It matters most for reasoning-effort `-none` selectors (glm-5-2-none and siblings), which emit no reasoning at all and therefore can never satisfy the rescue gate above. Nothing is retried or rewritten — the shape can

    Log-only detector for the #236 "form A" stall: a model NARRATES a tool call in visible text, finishes with stop and zero tool calls, and the client waits for a call that never comes. It matters most for reasoning-effort `-none` selectors (glm-5-2-none and siblings), which emit no reasoning at all and therefore can never satisfy the rescue gate above. Nothing is retried or rewritten — the shape can

  • DEVIN_CONNECT_RELOGIN_QUEUE_TIMEOUT_MSMax time a re-login attempt may wait for a global login slot before returning false. Default 60000. The login itself, once it holds a slot, is not capped.

    Max time a re-login attempt may wait for a global login slot before returning false. Default 60000. The login itself, once it holds a slot, is not capped.

  • DEVIN_CONNECT_REPLAY_REASONINGDebug/probe only — replay assistant reasoning back upstream as a native reasoning tag on request build. '1' = write as tag #11 (the genuine-history candidate), '9' = write as tag #9 (negative control; #9 is the inbound tag). Unset = byte-identical traffic (default). Live matrix (2026-08) showed upstream ACCEPTS but does NOT CONSUME either field; kept for future re-probing.

    Debug/probe only — replay assistant reasoning back upstream as a native reasoning tag on request build. '1' = write as tag #11 (the genuine-history candidate), '9' = write as tag #9 (negative control; #9 is the inbound tag). Unset = byte-identical traffic (default). Live matrix (2026-08) showed upstream ACCEPTS but does NOT CONSUME either field; kept for future re-probing.

  • DEVIN_CONNECT_RESCUE_MAXMax thinking-only rescues. swe-1-7 (Kimi K2 fine-tune) intermittently spends a whole turn in reasoning declaring tool intent without emitting the call; a corrective nudge forces emission (24/24 on a live probe). Default 2, hard ceiling 5 — values above it are clamped, and non-numeric input falls back to the default rather than silently disabling the feature. Set 0 to disable just the rescue, leavi

    Max thinking-only rescues. swe-1-7 (Kimi K2 fine-tune) intermittently spends a whole turn in reasoning declaring tool intent without emitting the call; a corrective nudge forces emission (24/24 on a live probe). Default 2, hard ceiling 5 — values above it are clamped, and non-numeric input falls back to the default rather than silently disabling the feature. Set 0 to disable just the rescue, leavi

  • DEVIN_CONNECT_RESCUE_REASONING_MAX_CHARSThinking-only rescue digest: when a turn finishes reasoning-only (swe-1-7 intermittently declares tool intent without emitting the call), the corrective nudge quotes the END of that reasoning so the model resumes where it stopped. Caps how many trailing reasoning chars are kept for the digest. Default 2000; 0 = plain nudge without any reasoning quote. Hard ceiling 32000 — values above it are clamp

    Thinking-only rescue digest: when a turn finishes reasoning-only (swe-1-7 intermittently declares tool intent without emitting the call), the corrective nudge quotes the END of that reasoning so the model resumes where it stopped. Caps how many trailing reasoning chars are kept for the digest. Default 2000; 0 = plain nudge without any reasoning quote. Hard ceiling 32000 — values above it are clamp

  • DEVIN_CONNECT_RETRY_ON_EMPTY--- Speculative re-issues: empty completions and thinking-only turns --- These decide how often the proxy re-sends a request the upstream already answered, so they cost real tokens on a real account. Worst case per client request is 1 + RETRY_ON_EMPTY_MAX + RESCUE_MAX upstream calls, and that ceiling IS the account protection: a paid E2E (2026-07-08, 27/27) showed the weak (fable) family returns D

    --- Speculative re-issues: empty completions and thinking-only turns --- These decide how often the proxy re-sends a request the upstream already answered, so they cost real tokens on a real account. Worst case per client request is 1 + RETRY_ON_EMPTY_MAX + RESCUE_MAX upstream calls, and that ceiling IS the account protection: a paid E2E (2026-07-08, 27/27) showed the weak (fable) family returns D

  • DEVIN_CONNECT_RETRY_ON_EMPTY_MAXMax retries for a turn that produced ZERO output (no content, no reasoning, no tool call) with a clean stop. Default 2.

    Max retries for a turn that produced ZERO output (no content, no reasoning, no tool call) with a clean stop. Default 2.

  • DEVIN_CONNECT_RETRY_ON_EMPTY_MSBackoff base; the Nth retry waits base×N. Default 350.

    Backoff base; the Nth retry waits base×N. Default 350.

  • DEVIN_CONNECT_ROUTER_MODELSExtra names/prefixes to treat as router models, beyond the built-in list.

    Extra names/prefixes to treat as router models, beyond the built-in list.

  • DEVIN_CONNECT_SESSION_MAX_STATESMax tracked dialogs (LRU-evicted). Default 500.

    Max tracked dialogs (LRU-evicted). Default 500.

  • DEVIN_CONNECT_SESSION_REASONING_COUNTQueue length in turns kept per session (capped 32).

    Queue length in turns kept per session (capped 32).

  • DEVIN_CONNECT_SESSION_REASONING_INJECT--- Reasoning continuity (Thinking-core T1) --- Keep per-session reasoning tails and re-inject them as a system-prompt checkpoint on the next turn (text channel; A3 matrix proved #11/#9 are accepted but not consumed upstream). Requires DEVIN_CONNECT_SESSION_REUSE. Injection gate, default off (store still fills when MAX_CHARS > 0).

    --- Reasoning continuity (Thinking-core T1) --- Keep per-session reasoning tails and re-inject them as a system-prompt checkpoint on the next turn (text channel; A3 matrix proved #11/#9 are accepted but not consumed upstream). Requires DEVIN_CONNECT_SESSION_REUSE. Injection gate, default off (store still fills when MAX_CHARS > 0).

  • DEVIN_CONNECT_SESSION_REASONING_MAX_CHARSTotal injection budget AND per-turn digest cap (tail kept). 0 disables. Clamped at 32000 (ceiling) so a runaway value cannot bloat the system prompt.

    Total injection budget AND per-turn digest cap (tail kept). 0 disables. Clamped at 32000 (ceiling) so a runaway value cannot bloat the system prompt.

  • DEVIN_CONNECT_SESSION_REUSE--- Session continuity (experimental, opt-in, default OFF) --- By default every DEVIN_CONNECT call mints a fresh protobuf session_id (#16), so the upstream velocity limiter sees each turn of one dialog as a brand-new session. With reuse ON, a stable session_id is derived from the conversation's own completed request/response pair chain (never from requestId/IP/timestamp), so a multi-turn dialog ri

    --- Session continuity (experimental, opt-in, default OFF) --- By default every DEVIN_CONNECT call mints a fresh protobuf session_id (#16), so the upstream velocity limiter sees each turn of one dialog as a brand-new session. With reuse ON, a stable session_id is derived from the conversation's own completed request/response pair chain (never from requestId/IP/timestamp), so a multi-turn dialog ri

  • DEVIN_CONNECT_SESSION_TTL_MSState TTL before a dormant dialog's session_id is forgotten. Default 1800000 (30 min).

    State TTL before a dormant dialog's session_id is forgotten. Default 1800000 (30 min).

  • DEVIN_CONNECT_STOP_REASON_MAP--- Upstream finish-reason map (overrides the built-in 0-13 table) --- The upstream sends finish as an enum varint. Built-in map (src/devin-connect.js: 2884-2899): 0=stop 1=length 2=stop 3=length 4=stop 5=length 6=stop 7=stop 8=stop 9=length 10=tool_calls 11=content_filter 12=stop 13=error. Only values OUTSIDE 0-13 fall back to a clean stop. Format: `<varint>=<openai reason>` where the reason is o

    --- Upstream finish-reason map (overrides the built-in 0-13 table) --- The upstream sends finish as an enum varint. Built-in map (src/devin-connect.js: 2884-2899): 0=stop 1=length 2=stop 3=length 4=stop 5=length 6=stop 7=stop 8=stop 9=length 10=tool_calls 11=content_filter 12=stop 13=error. Only values OUTSIDE 0-13 fall back to a clean stop. Format: `<varint>=<openai reason>` where the reason is o

  • DEVIN_CONNECT_THINKTEXT_REROUTE--- Think-text reroute (Thinking-core item 1, loop break) --- When the model emits a LEADING think-tagged span on the CONTENT channel, reroute it to the thinking channel so clients do not store it as visible assistant text and resend it into a self-reinforcing loop. Anthropic Messages egress. Default 0 (off).

    --- Think-text reroute (Thinking-core item 1, loop break) --- When the model emits a LEADING think-tagged span on the CONTENT channel, reroute it to the thinking channel so clients do not store it as visible assistant text and resend it into a self-reinforcing loop. Anthropic Messages egress. Default 0 (off).

  • DEVIN_CONNECT_TIMEOUT_MSAbsolute wall-clock cap from request start — the real backstop against a hung upstream that trickles bytes forever and defeats the idle timer. Default 600000 (10 min). Lower it for tighter SLAs.

    Absolute wall-clock cap from request start — the real backstop against a hung upstream that trickles bytes forever and defeats the idle timer. Default 600000 (10 min). Lower it for tighter SLAs.

  • DEVIN_CONNECT_TOKENSingle-token deploy (no account pool): provide the session token in env.

    Single-token deploy (no account pool): provide the session token in env.

  • DEVIN_CONNECT_TOOL_CALL_TAGSNative tool_call decode (repeated ChatToolCall). Off = prompt emulation owns tool calls. Only set once calibrated against a paid tool-using capture.

    Native tool_call decode (repeated ChatToolCall). Off = prompt emulation owns tool calls. Only set once calibrated against a paid tool-using capture.

  • DEVIN_CONNECT_TOOL_CHOICEtool_choice (#12) + disable_parallel_tool_calls (#11) passthrough. Both tags are guessed from a third-party .proto DECLARATION ORDER, not a wire capture, so this stays off. While off, `parallel_tool_calls:false` is still SERVED — never a 400 — but not honoured: the model may return several calls in one turn. Each such request is counted as parallel_tool_calls_hint_ignored on /dashboard/api/connect

    tool_choice (#12) + disable_parallel_tool_calls (#11) passthrough. Both tags are guessed from a third-party .proto DECLARATION ORDER, not a wire capture, so this stays off. While off, `parallel_tool_calls:false` is still SERVED — never a 400 — but not honoured: the model may return several calls in one turn. Each such request is counted as parallel_tool_calls_hint_ignored on /dashboard/api/connect

  • DEVIN_CONNECT_TOOL_CHOICE_TAGSOverride those two tags: choice=N,parallel=M. A value colliding with a tag the request already uses is skipped with a warning (never a fatal config error).

    Override those two tags: choice=N,parallel=M. A value colliding with a tag the request already uses is skipped with a warning (never a fatal config error).

  • DEVIN_CONNECT_TOOL_DEF_SOLOProbe: send ToolDefs WITHOUT the emulation preamble, to test whether upstream honours them alone. Diagnostic only — leave unset in production.

    Probe: send ToolDefs WITHOUT the emulation preamble, to test whether upstream honours them alone. Diagnostic only — leave unset in production.

  • DEVIN_CONNECT_TOOL_DEF_TAGS--- Native tool definitions on the connect path (calibration surface) --- Field tags for sending ToolDefs natively instead of via prompt emulation. Unset → the built-in default map when native tool mode is on, disabled otherwise. Accepted keys: outer, name, description, parameters (alias: schema), strict — unknown keys are ignored rather than failing the whole map. A bare positional form is back-c

    --- Native tool definitions on the connect path (calibration surface) --- Field tags for sending ToolDefs natively instead of via prompt emulation. Unset → the built-in default map when native tool mode is on, disabled otherwise. Accepted keys: outer, name, description, parameters (alias: schema), strict — unknown keys are ignored rather than failing the whole map. A bare positional form is back-c

  • DEVIN_CONNECT_WIRE_DUMPWrite raw request/response protobuf bytes to disk for offline RE. WIRE_DUMP needs a writable directory; DUMP_RAW logs raw frames inline. Both are noisy and can capture request content — do not enable on a shared deployment.

    Write raw request/response protobuf bytes to disk for offline RE. WIRE_DUMP needs a writable directory; DUMP_RAW logs raw frames inline. Both are noisy and can capture request content — do not enable on a shared deployment.

  • DEVIN_CONNECT_WIRE_DUMP_DIR
  • DEVIN_MAX_PROCS
  • DEVIN_QUEUE_TIMEOUT_MS
  • DEVIN_TIMEOUT_MS
  • DROUGHT_RESTRICT_PREMIUM---- 干旱模式(低配额保护)---- 所有活跃账号本周配额都低于阈值时,对 premium 模型直接返 503,而不是让请求 一路打到上游 rate-limit 把最后一点配额烧掉。**默认开启**。 免费层模型不受限制 —— 注意这份免费白名单按后端解析:Cascade 上是静态免费 模型表,DEVIN_CONNECT 上是**实时推导**的免费集合(账号目录里的 modelCostTier=FREE + promoStatus,其次费率表 rate===0;两者都无数据时才回退 到 FREE_REACHABLE_SELECTORS 的静态兜底)。另有 DROUGHT_SAFE_SELECTORS (swe-1-7 / swe-1-7-medium / glm-5-2):付费号旱灾时仍放行,因为它们不吃周配额。 那份 Set 不是免费账号 entitlement,不要和 FREE_R

    ---- 干旱模式(低配额保护)---- 所有活跃账号本周配额都低于阈值时,对 premium 模型直接返 503,而不是让请求 一路打到上游 rate-limit 把最后一点配额烧掉。**默认开启**。 免费层模型不受限制 —— 注意这份免费白名单按后端解析:Cascade 上是静态免费 模型表,DEVIN_CONNECT 上是**实时推导**的免费集合(账号目录里的 modelCostTier=FREE + promoStatus,其次费率表 rate===0;两者都无数据时才回退 到 FREE_REACHABLE_SELECTORS 的静态兜底)。另有 DROUGHT_SAFE_SELECTORS (swe-1-7 / swe-1-7-medium / glm-5-2):付费号旱灾时仍放行,因为它们不吃周配额。 那份 Set 不是免费账号 entitlement,不要和 FREE_R

  • GATE_INERT_SKIP_PATHS---- 门禁:声明本机"整文件跳过"的测试(默认一个都不接受)---- 一个测试文件如果声明了测试却一个都没真跑,`npm run gate` 会 FAIL。 两种形态都会被点名:整文件跳过(声明 N 个测试,全被 skip 计入总数), 以及 describe 层整段跳过(实测 tests 0 / suites 1 / skipped 0)。 门禁过去只读总数,于是根本没跑的套件可以拿到绿灯;现在会点名这些文件。 import 期就抛的模块**不在此列**:它让文件直接 FAIL(tests 1 / fail 1 / exit 1, node v24.21.0 实测),fail 计数本来就抓得到。 **部分跳过是合法的**:real-Git fixture 在任何没有"受信任绝对 POSIX 路径 git"的主机上 按设计跳过(test/git-fixture-env.js)。需要显式

    ---- 门禁:声明本机"整文件跳过"的测试(默认一个都不接受)---- 一个测试文件如果声明了测试却一个都没真跑,`npm run gate` 会 FAIL。 两种形态都会被点名:整文件跳过(声明 N 个测试,全被 skip 计入总数), 以及 describe 层整段跳过(实测 tests 0 / suites 1 / skipped 0)。 门禁过去只读总数,于是根本没跑的套件可以拿到绿灯;现在会点名这些文件。 import 期就抛的模块**不在此列**:它让文件直接 FAIL(tests 1 / fail 1 / exit 1, node v24.21.0 实测),fail 计数本来就抓得到。 **部分跳过是合法的**:real-Git fixture 在任何没有"受信任绝对 POSIX 路径 git"的主机上 按设计跳过(test/git-fixture-env.js)。需要显式

  • GRPC_PROTOCOLgRPC 传输协议;'connect' 走 Connect 协议,默认走 gRPC。仅影响 Cascade 路径。

    gRPC 传输协议;'connect' 走 Connect 协议,默认走 gRPC。仅影响 Cascade 路径。

  • HOSTBind host. Default 0.0.0.0 so Docker/LAN work; set 127.0.0.1 for loopback. Empty API_KEY is FAIL-CLOSED (401) even on a local bind — "local bind" is not "no proxy". A non-local bind also needs DASHBOARD_PASSWORD for panel writes. Open chat access is opt-in only: WINDSURFAPI_ALLOW_UNAUTHENTICATED=1 AND a local bind. Windows exe auto-generates a key on first run so it never needs that flag.

    Bind host. Default 0.0.0.0 so Docker/LAN work; set 127.0.0.1 for loopback. Empty API_KEY is FAIL-CLOSED (401) even on a local bind — "local bind" is not "no proxy". A non-local bind also needs DASHBOARD_PASSWORD for panel writes. Open chat access is opt-in only: WINDSURFAPI_ALLOW_UNAUTHENTICATED=1 AND a local bind. Windows exe auto-generates a key on first run so it never needs that flag.

  • LOCKOUT_MAX_ENTRIES---- 登录失败锁定表上限 ---- 暴力破解防护的锁定表最多记多少个来源。上限存在的意义是防止伪造 XFF 的请求 把这张表撑成内存放大器。

    ---- 登录失败锁定表上限 ---- 暴力破解防护的锁定表最多记多少个来源。上限存在的意义是防止伪造 XFF 的请求 把这张表撑成内存放大器。

  • LOG_LEVELinfo
  • LOG_MAX_FILE_MB
  • LOG_MAX_TOTAL_MB
  • LOG_RETENTION_DAYSApplication JSONL log limits (opt-in; restart after changes). All default to 0/off. Example: keep 14 UTC calendar days, rotate at 20 MiB, retain at most 500 MiB total. Only manages DATA_DIR/logs/{app,error}-YYYY-MM-DD[-NNNNNN].jsonl, single writer per dir. Does not manage console/Docker/PM2 logs, traces or wire dumps. LOG_LEVEL is console-only.

    Application JSONL log limits (opt-in; restart after changes). All default to 0/off. Example: keep 14 UTC calendar days, rotate at 20 MiB, retain at most 500 MiB total. Only manages DATA_DIR/logs/{app,error}-YYYY-MM-DD[-NNNNNN].jsonl, single writer per dir. Does not manage console/Docker/PM2 logs, traces or wire dumps. LOG_LEVEL is console-only.

  • LS_AUTO_RESTART---- Language Server 自动重启 ---- LS 进程异常退出后是否自动重启,以及重试次数与首次退避。仅 Cascade 路径用到 LS;DEVIN_CONNECT 是纯 HTTP 出口,不需要 LS。

    ---- Language Server 自动重启 ---- LS 进程异常退出后是否自动重启,以及重试次数与首次退避。仅 Cascade 路径用到 LS;DEVIN_CONNECT 是纯 HTTP 出口,不需要 LS。

  • LS_AUTO_RESTART_BASE_DELAY_MS
  • LS_AUTO_RESTART_MAX_RETRIES
  • LS_BINARY_PATH/opt/windsurf/language_server_linux_x64========== Language Server ========== Path to the language server binary. Linux x64 default: /opt/windsurf/language_server_linux_x64 Linux arm64 default: /opt/windsurf/language_server_linux_arm macOS Apple Silicon default: ~/.windsurf/language_server_macos_arm macOS Intel default: ~/.windsurf/language_server_macos_x64

    ========== Language Server ========== Path to the language server binary. Linux x64 default: /opt/windsurf/language_server_linux_x64 Linux arm64 default: /opt/windsurf/language_server_linux_arm macOS Apple Silicon default: ~/.windsurf/language_server_macos_arm macOS Intel default: ~/.windsurf/language_server_macos_x64

  • LS_DATA_DIR/opt/windsurf/dataPer-proxy language server data root. macOS local runs should use a user-writable directory, e.g. ~/.windsurf/data.

    Per-proxy language server data root. macOS local runs should use a user-writable directory, e.g. ~/.windsurf/data.

  • LS_IDLE_SWEEP_MSIdle reaper interval. Empty = derived from LS_IDLE_TTL_MS.

    Idle reaper interval. Empty = derived from LS_IDLE_TTL_MS.

  • LS_IDLE_TTL_MSStop idle non-default LS instances after this many milliseconds. 0 disables.

    Stop idle non-default LS instances after this many milliseconds. 0 disables.

  • LS_MAX_INSTANCESMax live LS instances. Empty = memory-aware default (about 700MB per LS). Set low on small VPSes, especially when using per-proxy/per-user LS isolation.

    Max live LS instances. Empty = memory-aware default (about 700MB per LS). Set low on small VPSes, especially when using per-proxy/per-user LS isolation.

  • LS_MEMORY_GUARDDisable memory guard only on hosts with external memory control/monitoring.

    Disable memory guard only on hosts with external memory control/monitoring.

  • LS_OBSERVED_RSS_MARGIN_PERCENT
  • LS_OBSERVED_RSS_MIN_BYTESDynamic RSS estimate floor/margin used only when LS_SPAWN_MIN_AVAILABLE_BYTES is unset.

    Dynamic RSS estimate floor/margin used only when LS_SPAWN_MIN_AVAILABLE_BYTES is unset.

  • LS_POOL_WAIT_MSHow long a request waits for an active non-default LS to go idle before returning LS_POOL_EXHAUSTED / LS_MEMORY_GUARD.

    How long a request waits for an active non-default LS to go idle before returning LS_POOL_EXHAUSTED / LS_MEMORY_GUARD.

  • LS_PORT42100gRPC port for language server

    gRPC port for language server

  • LS_PREWARM_DEFAULTPrewarm the no-proxy default LS on startup. Set 0 on low-memory hosts when all real traffic uses per-account proxies; LS will start lazily on demand.

    Prewarm the no-proxy default LS on startup. Set 0 on low-memory hosts when all real traffic uses per-account proxies; LS will start lazily on demand.

  • LS_PREWARM_ON_ACCOUNT_ADDAlso prewarm/probe LS when adding accounts from Dashboard/batch/OAuth. Default off so bulk account import cannot spawn many heavy LSPs at once.

    Also prewarm/probe LS when adding accounts from Dashboard/batch/OAuth. Default off so bulk account import cannot spawn many heavy LSPs at once.

  • LS_PREWARM_PROXIESPrewarm every configured proxy LS on startup. Default off to avoid memory spikes.

    Prewarm every configured proxy LS on startup. Default off to avoid memory spikes.

  • LS_SPAWN_MIN_AVAILABLE_BYTESMinimum available memory required before spawning another non-default LS. If unset, WindsurfAPI uses observed live LS RSS + margin after the first LS is ready, falling back to 700MB before any sample exists. Set this to pin a fixed operator budget. Accepts 700m / 1g / bytes.

    Minimum available memory required before spawning another non-default LS. If unset, WindsurfAPI uses observed live LS RSS + margin after the first LS is ready, falling back to 700MB before any sample exists. Set this to pin a fixed operator budget. Accepts 700m / 1g / bytes.

  • MAX_TOKENS
  • PORT3003========== Server ==========

    ========== Server ==========

  • RESPONSE_CACHE_ENABLEDIn-memory duplicate response cache. Max bytes accepts b/k/kb/kib/m/mb/mib/g/gb/gib.

    In-memory duplicate response cache. Max bytes accepts b/k/kb/kib/m/mb/mib/g/gb/gib.

  • RESPONSE_CACHE_MAX_BYTES
  • RESPONSE_STORE_ENABLED========== Responses API server-side state ========== The Responses API is built around the server holding the conversation: a client sends only the new turn plus previous_response_id. On by default. Turn it off and requests carrying previous_response_id get a 400 telling them to send the full `input` array instead (never a silent context reset). Conversations are scoped by callerKey, so one tenan

    ========== Responses API server-side state ========== The Responses API is built around the server holding the conversation: a client sends only the new turn plus previous_response_id. On by default. Turn it off and requests carrying previous_response_id get a 400 telling them to send the full `input` array instead (never a silent context reset). Conversations are scoped by callerKey, so one tenan

  • RESPONSE_STORE_MAX
  • RESPONSE_STORE_MAX_AGE_MS--- Response-store absolute retention bound --- RESPONSE_STORE_TTL_MS above is an IDLE timeout: it measures time since the last access and every successful GET refreshes it, so a polling client could keep one entry alive forever. This is the bound that does not depend on read traffic — measured from the entry's creation and never refreshed. Default 86400000 (24h). Chosen well above a long agent se

    --- Response-store absolute retention bound --- RESPONSE_STORE_TTL_MS above is an IDLE timeout: it measures time since the last access and every successful GET refreshes it, so a polling client could keep one entry alive forever. This is the bound that does not depend on read traffic — measured from the entry's creation and never refreshed. Default 86400000 (24h). Chosen well above a long agent se

  • RESPONSE_STORE_MAX_BYTESTotal byte budget. The count caps above bound cardinality, NOT memory: a realistic agent-loop conversation measures ~167KB once stored, so the default 2000 entries is ~327MB (a text-heavy shape measured ~518MB). Lower this on a small VPS — eviction triggers on whichever limit binds first.

    Total byte budget. The count caps above bound cardinality, NOT memory: a realistic agent-loop conversation measures ~167KB once stored, so the default 2000 entries is ~327MB (a text-heavy shape measured ~518MB). Lower this on a small VPS — eviction triggers on whichever limit binds first.

  • RESPONSE_STORE_MAX_MESSAGES
  • RESPONSE_STORE_TTL_MS
  • STATS_MAX_MODELSExplicit opt-ins (both default off): WINDSURFAPI_LS_ALLOW_MIRROR=1 # permit the pinned 3.5.17 mirror rollback WINDSURFAPI_LS_ALLOW_UNPINNED=1 # permit a version absent from ls-pins.json --- Misc bounds --- Cap the number of distinct model names the dashboard stats map tracks (overflow folds into an "(other)" bucket). Default 500; 0 = unbounded.

    Explicit opt-ins (both default off): WINDSURFAPI_LS_ALLOW_MIRROR=1 # permit the pinned 3.5.17 mirror rollback WINDSURFAPI_LS_ALLOW_UNPINNED=1 # permit a version absent from ls-pins.json --- Misc bounds --- Cap the number of distinct model names the dashboard stats map tracks (overflow folds into an "(other)" bucket). Default 500; 0 = unbounded.

  • STICKY_SESSION_ENABLED========== Sticky session (account affinity / prompt-cache economics) ========== Pin each conversation to ONE upstream account across turns. Off by default. Why it matters on DEVIN_CONNECT: upstream prompt caches are per-account, and a cache WRITE costs several times a read (~5.6x, from the 17.8%-of-miss calibration in devin-connect.js; the older "~10x" figure in this repo has no derivation anywhe

    ========== Sticky session (account affinity / prompt-cache economics) ========== Pin each conversation to ONE upstream account across turns. Off by default. Why it matters on DEVIN_CONNECT: upstream prompt caches are per-account, and a cache WRITE costs several times a read (~5.6x, from the 17.8%-of-miss calibration in devin-connect.js; the older "~10x" figure in this repo has no derivation anywhe

  • STICKY_SESSION_MAX
  • STICKY_SESSION_TTL_MSBinding TTL / max concurrent bindings (defaults: 30 min / 10000).

    Binding TTL / max concurrent bindings (defaults: 30 min / 10000).

  • TRUST_PROXY_HOPS
  • TRUST_PROXY_X_FORWARDED_FOR========== Reverse proxy / load balancer ========== Only honour X-Forwarded-For when a trusted proxy sits in front (e.g. the bundled docker-compose nginx LB, or openresty/Caddy). Without this every caller looks like the proxy's IP and the per-caller brute-force lockout collapses to one global bucket. TRUST_PROXY_HOPS = number of trusted proxies appended to XFF (default 1). The docker-compose file

    ========== Reverse proxy / load balancer ========== Only honour X-Forwarded-For when a trusted proxy sits in front (e.g. the bundled docker-compose nginx LB, or openresty/Caddy). Without this every caller looks like the proxy's IP and the per-caller brute-force lockout collapses to one global bucket. TRUST_PROXY_HOPS = number of trusted proxies appended to XFF (default 1). The docker-compose file

  • WAPI_ACCOUNTS_ONLYPer-instance account isolation (opt-in). Comma-separated emails or account ids; only matching accounts are loaded from accounts.json. Unset/blank = load all. Example: WAPI_ACCOUNTS_ONLY=alice@example.com,bob@example.com A non-blank value with no valid tokens fails closed (loads no accounts).

    Per-instance account isolation (opt-in). Comma-separated emails or account ids; only matching accounts are loaded from accounts.json. Unset/blank = load all. Example: WAPI_ACCOUNTS_ONLY=alice@example.com,bob@example.com A non-blank value with no valid tokens fails closed (loads no accounts).

  • WINDSURFAPI_ALLOW_HTTP_IMAGES---- 远程图片协议门 ---- 默认只允许 https 下载远程图片:明文 http 的图片字节可被链路上任何中间人替换, 而它会原样进入模型上下文。设为 1 只恢复**公网主机**的 http;默认或任何其他值 只收 https。私网/回环目标不受该开关影响,始终被独立的 SSRF 策略拒绝(含每一跳 解析到私网地址的情况),不能用来接可信内网图床。策略在每一跳 redirect 上生效。

    ---- 远程图片协议门 ---- 默认只允许 https 下载远程图片:明文 http 的图片字节可被链路上任何中间人替换, 而它会原样进入模型上下文。设为 1 只恢复**公网主机**的 http;默认或任何其他值 只收 https。私网/回环目标不受该开关影响,始终被独立的 SSRF 策略拒绝(含每一跳 解析到私网地址的情况),不能用来接可信内网图床。策略在每一跳 redirect 上生效。

  • WINDSURFAPI_ALLOW_UNAUTHENTICATEDRun the REST API with NO API_KEY (fail-open). Default OFF = fail closed: a request without a matching API_KEY is rejected even on a local bind, because "local bind" is not "no proxy" (a same-host reverse proxy makes every request look loopback). Set to 1 ONLY for genuine local/dev use on a local bind; never on a public bind. The Windows exe auto-generates an API_KEY so it never needs this.

    Run the REST API with NO API_KEY (fail-open). Default OFF = fail closed: a request without a matching API_KEY is rejected even on a local bind, because "local bind" is not "no proxy" (a same-host reverse proxy makes every request look loopback). Set to 1 ONLY for genuine local/dev use on a local bind; never on a public bind. The Windows exe auto-generates an API_KEY so it never needs this.

  • WINDSURFAPI_ATTESTATION_DIROptional directory holding a pre-staged release-attestation.json and release-attestation.json.sigstore.json for air-gapped hosts and forks. Unset = download them from the canonical GitHub release (dwgx/WindsurfAPI).

    Optional directory holding a pre-staged release-attestation.json and release-attestation.json.sigstore.json for air-gapped hosts and forks. Unset = download them from the canonical GitHub release (dwgx/WindsurfAPI).

  • WINDSURFAPI_BACKGROUND_MAINTENANCE_SKIP_BUSYBackground credit/token refresh skips accounts currently serving chat, account maintenance, or LS maintenance by default. Set 0 only if you want scheduled maintenance to run even when an account is busy.

    Background credit/token refresh skips accounts currently serving chat, account maintenance, or LS maintenance by default. Set 0 only if you want scheduled maintenance to run even when an account is busy.

  • WINDSURFAPI_DEGRADED_SERVE========== 429 缓解 / 限流调优 (2026-07-12: 默认已开启) ========== 这三个旋钮治理 Claude Code / OpenCode 的 "429 死循环":单号被限流 → 网关秒拒 429 → 客户端立即重试 → 冷却越叠越长 → 打穿账号池。都是运行时 tunable(面板 设置页可热调,不重启),也可用下面的环境变量固定。2026-07-12 起默认值已从 "字节等价关闭" 翻为 "开启";把值改回括号里的旧默认即可回退。 F1 降级放行:整个可用池仅被"瞬态限流"(非配额耗尽)时,不甩 429,而是选一个 "最不糟、只是短暂冷却"的号继续服务。配额干井(quotaResetAt)仍严格排除。 默认 true(旧默认 false=硬 429)。设 0 关闭。

    ========== 429 缓解 / 限流调优 (2026-07-12: 默认已开启) ========== 这三个旋钮治理 Claude Code / OpenCode 的 "429 死循环":单号被限流 → 网关秒拒 429 → 客户端立即重试 → 冷却越叠越长 → 打穿账号池。都是运行时 tunable(面板 设置页可热调,不重启),也可用下面的环境变量固定。2026-07-12 起默认值已从 "字节等价关闭" 翻为 "开启";把值改回括号里的旧默认即可回退。 F1 降级放行:整个可用池仅被"瞬态限流"(非配额耗尽)时,不甩 429,而是选一个 "最不糟、只是短暂冷却"的号继续服务。配额干井(quotaResetAt)仍严格排除。 默认 true(旧默认 false=硬 429)。设 0 关闭。

  • WINDSURFAPI_HISTORY_BUDGET_BYTES
  • WINDSURFAPI_HISTORY_BUDGET_TURNS---- 出站历史预算(准入护栏,#136/#191)---- 转发给上游的转录超过任一维度时,在**任何上游调用之前**本地拒绝(413, code=history_budget_exceeded),并打一条带实测数字的 WARN —— 而不是让上游在 流中途用 context deadline exceeded 掐断(客户端已收到半截字节,无法干净重试)。 轮数默认 100,取在实测死亡线(113 轮)之下;字节默认 8 MB,压在入口 10 MB 上限 之下,拦的是病态载荷。任一维度设为 0 即关闭该维度。

    ---- 出站历史预算(准入护栏,#136/#191)---- 转发给上游的转录超过任一维度时,在**任何上游调用之前**本地拒绝(413, code=history_budget_exceeded),并打一条带实测数字的 WARN —— 而不是让上游在 流中途用 context deadline exceeded 掐断(客户端已收到半截字节,无法干净重试)。 轮数默认 100,取在实测死亡线(113 轮)之下;字节默认 8 MB,压在入口 10 MB 上限 之下,拦的是病态载荷。任一维度设为 0 即关闭该维度。

  • WINDSURFAPI_IGNORE_CLOUD_FILTEROn the Cascade transport, after GetCascadeModelConfigs returns usable per-account catalogs, pool model lists show their union while routing enforces the selected account's catalog. DEVIN_CONNECT uses its separate selector catalog and is not filtered by this switch. Missing, empty, or failed catalog fetches fail open. Set this to 1 only if you need the legacy behavior that exposes and routes the fu

    On the Cascade transport, after GetCascadeModelConfigs returns usable per-account catalogs, pool model lists show their union while routing enforces the selected account's catalog. DEVIN_CONNECT uses its separate selector catalog and is not filtered by this switch. Missing, empty, or failed catalog fetches fail open. Set this to 1 only if you need the legacy behavior that exposes and routes the fu

  • WINDSURFAPI_IMAGE_JPEG_QUALITY
  • WINDSURFAPI_IMAGE_MAX_BYTES---- 图片解码上限(资源护栏)---- 单张图片字节上限、长边像素上限、重编码 JPEG 质量。

    ---- 图片解码上限(资源护栏)---- 单张图片字节上限、长边像素上限、重编码 JPEG 质量。

  • WINDSURFAPI_IMAGE_MAX_LONG_SIDE
  • WINDSURFAPI_LEAK_TRACE--- Reasoning/content 边界泄漏追踪 (实验性,默认 OFF) ---

    --- Reasoning/content 边界泄漏追踪 (实验性,默认 OFF) ---

  • WINDSURFAPI_LS_MANIFEST_URLOverride the manifest (URL or local path; for tests/air-gapped installs):

    Override the manifest (URL or local path; for tests/air-gapped installs):

  • WINDSURFAPI_LS_PINSOverride the reviewed pins file:

    Override the reviewed pins file:

  • WINDSURFAPI_LS_VERSION--- Language Server installer (read by install-ls.sh, not by the server) --- The installer resolves the newest *pinned* stable release from the upstream manifest (https://windsurf-stable.codeium.com/api/versions), verifies the archive sha256 and the inner binary sha256 against ls-pins.json, and fails closed on any mismatch. There is no silent fallback source. Pin an exact version (must exist in ls

    --- Language Server installer (read by install-ls.sh, not by the server) --- The installer resolves the newest *pinned* stable release from the upstream manifest (https://windsurf-stable.codeium.com/api/versions), verifies the archive sha256 and the inner binary sha256 against ls-pins.json, and fails closed on any mismatch. There is no silent fallback source. Pin an exact version (must exist in ls

  • WINDSURFAPI_NATIVE_TOOL_BRIDGENative Cascade tool bridge. Default off because Cascade executes native Read/Bash-style tools in the remote Windsurf workspace, while most clients expect local execution. v2.0.139 production canary scope is intentionally narrow: without an explicit tool allowlist, only Bash / shell_command / run_command can route through the native bridge. Read/Grep/Glob/WebSearch/WebFetch are protocol matrix work

    Native Cascade tool bridge. Default off because Cascade executes native Read/Bash-style tools in the remote Windsurf workspace, while most clients expect local execution. v2.0.139 production canary scope is intentionally narrow: without an explicit tool allowlist, only Bash / shell_command / run_command can route through the native bridge. Read/Grep/Glob/WebSearch/WebFetch are protocol matrix work

  • WINDSURFAPI_NATIVE_TOOL_BRIDGE_API_KEYS
  • WINDSURFAPI_NATIVE_TOOL_BRIDGE_MODELS
  • WINDSURFAPI_NATIVE_TOOL_BRIDGE_OFFEmergency hard-off:

    Emergency hard-off:

  • WINDSURFAPI_NATIVE_TOOL_BRIDGE_TOOLSOptional gray gates for real testing:

    Optional gray gates for real testing:

  • WINDSURFAPI_NEUTRALIZE_CLIENT_ID---- 上游反竞品指纹的防线(两个默认开的关,关掉是静默失效)---- 这两个都**默认开**,列在这里是因为关掉之后的失败形态是静默的:请求被上游挡下, 而报错不会提到这两个开关,所以运维很难把症状连回来。 Devin 上游对竞品 coding-agent 流量有两道闸:529 竞品指纹闸,以及 2026-07-10 实测 确认的 content-policy permission_denied(踩 Claude Code Agent-SDK 那行自我标识)。 默认会把 system prompt 正文里的这类指纹改写成通用助手身份,请求才发得出去。 设 0 关掉改写 —— 只在你确认某条改写规则误伤了自己的提示词时才用。

    ---- 上游反竞品指纹的防线(两个默认开的关,关掉是静默失效)---- 这两个都**默认开**,列在这里是因为关掉之后的失败形态是静默的:请求被上游挡下, 而报错不会提到这两个开关,所以运维很难把症状连回来。 Devin 上游对竞品 coding-agent 流量有两道闸:529 竞品指纹闸,以及 2026-07-10 实测 确认的 content-policy permission_denied(踩 Claude Code Agent-SDK 那行自我标识)。 默认会把 system prompt 正文里的这类指纹改写成通用助手身份,请求才发得出去。 设 0 关掉改写 —— 只在你确认某条改写规则误伤了自己的提示词时才用。

  • WINDSURFAPI_NLU_RETRY--- Narrate-instead-of-calling retry (referenced in the README FAQ) --- Some models answer "I will read that file" in prose instead of emitting a tool call. When that happens the narration is folded back into history with a correction prompt asking for a protocol-conformant emit (#125). Tri-state, and the default is NOT a plain on/off: unset → ON for GLM / Kimi / Zhipu / Moonshot only (they narrat

    --- Narrate-instead-of-calling retry (referenced in the README FAQ) --- Some models answer "I will read that file" in prose instead of emitting a tool call. When that happens the narration is folded back into history with a correction prompt asking for a protocol-conformant emit (#125). Tri-state, and the default is NOT a plain on/off: unset → ON for GLM / Kimi / Zhipu / Moonshot only (they narrat

  • WINDSURFAPI_NO_OPEN启动后不自动打开浏览器(打包版默认会开)。

    启动后不自动打开浏览器(打包版默认会开)。

  • WINDSURFAPI_PROTO_TRACESmoke an already-running native bridge deployment with: API_KEY=... BASE_URL=http://127.0.0.1:3003 npm run smoke:native-bridge Protocol trace is lab-only. For Read wrapper reverse engineering, prefer the dedicated child summary over global raw string dumps:

    Smoke an already-running native bridge deployment with: API_KEY=... BASE_URL=http://127.0.0.1:3003 npm run smoke:native-bridge Protocol trace is lab-only. For Read wrapper reverse engineering, prefer the dedicated child summary over global raw string dumps:

  • WINDSURFAPI_PROTO_TRACE_READ_WRAPPER_STRINGS
  • WINDSURFAPI_QUOTA_COOLDOWN_MS
  • WINDSURFAPI_QUOTA_DRY_THRESHOLD---- 配额干涸判定 ---- 本周配额百分比低到多少算"干涸"(会给账号加一段自愈冷却),以及冷却时长。

    ---- 配额干涸判定 ---- 本周配额百分比低到多少算"干涸"(会给账号加一段自愈冷却),以及冷却时长。

  • WINDSURFAPI_RL_BURST_MSF2 裸 429 短冷却:上游给裸 429(无 Retry-After reset)时账号冷却时长(毫秒)。 默认 15000(15s);旧默认 300000(5min)。KiroStudio 生产数据显示裸 burst 几秒 自愈,冻 5min 是小池雪崩源。breaker 指数退避仍会拉长重复触发的号。

    F2 裸 429 短冷却:上游给裸 429(无 Retry-After reset)时账号冷却时长(毫秒)。 默认 15000(15s);旧默认 300000(5min)。KiroStudio 生产数据显示裸 burst 几秒 自愈,冻 5min 是小池雪崩源。breaker 指数退避仍会拉长重复触发的号。

  • WINDSURFAPI_RL_CLIENT_BACKOFF_CEIL_MS
  • WINDSURFAPI_RL_CLIENT_BACKOFF_FLOOR_MSF3 客户端退避地板:给客户端的 Retry-After 响应头设最小值(毫秒),掐断"1s 提示 → 立即重试"的热循环。默认 30000(30s);旧默认 0(无地板)。注意:影响所有 429 响应(不止 CC),所有客户端至少等这么久。ceil 默认 600000(10min)只削超长值。

    F3 客户端退避地板:给客户端的 Retry-After 响应头设最小值(毫秒),掐断"1s 提示 → 立即重试"的热循环。默认 30000(30s);旧默认 0(无地板)。注意:影响所有 429 响应(不止 CC),所有客户端至少等这么久。ceil 默认 600000(10min)只削超长值。

  • WINDSURFAPI_SINGLE_TENANT_CACHEBinding requires a per-user scope on the caller (body.user, safety_identifier, prompt_cache_key, Claude Code metadata.user_id, or an x-session-id header) so a shared API key can't funnel N users onto one account. A genuine single-user self-host without those signals opts in via:

    Binding requires a per-user scope on the caller (body.user, safety_identifier, prompt_cache_key, Claude Code metadata.user_id, or an x-session-id header) so a shared API key can't funnel N users onto one account. A genuine single-user self-host without those signals opts in via:

  • WINDSURFAPI_SKIP_DOTENV---- 启动行为 ---- 跳过 .env 加载(容器里用真实环境变量注入时更干净)。

    ---- 启动行为 ---- 跳过 .env 加载(容器里用真实环境变量注入时更干净)。

  • WINDSURFAPI_SPECIAL_AGENT_BACKENDOptional special-agent backend for models that do not work through direct Cascade chat (currently swe-1.6 / swe-1.6-fast / adaptive / arena-*). Default off. When enabled, the first PoC backend uses Devin CLI print mode: devin -p "<prompt>" Install/configure Devin CLI separately on the host/container and point this path at it. Do not bake it into the base image unless you want that runtime dependen

    Optional special-agent backend for models that do not work through direct Cascade chat (currently swe-1.6 / swe-1.6-fast / adaptive / arena-*). Default off. When enabled, the first PoC backend uses Devin CLI print mode: devin -p "<prompt>" Install/configure Devin CLI separately on the host/container and point this path at it. Do not bake it into the base image unless you want that runtime dependen

  • WINDSURFAPI_STICKY_QUEUE_ON_PIN_MSWatch it work: GET /dashboard/api/connect-metrics → sticky.{hits,creates,...}. Related experimental flags (dashboard → 实验性): stickyBindByUserOnly, stickyNoFallback. queue-on-pin:当被绑定的账号暂时不可用(RPM 触顶 / 冷却)时,**短等它自己恢复**, 而不是立刻换到替补号。0 = 关(默认),现有部署行为不变。2000 是有用的起点。 为什么值得等:换号要在替补上付一次完整前缀写(约为读的 5.6 倍 —— 这个比例来自 devin-connect.js 的 "hit cost measured at 17.8% of miss",1/0.178;**不是**来自那组 tag4 token 计数,那组数只确定

    Watch it work: GET /dashboard/api/connect-metrics → sticky.{hits,creates,...}. Related experimental flags (dashboard → 实验性): stickyBindByUserOnly, stickyNoFallback. queue-on-pin:当被绑定的账号暂时不可用(RPM 触顶 / 冷却)时,**短等它自己恢复**, 而不是立刻换到替补号。0 = 关(默认),现有部署行为不变。2000 是有用的起点。 为什么值得等:换号要在替补上付一次完整前缀写(约为读的 5.6 倍 —— 这个比例来自 devin-connect.js 的 "hit cost measured at 17.8% of miss",1/0.178;**不是**来自那组 tag4 token 计数,那组数只确定

  • WINDSURFAPI_STREAM_COMMIT_MSOpenAI chat stream commit gate: wait this many ms for a real first frame before committing HTTP 200. 0 disables the gate (legacy headers-first).

    OpenAI chat stream commit gate: wait this many ms for a real first frame before committing HTTP 200. 0 disables the gate (legacy headers-first).

  • WINDSURFAPI_STRICT_MODEL---- 未知模型名的处理(footgun,建议别关)---- 默认 1 = 目录里没有的模型名直接 400 model_not_found。 设 0 会恢复"静默降级到免费 selector swe-1-6-slow"的旧行为 —— 这不是一个 兼容开关而是个陷阱:客户端明确要了付费模型,实际跑的是免费模型,**输出与 计费都变了**,而响应里回显的仍是客户端请求的那个名字,客户端无法察觉。 只有在确信快照过期、且某个确实存活的 selector 被误拒时才临时用。

    ---- 未知模型名的处理(footgun,建议别关)---- 默认 1 = 目录里没有的模型名直接 400 model_not_found。 设 0 会恢复"静默降级到免费 selector swe-1-6-slow"的旧行为 —— 这不是一个 兼容开关而是个陷阱:客户端明确要了付费模型,实际跑的是免费模型,**输出与 计费都变了**,而响应里回显的仍是客户端请求的那个名字,客户端无法察觉。 只有在确信快照过期、且某个确实存活的 selector 被误拒时才临时用。

  • WINDSURFAPI_STRICT_USAGE_TOTAL---- total_tokens 的算术恒等式 ---- OpenAI 规定 total_tokens == prompt_tokens + completion_tokens。本代理默认**刻意 打破**这条恒等式:cache_write > 0 时 total 会多出 cache_write,因为它承担的是 "完整可计费成本"这个职责,而分桶字段严格遵守 OpenAI/Anthropic 语义 (cache_write 走 cache_creation_input_tokens,不进 prompt_tokens)。 #118 是刻意这么选的:另一个方案(cache_write 进 prompt_tokens)会让下游中转 (one-api / new-api / sub2api)把它当普通输入计费,几小时烧穿试用额度。 设 1 恢复恒等式,给会**校验**这条等式的客户端用。默认 0

    ---- total_tokens 的算术恒等式 ---- OpenAI 规定 total_tokens == prompt_tokens + completion_tokens。本代理默认**刻意 打破**这条恒等式:cache_write > 0 时 total 会多出 cache_write,因为它承担的是 "完整可计费成本"这个职责,而分桶字段严格遵守 OpenAI/Anthropic 语义 (cache_write 走 cache_creation_input_tokens,不进 prompt_tokens)。 #118 是刻意这么选的:另一个方案(cache_write 进 prompt_tokens)会让下游中转 (one-api / new-api / sub2api)把它当普通输入计费,几小时烧穿试用额度。 设 1 恢复恒等式,给会**校验**这条等式的客户端用。默认 0

  • WINDSURFAPI_TOOL_HYGIENETool-name/id hygiene across messages/responses/gemini (0 disables).

    Tool-name/id hygiene across messages/responses/gemini (0 disables).

  • WINDSURFAPI_TRACEFull-chain request trace. 1 writes one directory per request under WINDSURFAPI_TRACE_DIR, including raw upstream bytes; prompt fragments land on disk. Pack it for an issue with `node scripts/support-bundle.mjs`, which redacts text legs and refuses to write a bundle when the secret scan hits.

    Full-chain request trace. 1 writes one directory per request under WINDSURFAPI_TRACE_DIR, including raw upstream bytes; prompt fragments land on disk. Pack it for an issue with `node scripts/support-bundle.mjs`, which redacts text legs and refuses to write a bundle when the secret scan hits.

  • WINDSURFAPI_TRACE_DIR
  • WINDSURFAPI_UPDATE_ATTESTATIONSource-OTA attestation policy for git/PM2 updates (/self-update and update.sh). warn (default): verify release-attestation.json when present; missing material (no attestation, no signature bundle, no cosign, network failure) logs a warning and the update proceeds, so existing installs keep working. required: refuse any release without a valid cosign-verified attestation whose commit matches the se

    Source-OTA attestation policy for git/PM2 updates (/self-update and update.sh). warn (default): verify release-attestation.json when present; missing material (no attestation, no signature bundle, no cosign, network failure) logs a warning and the update proceeds, so existing installs keep working. required: refuse any release without a valid cosign-verified attestation whose commit matches the se

WindsurfAPI releases & version history

More in API

Rate this project

Click to rate — no account needed.

0 comments

Posting as guest
Loading comments…