mirror of
https://github.com/PerpetualSoftware/pad.git
synced 2026-10-27 06:47:07 +00:00
95046c9b3b
* feat(server): gzip what the client accepts, except streams, upgrades, ranges and credential responses (TASK-2225) Audit C18: the Go server ignored Accept-Encoding entirely, so the items index (2.1 MB on a large workspace, 6.8x smaller gzipped), every JS chunk and every API poll went out uncompressed. Invisible on a LAN; most of a page's load time on a self-hosted WAN link. Pad Cloud already gets Brotli from Cloudflare, so this is the self-host win. CompressResponses wraps chi's Compress(5) (negotiation, Vary: Accept-Encoding, and compressible types only: text, JSON, JS, CSS, SVG; never images, archives or the export bundle) and skips: - long-lived streams: /api/v1/events* (SSE) and /mcp (streamable HTTP); - the collab WebSocket and any Upgrade request; - Range requests (a 206's Content-Range names identity offsets); - credential responses (/api/v1/auth/*, /oauth/*, */tokens): the BREACH shape, and small enough that skipping costs nothing. Measured on a throwaway instance: the largest JS chunk 1,456,216 -> 452,518 bytes (3.2x), index.html 6,444 -> 1,354. Tests: gzip for an accepting client (Vary set, body decodes) and identity otherwise; every skip; images untouched; an SSE stream still delivers a flushed event while open; wired into the real router (/api/v1/health). Mutants: a skip list that never skips, the middleware unwired: each killed. e2e on the built binary: the SSE and collab specs (14 pass). Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): compress reads only, so no minted secret is ever compressed (TASK-2225, codex r1) Every secret the API mints comes back from a write; write responses are small. The account export's flushes still deliver (chi's writer flushes gzip then the connection), now pinned by a test. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): a workspace claim code is never compressed (TASK-2225, codex r2) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): a HEAD keeps its Content-Length: compress GETs only (TASK-2225, codex r3) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): the members list is never compressed: it can carry a legacy invitation code (TASK-2225, codex r4) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): a share view's signed attachment references are never compressed (TASK-2225, codex r5 sweep) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * test(server): a census of every GET route, each classified compress or skip (TASK-2225, lead condition) The secret-bearing GETs compressSkips leaves alone are a deny list, and a deny list rots: the next GET route that returns a secret would be compressed silently. testdata/compress_get_routes.txt holds every GET route the router serves (149, 18 skipped) with its classification; TestCompressRouteCensus walks the router with chi.Walk and fails when a route is missing from the census, when the census names a route the router no longer serves, or when compressSkips disagrees with it. A new GET route therefore fails CI until someone decides whether its response can carry a secret and regenerates the file (PAD_UPDATE_COMPRESS_ROUTES=1), making the decision a reviewable diff. Checked: a dropped census line fails naming the route; dropping the claim-code skip fails naming the disagreement. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * fix(server): leave attachment bytes uncompressed (TASK-2225) CI on #1934 failed TestTask3401c_AStalledClientDoesNotOutliveTheDeadline in both Go jobs: the client got EOF before any headers. chi's compressor holds the headers until its first flush, and a 24 MiB file of repeated text compresses ~1000:1, so on a slower runner the 300ms route write deadline expired before a byte reached the wire. Attachment downloads are users' files streamed under that deadline; they now go out as written. The attachment list at /attachments still compresses. Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS * docs(server): state the deadline rule beside the attachment skip (TASK-2225) Claude-Session: https://claude.ai/code/session_01Q9WssCjFuHjSfLABoW4tmS