Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,20 @@ You can pre-fill via query string: `?baseUrl=https://your.gateway&apiKey=mnfst_.

### Cross-origin requirements

Wingman runs at `wingman.manifest.build` and your gateway runs elsewhere, so the gateway must allow the Wingman origin via CORS. The Manifest backend allows it out of the box; self-hosters running a different backend need:
Wingman runs at `wingman.manifest.build` and your gateway runs elsewhere, so the gateway must allow the Wingman origin via CORS. The Manifest backend allows it **in dev mode** (`NODE_ENV !== 'production'`), which covers local and self-hosted-dev gateways. Production builds keep CORS off β€” the dashboard is same-origin there β€” so pointing Wingman at a production gateway means opting into CORS for the Wingman origin yourself.

Whatever the backend, it must answer the preflight with at least:

```
Access-Control-Allow-Origin: https://wingman.manifest.build
Access-Control-Allow-Headers: Authorization, Content-Type, X-API-Key
Access-Control-Allow-Headers: Authorization, Content-Type, X-API-Key, X-Stainless-Lang, X-Stainless-Package-Version, X-Stainless-OS, X-Stainless-Arch, X-Stainless-Runtime, X-Stainless-Runtime-Version, X-Stainless-Retry-Count
```

`Access-Control-Allow-Credentials` should stay **false** β€” Wingman uses bearer keys, never cookies.
The `X-Stainless-*` headers matter: the OpenClaw, Hermes, and OpenAI SDK profiles replay them to mimic the real SDK fingerprint, so an allow-list that omits them fails the preflight and the request never leaves the browser. Hermes sends a couple more (`X-Stainless-Async`, `X-Stainless-Read-Timeout`), so the robust option is to reflect the request's `Access-Control-Request-Headers` instead of hard-coding a list.

If the gateway is on a loopback address (`localhost` / `127.0.0.1`) and you load Wingman over HTTPS, Chrome's Private Network Access also wants `Access-Control-Allow-Private-Network: true` on the preflight.

`Access-Control-Allow-Credentials` can stay **false** β€” Wingman uses bearer keys, never cookies.

If you're behind a corporate firewall or running a fully air-gapped Manifest, clone this repo and `npm run dev` β€” Wingman runs entirely client-side.

Expand Down
Loading