check / check (push) Successful in 1m20s
The image's HEALTHCHECK requests /.well-known/healthcheck through nginx on the port from PORT, so it fails unless both processes answer. The backend reads PORT and DEBUG with strconv instead of viper, which turned a bad PORT into 0 and a bad DEBUG into false. Those, and a BIND_ADDRESS that is not an IP address, now stop the start with an error naming the variable; the TRUSTED_PROXIES error names it too. README.md gains "Running under upaas". Its first-run steps create the host directory owned by uid 1000, so the image changes no ownership. Model: opus-5-5
11 KiB
11 KiB
Workflow
- branch (from
main) - do the work in Next Step
- move Next Step to the top of Completed Steps
- move the top item of Future Steps into Next Step
- commit (
TODO.mdchanges in the same commit as the work) - merge to
mainif the branch is not protected, otherwise open a PR - push
Status
pre-1.0. No git tags. feat/reportbuf-storage is merged; the backend, the CI
workflow, and the backend repo standard files are all on main. Frontend and
backend are both functional. Working toward the 1.0.0 milestone by closing the
remaining repo-compliance issues on the tracker.
Next Step
Confirm the .gitea/workflows/check.yml run is green (main always green
policy). The workflow file is already on main; what is unverified is that its
latest run passes.
Completed Steps
- 2026-09-29: ready to run under upaas (issue #59): the image has a
HEALTHCHECKthat requests/.well-known/healthcheckthrough nginx on the port fromPORT. The backend no longer reads a badPORTas 0 or a badDEBUGas false: those, and aBIND_ADDRESSthat is not an IP address, stop it from starting with an error naming the variable, as the limits,CORS_ALLOWED_ORIGINSand, now by name,TRUSTED_PROXIESalready did.README.mdhas a "Running under upaas" section, whose first-run steps create the host directory for/dataowned by uid 1000; the image does not change its owner - 2026-09-29: nginx listens on
PORT(issue #26), 8080 when unset or empty: the nginx image rendersnginx.confas a template at container start, filling inPORTand no other variable.bin/entrypoint.shrefuses to start whenPORTis not digits only.server_tokens offkeeps the nginx version out of responses.script/frontend-viewport-testrenders the template the same way. Gzip and a50x.htmlerror page are not added - 2026-09-29: bounded the report endpoint (issue #20):
POST /api/v1/reportsstill needs no credentials, but each client address, as resolved throughTRUSTED_PROXIES, may sendREPORTS_PER_MINUTE(default 60) reports a minute, counted bygo-chi/httprate, and past that gets 429 withRetry-After; the report files inDATA_DIR, counted from start with those already there, may total at mostDATA_DIR_MAX_BYTES(default 1 GiB), past which reports get 507; and the wildcard CORS is gone: no CORS headers unlessCORS_ALLOWED_ORIGINSlists origins, and an entry that is not a plainscheme://host[:port]origin,*included, stops the server from starting. Deleting report files frees room only at the next start; pruning is issue #54 - 2026-09-28: one container image (issue #52): the root
Dockerfilebuilds the only image, andDockerfile.backendis gone. nginx serves the frontend on port 8080 and proxies/api/and/.well-known/healthcheckto the backend, which listens on127.0.0.1:8081in the same container; the newBIND_ADDRESSsetting sets its listen address.bin/entrypoint.shstarts both, passes TERM and INT on to both, and exits non-zero when either exits on its own. The backend runs as usernetwatchand stores reports on the/datavolume.script/dockeris the org model again - 2026-09-28: unified the gate (issue #16): the root
make checkcovers the Go backend as well as the frontend, and the pre-commit hook with it; the backend moved onto scripts-to-rule-them-all (backend/script/*,backend/Makefileas shims, its duplicate hook installer removed);script/cibuildbuilds both images and is the workflow's only build step. The rootmake lintruns golangci-lint only in Docker, by building the lint stage ofDockerfile.backendwithout the cache.script/bootstrapinstalls the pinned Go unless the installed one is at least whatbackend/go.modasks for, links what it installs into~/.local/binwithout replacing anything it did not create, and installs no linter. Rootmake testruns both halves within one 30-second timeout. WhenVERSIONis unset or empty, the backend binary's version falls back togit describeinside a git checkout, then todev - 2026-09-28: frontend reporting client (issue #53): a
Reporterclass posts collected samples to/api/v1/reportseveryreportInterval(default 60s) as a per-host delta, with the report-building step a pure exported function of host state; a per-host mark advances only on a delivered POST; at most one report POST is pending at a time and it is abandoned after half the interval, so a slow POST never overlaps the next report and a mark never moves backwards; the samples of an abandoned POST are sent again at the next interval, so a backend that stored them but answered late receives them twice; the per-browser client id works in insecure (plain-HTTP) contexts;vite.config.jsproxies/apito the local backend foryarn dev - 2026-09-28: report ingest correctness (issue #23): a storage failure now
returns 500 instead of a false
ok; oversize bodies return 413 (distinguished from malformed JSON, which stays 400); aMaxBodyBytesmiddleware caps every route, not just the report route; the raw attacker-controlledgeoblob is no longer logged (only its length), andclient_id,timestampand decode error text are length-bounded before logging; adecodeJSONhandler helper was added; panic recovery now routes the stack through slog instead of chi's plain-text stderr; and writing a report file now returns its error, so a failed final flush on shutdown makes the process exit non-zero instead of losing the buffered reports silently - 2026-09-21: shutdown lifecycle correctness. The process now shuts down through
fx instead of
os.Exit, so every component'sOnStopruns and buffered reports are flushed to disk onSIGTERM— previously a full flush window of telemetry was silently lost on every restart. Thehttp.Serveris now built before its serving goroutine starts, so shutdown can no longer race or nil-deref it; a listen failure exits non-zero viafx.Shutdowner;reportbufOnStopis idempotent; andwriteTimeoutnow exceeds the chi per-request budget so that budget is actually reachable. DeadstartupTime,exitCode, andcancelFuncfields were removed - 2026-09-21: backend HTTP hardening (issue #19): added
ReadHeaderTimeoutandIdleTimeoutto the server, aSecurityHeadersmiddleware (HSTS, tight CSP, frame/sniff/referrer/permissions headers) registered before CORS, and trusted-proxy client IP resolution honouringX-Forwarded-For/X-Real-IPonly from aTRUSTED_PROXIESallowlist (loopback plus RFC1918 by default) - 2026-08-10: adopted the org-standard
backend/.golangci.ymlverbatim and moved the pinned golangci-lint from v2.7.2 to v2.12.2 (thelintstage ofDockerfile.backendnow pins thegolangci/golangci-lint:v2.12.2image by digest); the previous config declaredversion: "2"but used v1 schema keys, so every threshold in it was inert and its green result was meaningless.backend/Makefile'slinttarget now asserts the config's sha256 against the canonical file first, so drift from the org standard fails the build instead of silently degrading to defaults - 2026-08-10: every interactive control now meets the 44x44 CSS px minimum tap
target (
.pin-btn,#interval-select, the debug-log label and, on narrow viewports,#pause-btn). The pin button's hit area grows via matching negative margins, so its layout footprint and row density are unchanged - 2026-08-10: per-host status line wraps below the 768px breakpoint instead of forcing horizontal page scroll at 320px
- 2026-08-09:
Dockerfile.backendreworked to the mandated Go multistage lint-stage pattern: separatelintstage on the hash-pinnedgolangci/golangci-lintimage,COPY --from=lintstage dependency,CGO_ENABLED=0static build driven byARG VERSION, and no moreCOPY .git - 2026-08-09: dotfile compliance — lifted
backend/.editorconfigto the repo root soroot = truecovers the frontend too, and replaced.gitignorewith the org model (OS, editor, node, and environment/secrets sections) plus this repo'sdist/and*.log..env,.env.*,*.pem, and*.keyare now ignored repo-wide, not just underbackend/. Excluding.gitfrom.dockerignorestays deferred: both images read git metadata at build time (COPY .gitinDockerfile.backend,git rev-parseinvite.config.js) - 2026-08-09: automated responsive-layout harness
(
make frontend-viewport-test): digest-pinned headless Chrome driven over CDP against the builtdist/, viewport widths derived from the breakpoints insrc/styles.css(#13). Every check carries a presence guard so none of them can pass against a page it is not actually measuring. Found two real layout defects, filed as #42 and #43 - 2026-07-07 Adopted scripts-to-rule-them-all:
script/entrypoints, Makefile shims, README Entrypoints section - 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow and backend repo standard files; backend Dockerfile fixed (Go 1.25, golangci-lint) and moved to repo root (feat/reportbuf-storage)
- 2026-02-26: host row layout redesigned with CSS grid; overflow and spacing fixes; nginx config extracted; port hardcoded to 8080
- 2026-02-26: debug log panel, median stats, recovery probe, Docker build fix, S3 Singapore endpoint added
- 2026-02-23: summary box redesign, host pinning, local and UTC clocks, checks counter
- 2026-02-23: hosts sorted by latency; GET instead of HEAD for latency; timeout derived from interval; Hetzner regional endpoints; 3s interval
- 2026-01-29: initial NetWatch network latency monitor
Future Steps
- Wire
script/frontend-viewport-testinto CI as its own step (deliberately not part ofmake checktoday; the decision has real CI-runtime cost and is tracked separately) - Compliance top-up as one small commit: add .editorconfig and add the hooks target to the Makefile
- After merge, confirm .gitea/workflows/check.yml is on main and CI is green (main always green policy)
- Decide what to do with untracked resume.sh: commit it, gitignore it, or delete it
- Upstream fix needed in
sneak/prompts: the org-standard.golangci.ymlenablesgomodguard, which golangci-lint v2.12.2 reports as deprecated since v2.12.0 and replaced bygomodguard_v2, so every backend lint run prints a deprecation warning. The file is standardized and must never be edited in this repo, so nothing can be done here beyond tracking it — tracked at https://git.eeqj.de/sneak/netwatch/issues/41