Problem
The relay serves no CORS headers at all — confirmed via `grep -rn "cors|CORS|Access-Control"` across the Go source, which returns nothing, and there's no middleware registered for it in `cmd/relay/main.go`'s handler chain (`API Key → Rate Limiter → Router`).
Any browser-based consumer that calls the relay directly (i.e. without routing through a same-origin reverse proxy) hits a blocked preflight:
```
Access to fetch at 'https://thrubox-server.onrender.com/api/messages' from origin 'https://'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
This was already anticipated in the ThruBox-Client consumer docs/comments (see Chainvoice's `relayClient.js` and `vite.config.js`), which document proxying as the only supported workaround today:
"The relay serves no CORS headers, so a direct cross-origin request from the browser fails its preflight... Production is expected to reach the relay through a reverse proxy that supplies CORS."
That workaround is fine for consumers willing to run their own reverse proxy, but ThruBox-Server is published as a general-purpose relay (npm SDK + GHCR image) — any consumer that wants to call it directly from the browser without standing up a proxy currently can't.
Proposed fix
Add CORS middleware to the handler chain in `cmd/relay/main.go`, alongside the existing `APIKeyAuth`/`RateLimiter` middleware:
- Handle preflight `OPTIONS` requests explicitly (the current `http.ServeMux` routes have no `OPTIONS` handler, so preflights currently 404 in addition to missing headers).
- Set `Access-Control-Allow-Origin`, `Access-Control-Allow-Methods` (GET, POST, DELETE), and `Access-Control-Allow-Headers` (at minimum `Content-Type`, and `X-API-Key` when `security.api_key` is configured).
- Make allowed origins configurable (new `security.allowed_origins` config key + `RELAY_SECURITY_ALLOWED_ORIGINS` env override, following the existing pattern in `internal/config/config.go`) rather than hardcoding `*`, so self-hosted operators can restrict it if they want to.
- Document the new config in README's Configuration table, and note that consumers can now either proxy (existing approach) or call the relay directly if their origin is allowlisted.
Notes
- Not urgent for Chainvoice's current deployment specifically — that's being fixed independently via a Vercel rewrite (same-origin proxy), matching the already-documented intended setup.
- This is about unblocking other ThruBox consumers who don't have a proxy in front of the relay.
Problem
The relay serves no CORS headers at all — confirmed via `grep -rn "cors|CORS|Access-Control"` across the Go source, which returns nothing, and there's no middleware registered for it in `cmd/relay/main.go`'s handler chain (`API Key → Rate Limiter → Router`).
Any browser-based consumer that calls the relay directly (i.e. without routing through a same-origin reverse proxy) hits a blocked preflight:
```
Access to fetch at 'https://thrubox-server.onrender.com/api/messages' from origin 'https://'
has been blocked by CORS policy: Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
```
This was already anticipated in the ThruBox-Client consumer docs/comments (see Chainvoice's `relayClient.js` and `vite.config.js`), which document proxying as the only supported workaround today:
That workaround is fine for consumers willing to run their own reverse proxy, but ThruBox-Server is published as a general-purpose relay (npm SDK + GHCR image) — any consumer that wants to call it directly from the browser without standing up a proxy currently can't.
Proposed fix
Add CORS middleware to the handler chain in `cmd/relay/main.go`, alongside the existing `APIKeyAuth`/`RateLimiter` middleware:
Notes