# static-rewrite-demo — CDN page + standalone server + same-origin rewrite

The most common real-world shape: your page is a **static/JAMstack site on a
CDN** (Vercel, Netlify, Cloudflare Pages), and `@qid/connect-server` runs as a
**small backend on another host**. A **same-origin rewrite** of `/qid/*` glues
them together so the browser, the wallets, and the session cookie all live on
the page's own origin.

```
browser ── https://www.yoursite.com/qid/*          (same origin as the page)
   └─ CDN rewrite of /qid/*  (transparent, keeps the path + Origin header)
        └─ https://your-backend.example/qid/*
             └─ this server on 127.0.0.1:8890
```

## Why the rewrite must be same-origin (not a cross-origin call)

The session is a **first-party HttpOnly cookie** set by the server. If the
browser called the backend cross-origin (`fetch('https://your-backend…')`),
the cookie would be third-party — `SameSite`/CORS stop it being set or sent,
and proofs would bind to the wrong origin. Proxying `/qid/*` under the page's
own origin makes the cookie first-party and the proof origin correct. This is
mandatory, not a preference.

The one rule: **never let the proxy change the origin the wallet sees vs. the
one the user visits.** A transparent path-only rewrite that forwards `Origin`
is exactly right; a rewrite that makes the server issue `rp_origin` for a
different host is exactly wrong.

## Run the server

```sh
# from the repo root (workspace resolves @qid/connect-server), or vendor packages/server
QID_ORIGIN=https://www.yoursite.com \
QID_SESSION_SECRET=$(openssl rand -hex 24) \
QID_DB=/var/lib/qid-connect/qid.sqlite \
bun server.mjs
# Node 20+: `node server.mjs` (uses node:sqlite). Needs `unzip` for `bun install`
# on a bare box: `apt-get install -y unzip`.
```

`QID_ORIGIN` must be the host users **actually land on** after any apex↔www or
http→https redirect (open your live site, read `location.origin`). The server
warns at boot if the configured origin itself redirects — the classic
`bad_origin` trap.

## Rewrite recipes

**Vercel** (`vercel.json`) — host-scope it to every host your site answers on
(apex *and* www), because the edge may canonicalize between them:

```json
{
  "rewrites": [
    { "source": "/qid/:path*", "has": [{ "type": "host", "value": "www.yoursite.com" }],
      "destination": "https://your-backend.example/qid/:path*" },
    { "source": "/qid/:path*", "has": [{ "type": "host", "value": "yoursite.com" }],
      "destination": "https://your-backend.example/qid/:path*" }
  ]
}
```

**Netlify** (`netlify.toml`):

```toml
[[redirects]]
  from = "/qid/*"
  to = "https://your-backend.example/qid/:splat"
  status = 200      # 200 = rewrite (proxy), NOT 301/302
  force = true
```

**Caddy**:

```
www.yoursite.com {
  reverse_proxy /qid/* your-backend.example:443 {
    header_up Host {upstream_hostport}
  }
  # ... your static file serving ...
}
```

**nginx**:

```nginx
location /qid/ {
  proxy_pass https://your-backend.example;
  proxy_set_header Host $host;               # keep the browser's Origin/Host semantics
  proxy_set_header Origin $http_origin;
}
```

## systemd unit

See [`qid-connect.service`](./qid-connect.service). Put the secrets in an
`EnvironmentFile` (e.g. `/etc/qid-connect.env`), not in the unit.

## Env template

```
QID_ORIGIN=https://www.yoursite.com
QID_SESSION_SECRET=          # openssl rand -hex 24 ; rotating it signs everyone out
QID_DB=/var/lib/qid-connect/qid.sqlite
QID_HOST=127.0.0.1
QID_PORT=8890
```
