The AuthSession cookie fix is shipped, but PouchDB longpoll requests get truncated at ~43s by Traefik on HTTP/2. HTTP/1.1 holds fine; CouchDB and nginx are clean. Captures the diagnosis trail and the three options for the next session so the work isn't re-derived.
5.3 KiB
Handoff: PouchDB longpoll truncates at ~43s (Traefik HTTP/2)
What's done — original cookie bug is fixed
The cross-origin AuthSession cookie bug from the previous handoff is fully resolved, deployed, and verified end-to-end.
- nginx sidecar fronts CouchDB (
nginx/Dockerfile,nginx/default.conf). proxy_cookie_flags AuthSession secure;addsSecureto the cookie that CouchDB itself refuses to mark (mochiweb only checks the raw socket scheme;x_forwarded_proto/x_forwarded_sslare ignored for cookie attribution in CouchDB 3.5).couch_httpd_auth.same_site = noneis now persisted viacouchdb-init(previously only set live; would vanish on container rebuild because/opt/couchdb/etc/local.d/is not in the data volume).- Coolify domain reassigned in UI from
couchdbtonginx:5984.couchdbis now internal-only.
Verified externally:
set-cookie: AuthSession=...; HttpOnly; SameSite=None; Secure
New problem (the one to solve)
_changes?feed=longpoll requests from PouchDB receive a truncated response:
{"results":[
<heartbeat newlines>
…then the connection is torn down before the closing ],"last_seq":"…"}. Browser surfaces SyntaxError: JSON.parse: unexpected end of data. PouchDB can't process replication.
Diagnosis (already done — don't repeat)
The cut happens at ~43s and is HTTP/2 specific. Tested every segment of the chain:
| Path | Result |
|---|---|
curl → couchdb directly (via docker exec on the couchdb container) |
Holds indefinitely; ^C to stop |
curl → nginx → couchdb (via docker run --network container:<nginx>) |
Holds indefinitely |
curl HTTP/2 → Traefik → nginx → couchdb |
Dies at 43.77s with HTTP/2 stream … INTERNAL_ERROR (err 2) |
curl --http1.1 → Traefik → nginx → couchdb |
Held for 2:05+ before user ^C'd |
⇒ Traefik is the source of the kill, and only for HTTP/2. CouchDB and nginx are clean.
The reproducer (cookie is from POST /_session saved to /tmp/c):
time curl -v --cookie /tmp/c \
'https://couch.apoena.dev/vaquant/_changes?feed=longpoll&since=now&heartbeat=10000&timeout=60000'
With --http1.1 the same request is fine.
43s does not match any documented Traefik default. It's a Coolify-shipped or Go-stdlib HTTP/2 server timer. Related but not identical to the regression in traefik/traefik#11986 — that one fires in milliseconds, not ~43s.
What's NOT the cause (already ruled out)
- Not CouchDB's
chttpd.changes_timeoutor any CouchDB knob — direct connection holds. - Not nginx —
proxy_read_timeout 1h,proxy_buffering off, and direct nginx→couchdb path holds. - Not the auth cookie — cookie is now valid through the whole chain.
- Not a flaky network — 43s is consistent across runs.
- Not the heartbeat — heartbeat newlines do flow through, they don't keep the H2 stream alive on Traefik's side.
Decision pending (asked the user, no answer yet)
Three options on the table:
A. Disable HTTP/2 on Coolify's HTTPS entrypoint (recommended).
Edit Coolify's Traefik static config, usually at /data/coolify/proxy/traefik.conf on the Coolify host. Add to the HTTPS entrypoint's TLS section, restrict ALPN to http/1.1 only — or set --entryPoints.https.http2.maxConcurrentStreams=0 or similar. Side effect: all HTTPS traffic on this Coolify server falls back to HTTP/1.1. For PouchDB's request pattern, this is fine; HTTP/2 multiplexing isn't load-bearing.
B. Find the specific Traefik HTTP/2 timeout knob. The 43s value is not in Traefik's documented defaults. Needs Coolify Traefik config inspection + trial. Higher effort, brittle.
C. Live with it. PouchDB does retry. Every ~43s of idle replication produces an error + reconnect. Functionally works, log noise + slight latency on resume of activity.
User leaned toward A but hasn't confirmed. Needs to know if they can edit Coolify's Traefik config.
Repo layout (relevant files)
docker-compose.yml— services:couchdb(internal),nginx(public via Traefik),couchdb-init(one-shot config),signup(Gleam service).nginx/Dockerfile+nginx/default.conf— nginx sidecar that addsSecurecookie flag.signup/— Gleam HTTP service for invite-gated user signup;manifest.tomlis committed (locked dep tree). Uses wisp 2.x, mist 6.x.README.md— explains the architecture (now including why nginx exists).- Coolify network UUID
lvw8efvnvsxduodrkg68zul3is hardcoded in three places (two labels + one network alias); documented in README.
Out of scope
- The frontend/PouchDB app lives in a separate repo (
vaquant). No changes needed there for the cookie fix; for option C (longpoll workaround at app level) the app would need adjusting. - CORS is correct, do not touch.
- The signup service has its own deploy story (built from
./signup), not relevant to this bug.
Verify the cookie fix is still working (sanity check before touching anything)
curl -i -X POST https://couch.apoena.dev/_session \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d "name=admin&password=$COUCHDB_PASSWORD" | grep -i set-cookie
Expect: ... HttpOnly; SameSite=None; Secure.
If Secure is missing, the Coolify domain assignment slipped off nginx and is hitting couchdb directly — re-assign in the Domains tab.