Skip to content

Latest commit

 

History

History
344 lines (261 loc) · 11.8 KB

deployment.md

File metadata and controls

344 lines (261 loc) · 11.8 KB

ChatGPT-Web2API — Deployment Guide

Looking for a different doc? See the documentation index for a "which doc should I read?" routing table.

Three ways to share this with others, depending on the audience.


Option 1: pip install (Simplest — for developers)

Anyone with Python and Chrome can run it in 3 steps:

# 1. Install
pip install git+https://github.com/your-org/ChatGPT-Web2API.git

# 2. Start — launches Chrome, opens chatgpt.com
chatgpt-web2api

# 3. First time: log into ChatGPT in the Chrome window that opens.
#    The proxy detects login automatically and starts serving.

The Chrome profile is saved at ~/.chatgpt-web2api/chrome-profile/. Subsequent starts skip login.

Configuration

Create ~/.chatgpt-web2api/config.json:

{
  "port": 8080,
  "cdp_port": 9222,
  "api_keys": ["sk-my-secret-key"],
  "default_model": "auto"
}

Or use environment variables:

W2A_PORT=9090 W2A_API_KEYS=sk-key1,sk-key2 chatgpt-web2api

Option 2: Docker (For servers / headless)

Requires exporting cookies from an already-logged-in browser session.

Step 1: Export cookies from your browser

Use a browser extension like EditThisCookie or Cookie-Editor:

  1. Open chatgpt.com while logged in
  2. Export all cookies for chatgpt.com as JSON
  3. Save as cookies.json

The file should look like:

[
  {
    "name": "__Secure-next-auth.session-token",
    "value": "...",
    "domain": ".chatgpt.com",
    "path": "/",
    "secure": true,
    "httpOnly": true
  },
  ...
]

Step 2: Run with Docker

# Build
docker build -t chatgpt-web2api .

# Run (mount cookies + persistent profile)
docker run -d \
  --name chatgpt-proxy \
  -p 8080:8080 \
  -v ./cookies.json:/data/cookies/cookies.json:ro \
  -v chatgpt-profile:/data/chrome-profile \
  chatgpt-web2api

Step 3: Use it

curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"auto","messages":[{"role":"user","content":"Hello"}]}'

Cookie refresh

Cookies expire. When auth fails:

  1. Re-export fresh cookies from your browser
  2. Replace cookies.json
  3. Restart the container: docker restart chatgpt-proxy

Option 3: Remote server (For teams)

Run the proxy on a server, let others connect to it.

# On the server (with API key protection)
chatgpt-web2api --host 0.0.0.0 --port 8080

# Or with config
cat > config.json << 'EOF'
{
  "port": 8080,
  "host": "0.0.0.0",
  "api_keys": ["sk-team-key-1", "sk-team-key-2"],
  "default_model": "auto"
}
EOF
chatgpt-web2api --config config.json

Others connect:

from openai import OpenAI

client = OpenAI(
    base_url="http://your-server:8080/v1",
    api_key="sk-team-key-1"
)

resp = client.chat.completions.create(
    model="auto",
    messages=[{"role": "user", "content": "Hello"}]
)

Important: One mutating request at a time per tab by default. The browser is single-threaded per page. For team use, queue requests, run multiple instances on different ports, or enable parallel_tabs (see Parallel mode below) for per-tab concurrency on one shared Chrome.


Option 4: OS-level supervision (always-on / server)

To run the proxy continuously under an OS supervisor (survives reboots, restarts on failure), see the dedicated OS-Level Supervision Guide. It covers systemd (Linux), launchd (macOS), and Task Scheduler / NSSM (Windows), with two supervisor styles:

  • ensure on a timer — mirrors the ZCode hook model; reconciles REST + SSE on a schedule, no long-lived Python process.
  • start + chatgpt-web2api-mcp as services — classic always-on; the supervisor owns restart policy for the two long-lived processes (REST owns Chrome; MCP/SSE is a separate process that attaches to it).

The guide is documentation only — no supervisor scripts are installed by the package. ZCode users should prefer the ensure hook over OS supervision.


Option 5: Production runbook (operating a running deployment)

For interpreting /health, diagnosing failure modes, understanding breaker states and cooldowns, recovering auth, collecting logs, and safe restart sequences, see the dedicated Production Runbook. It covers:

  • startup checklist + the reconcile command and its exit codes (0/1/2)
  • /health field reference and the exact conditions for each status (starting/healthy/degraded/broken)
  • common failure modes mapped to symptoms and fixes
  • the four breakers (auth_required, composer_send_readiness, cdp_reconnect, chrome_crash_loop) with their thresholds/cooldowns
  • the auth-recovery flow (the one breaker that needs a human)
  • the safe restart rule (restart on process exit, not on degraded)
  • post-deploy validation including the exact-output "Reply with exactly: ok" sanity send

All field names, state values, and thresholds are source-cited.


Cookie Export Guide (Detailed)

Chrome — EditThisCookie

  1. Install EditThisCookie
  2. Go to chatgpt.com while logged in
  3. Click the cookie icon in toolbar
  4. Click "Export" → copies JSON to clipboard
  5. Save as cookies.json

Firefox — Cookie-Editor

  1. Install Cookie-Editor
  2. Go to chatgpt.com while logged in
  3. Click the cookie icon → "Export" → "Export as JSON"
  4. Save as cookies.json

Manual (any browser) — DevTools

  1. Open chatgpt.com, press F12 → Console
  2. Run: document.cookie
  3. Parse the cookie string into JSON format

Multiple Instances (Scale concurrency)

Each Chrome instance handles one request at a time. Run multiple for throughput:

# Instance 1
chatgpt-web2api --port 8081 --cdp-port 9222

# Instance 2
chatgpt-web2api --port 8082 --cdp-port 9223 --user-data-dir ~/.chatgpt-web2api/chrome-profile-2

# Instance 3
chatgpt-web2api --port 8083 --cdp-port 9224 --user-data-dir ~/.chatgpt-web2api/chrome-profile-3

Put a reverse proxy (nginx, Caddy) in front with round-robin:

upstream chatgpt {
    server 127.0.0.1:8081;
    server 127.0.0.1:8082;
    server 127.0.0.1:8083;
}

server {
    listen 8080;
    location / {
        proxy_pass http://chatgpt;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_buffering off;
    }
}

Parallel mode (one Chrome, many tabs)

parallel_tabs: true (or W2A_PARALLEL_TABS=1) lets several bridge processes share one Chrome instance, each driving its own owned tab in parallel — no proxy, no second browser profile. Per-target locking serializes same-tab mutations while different tabs run concurrently. Requires tab_mode: "owned" (enforced at load; the default).

# Process A — its own REST port, own tab on the shared Chrome (cdp 9222)
W2A_PARALLEL_TABS=1 chatgpt-web2api --port 8081 --cdp-port 9222

# Process B — different REST port, different tab on the SAME Chrome
W2A_PARALLEL_TABS=1 chatgpt-web2api --port 8082 --cdp-port 9222

Requirements and caveats:

  • parallel_tabs: true requires tab_mode: "owned" (enforced at load). Each process drives exactly one owned tab; mutating operations serialize per owned target, not globally per CDP port. This is not an in-process tab pool or request router — that remains future work.
  • Each process still needs its own local REST/MCP port (--port). There is no single-endpoint router yet.
  • One ChatGPT account is shared across all tabs (one Chrome profile). Parallel mode increases browser-tab parallelism, not account quota — all workers share one session and may hit account-level rate limits; rate-limit handling stays reactive (backoff/retry).
  • Unique instance identity per process. REST derives it from rest:{port} automatically. MCP derives it from mcp:sse:{host}:{port} (SSE) or mcp:stdio:{pid} (stdio) when parallel_tabs=true. The stdio PID identity is unique per run but not stable across restart (a new PID can't reclaim the prior tab); set W2A_INSTANCE_ID to a stable, unique-per-worker value if you need restart-reclaim. Do not reuse the same W2A_INSTANCE_ID across live workers on the same Chrome — that makes them collide on one tab.
  • Fail-closed, not fallback. If a process can't obtain (or loses mid-flight) an owned tab, the operation fails retryably (REST 503 code=owned_tab_required / MCP isError) rather than silently sharing a tab. Mixing per-target and port-wide locks would reintroduce split-brain. Other retryable 503/isError codes (lock_timeout, circuit_open) keep their existing semantics.

Rollout warning: do not mix old (port-lock-only) workers and new (parallel_tabs=true) workers on the same CDP port during a rolling upgrade — their lock files don't exclude each other. Finish the rollout on a port before enabling parallel mode on it.

Other CDP clients on the same Chrome (Super-Browser, Playwright, etc.)

The bridge creates owned tabs (tab_mode="owned", the default) — each driver gets its own dedicated tab via Target.createTarget and its own websocket. A Page.navigate issued by one CDP client goes over that client's websocket to that client's tab; it cannot navigate another client's tab. CDP targets are independent.

However, running multiple CDP clients (e.g., 4 Super-Browser MCP servers + the bridge + the MCP server = 6+ clients) on one Chrome instance can still degrade reliability through resource contention:

  • CPU/memory saturation — each CDP client drives React hydration, DOM polling, and JS evaluation. Under load, the Chrome renderer can slow enough that ChatGPT's SPA fails to hydrate the composer within the bridge's readiness window (see P2 staged readiness diagnostics).
  • CDP HTTP endpoint queuing — the /json/list and /json/version endpoints are served by Chrome's DevTools HTTP server, which is single-threaded. Many clients polling concurrently can introduce latency.
  • Profile-level state — all tabs share one Chrome profile (one account, one cookie jar, one service worker). A service-worker update or auth redirect on one tab can briefly affect the others.

What this means in practice: the bridge's write path (navigate + send) is more sensitive to these effects than the read path (list/get conversations via /backend-api/*), because the write path requires DOM-level readiness (composer hydrated, send button enabled) while the read path is a pure HTTP fetch. If you observe "navigation timeout" or "no send button" errors that worsen as more clients connect, consider:

  1. Running the bridge on a dedicated Chrome instance (separate --user-data-dir and --cdp-port).
  2. Reducing the number of concurrent CDP clients on the shared Chrome.
  3. Enabling parallel_tabs: true so the bridge and MCP server each get their own tab (they do by default with tab_mode="owned", but parallel_tabs adds per-target locking and fail-closed semantics).

The bridge's error messages now include staged diagnostics (P2) that name the failing readiness stage, making this degradation diagnosable rather than opaque.


Troubleshooting

Problem Fix
"No access token" Log into ChatGPT in the Chrome window that opens
"Chrome CDP did not respond" Chrome isn't running. Check chrome_path in config
"Timed out waiting for assistant" Page may be stuck. Restart the proxy
Cookies not working Re-export fresh cookies. They expire every ~2 weeks
Headless fails Anti-bot detection blocks headless. Use cookie injection + headed mode on a VNC/display. The Dockerfile ships W2A_HEADLESS=false for this reason; set W2A_HEADLESS=true only if you accept the anti-bot risk