* chore: bump Go toolchain to 1.26 (TASK-763)
Bump Go from 1.25 to 1.26 across all toolchain pins:
- go.mod — go 1.25.0 → go 1.26.0
- Dockerfile — golang:1.25-alpine → golang:1.26-alpine
- .github/workflows/ci.yml — three setup-go steps (Go, Go-Postgres, E2E jobs)
- .github/workflows/release.yml — release pipeline
No `toolchain` directive: the repo is pre-launch with no external
contributors yet, so we set the floor where we want it (hard requirement).
Verified locally before commit:
- golangci-lint v2.11.4 builds and runs under Go 1.26.2 (pinned in CI)
- golang:1.26-alpine and 1.26.2-alpine images present on Docker Hub
- go build ./... clean
- go vet ./... clean
- go test ./... all pass
Parent: PLAN-644 (OSS Repo Hygiene and Launch Polish).
* chore: gofmt -w under Go 1.26 (TASK-763)
Apply Go 1.26's gofmt to the codebase. ~41 files reformatted, all
struct-tag whitespace realignment — no semantic changes. Verified:
- gofmt -l ./cmd ./internal returns empty after
- go build ./... still clean
- go test ./... still passes (run before commit)
Bundling the gofmt diff with the toolchain bump in the same PR because
the formatting drift is a direct consequence of moving from 1.25 to
1.26; splitting them creates a mandatory two-PR ordering for no value.
Parent: PLAN-644.
* docs: bump documented Go floor to 1.26 (TASK-763)
Match go.mod's hard 1.26.0 requirement in the source-build instructions.
Caught by Codex review round 1 on PR #247.
- README.md:158 — "Go 1.25+" → "Go 1.26+"
- CONTRIBUTING.md:9 — "Go 1.25+" → "Go 1.26+"
* fix(server): cap JSON body + header size (TASK-663)
decodeJSON called json.NewDecoder(r.Body).Decode(v) with no size limit.
Any client could POST a multi-GB JSON blob and watch Pad stream the
whole thing into one allocation — a single request could OOM the
process.
- internal/server/server.go: wrap r.Body in http.MaxBytesReader(..., 2 MB)
inside decodeJSON. Every legitimate payload (item, collection, auth,
etc.) is well under 100 KB so 2 MB is several orders of magnitude
above real traffic. Factor out decodeJSONWithLimit(maxBytes) so
future bulk-import endpoints can opt in to a larger cap without
removing the wrapper.
- internal/server/server.go: set MaxHeaderBytes = 64 KiB on the
http.Server (default is 1 MB). Plenty for cookies/auth/CORS while
cheaply rejecting header-flood DoS.
Test: decode_json_test.go covers the 3 MiB body rejection, a happy
path, and a custom-limit override that rejects a 1 MiB body under a
256 KiB cap.
Parent: PLAN-643 (OSS Security Hardening).
* fix(server): bump workspace import JSON cap to 64 MiB per Codex P1
Codex flagged that handleImportWorkspace inherits the new 2 MiB default
cap, but WorkspaceExport contains full collections, items, comments,
and item_versions for the workspace — a realistic project backup
routinely exceeds 2 MiB, so existing exports stop re-importing.
Switch to decodeJSONWithLimit(64 << 20). 64 MiB is multiple orders of
magnitude above any realistic single-workspace backup while still far
from heap-exhaustion territory.