# Architecture — one container, everything inside Everything runs in **one container** built from the root `Dockerfile`. The extensions inside Chrome dial their servers on hardcoded `127.0.0.1` ports, so the servers **must** share Chrome's localhost — a separate machine/Space can never reach them. That's why there's a single image, not microservices. ``` https://…hf.space (only port 3001 is public) │ ┌────────▼─────────┐ │ monitor :3001 │ UI + API gateway (one Go binary) │ / /chrome.log │ (open, no key) │ /gpt /gemini /flow│ (API-key gated → localhost) └──┬──────┬──────┬──┘ 9225 ───┘ 8000│ └─── 8101 (chatgpt) (gemini) (flow) │ │ │ ws 9225 ws 9226 ws 9227 ◄── extensions dial in (localhost) └──────── Chrome (headless) + 3 unpacked extensions ────────┘ CDP 9222 · socat proxy 9223 · profile in /home/chrome/data ``` ## Port map (all on the container's 127.0.0.1) | Port | Who | Purpose | |------|-----|---------| | 3001 | monitor | UI + API gateway (**only published port**) | | 9222 | Chrome | DevTools Protocol (CDP) | | 9223 | socat | CDP proxied for external tools | | 9225 | chatgpt server | HTTP API **and** extension WS (same port) | | 8000 / 9226 | gemini server | HTTP API / extension cookie-WS | | 8101 / 8100 / 9227 | flow server | HTTP API / ext-callback / extension WS | ## What starts it `start_hf.sh` (the container entrypoint) in order: restore profile from Postgres → clean profile → start monitor (3001) → CDP proxy → **start the 3 backend servers** (step 6a) → launch Chrome with the 3 extensions. Each server logs to `/home/chrome/{chatgpt,gemini,flow}.log`. ## Components added on top of the base image - `monitor/` — Go UI, now also the **reverse-proxy gateway** (`gatewayHandler`, `authOK`). - `profilesync/` — Go tool, Chrome profile ⇄ Postgres snapshot. - `free-Chatgpt-api/`, `free-gemini-api/` (Go) + `flow-agent/` (Python/FastAPI) — the 3 servers, built/installed into the image by the `Dockerfile`.