--- title: Minimal Conversation JS Static emoji: πŸŽ™οΈ colorFrom: indigo colorTo: purple sdk: static pinned: false hf_oauth: true short_description: Static port of the minimal JS voice chat (Reachy Mini). tags: - reachy_mini - reachy_mini_js_app --- # Minimal Conversation (JS) β€” static build A **`sdk: static`** port of [`tfrere/minimal-conversation`](https://huggingface.co/spaces/tfrere/minimal-conversation) (originally a `sdk: docker` Space). Same app, no container, no nginx, no entrypoint script β€” just the Vite production build served as files. This is an **investigation** Space: does the conversation app run as a pure static Space with **no server-side proxy**? Conclusion: **yes.** Three things the Docker variant used a server for turn out to be unnecessary on a static Space: 1. **HF OAuth client ID.** The Docker variant patched `__OAUTH_CLIENT_ID__` into `index.html` from a container env var at start-up (`docker-entrypoint.d/10-inject-hf-vars.sh`). On a static Space, HF injects `window.huggingface.variables` (incl. `OAUTH_CLIENT_ID`) into the `` automatically when `hf_oauth: true` is set β€” the SDK host shell reads it directly. 2. **OpenAI ephemeral-key minting.** `src/ephemeral-key.ts` POSTs the visitor's HF token to the Pollen website (`…pollen-robotics-reachy-mini.hf.space/api/openai/ephemeral`) straight from the browser. That endpoint's CORS is origin-reflecting, so this origin is accepted with no allow-list change. The master `OPENAI_API_KEY` stays in the website Space's secrets. 3. **OpenAI Realtime SDP call.** The Docker variant routed this through an nginx `/openai/` reverse-proxy because OpenAI exposes no CORS for personal `sk-` keys. But this app authenticates with short-lived `ek_…` ephemeral keys, and OpenAI's `POST /v1/realtime/calls` returns `Access-Control-Allow-Origin: *` for those β€” so the browser calls `https://api.openai.com/v1/realtime/calls` directly. `src/openai-realtime.ts` was changed from the `/openai/...` same-origin path to the direct URL (overridable via `VITE_OPENAI_REALTIME_URL`). So there is **no backend**. The browser talks to: Reachy Mini central (robot signaling, via the SDK), the Pollen website (ephemeral key mint), and OpenAI Realtime (SDP handshake + WebRTC voice tunnel). > **Must be public.** A *private* static Space can't be loaded in the > `huggingface.co` Spaces iframe wrapper: its `hf_jwt` auth cookie is > SameSite-blocked in the cross-origin iframe, so assets come back as > `401 text/plain` and the module is refused. Public has no such cookie > requirement. ## How it's built ```bash npm install npm run build # β†’ dist/ (this Space is the contents of dist/) ``` The source lives in the upstream `tfrere/minimal-conversation` repo (plus the one-line `openai-realtime.ts` change above). This Space holds only the built artifacts + this README. To update, rebuild and re-upload `dist/`. ## Architecture (unchanged from upstream) ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” robot mic β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” input track β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Reachy β”‚ ────────────► β”‚ Browserβ”‚ ──────────► β”‚ OpenAI β”‚ β”‚ Mini β”‚ β”‚ (this β”‚ β”‚ Realtime β”‚ β”‚ daemon β”‚ ◄──────────── β”‚ app) β”‚ ◄────────── β”‚ API β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ robot speaker β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ output track β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## License MIT for the glue code (same as upstream). Underlying SDKs keep their own licenses.