diff --git a/README.md b/README.md index bbee90d..a4298c8 100644 --- a/README.md +++ b/README.md @@ -144,9 +144,10 @@ it resets, read from the headers of its last response — and when the onion service was last reached over Tor, from the gateway's own ten-minute self-fetch. Thirty minutes without one turns `/healthz`'s `status` to `degraded` (still a 200: the clearnet site is serving). The server -also logs one JSON object per request on stdout — `docker logs -basicautomation-site | jq 'select(.status >= 400)'`, or `select(.via == "onion")` -for the visits that came over Tor — and warns once an hour for +also logs one JSON object per request on stdout, failed ones included, each +with the client's `ip`, `ua` and `ref` — `docker logs basicautomation-site | jq +'select(.status >= 400)'`, or `select(.via == "onion")` for the visits that +came over Tor — and warns once an hour for as long as it has been answering from the snapshot, and once when the GitHub quota runs out (`event: "upstream.rate_limited"`). Every other line the app writes is JSON too, with an `event` to select on — a GitHub call that failed and @@ -200,7 +201,7 @@ npm run cuts # cut card art down to its srcset widths (needs ImageMagick npm run bases # runtime Alpine still matches the node base (needs docker) npm run contrast # WCAG contrast for every palette colour, against the ground npm run sizes # re-read every image's intrinsic size -npm run audit:runtime # advisories for what .output/server ships (after a build) +npm run audit:runtime # advisories for what the build ships, server and client ``` `npm test` is Vitest over the pure functions in `shared/` — README rewriting, @@ -230,7 +231,8 @@ npm run font # re-cut the preloaded core of Fira Code actually served. The rules that need a layout engine are named and skipped rather than silently failing — jsdom has none — so this is "every axe rule that can be judged from markup". `npm run a11y:browser` runs those skipped rules — -touch-target size, keyboard access to scrollable regions, the viewport meta — +touch-target size, keyboard access to scrollable regions, the viewport meta, +whether each control's visible label is part of its accessible name — in headless Chromium at a desktop and a phone width (set `CHROME_PATH` if Chromium is not on `PATH`), reading each page to the bottom so lazy content loads, and fails on any console error, uncaught exception or CSP refusal the @@ -257,7 +259,9 @@ READMEs set — that every page preloads, and the full 113 KB font behind it by `npm run audit:runtime` (in CI, after the build) is `npm audit` narrowed to the code a visitor's request can reach: the packages Nitro traced into `.output/server/node_modules`, plus the ones it inlined into the server chunks, -read from their sourcemaps. It fails on a high or critical advisory that applies +read from their sourcemaps, plus every package in the client bundle, which the +build records as it writes the chunks (`.output/server/client-packages.json`; +the client has no sourcemaps to read). It fails on a high or critical advisory that applies to one of them. Plain `npm audit` reports the whole lockfile, so it also counts the build's file globbing and the dev server's certificate helper, and it can stay red on advisories nobody can patch. The onion gateway, the image's other diff --git a/ROADMAP.md b/ROADMAP.md index 765b607..76255b0 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -190,6 +190,13 @@ record. There is no run log. and `test/log-lines.test.ts` fails a `console` call in `server/` or `shared/` that does not write JSON. Nitro's own "Listening on" line is the one left, and is not this app's to change. +- [x] …and a failed request says who asked. The error-hook line — every 404 + and 500 — stopped at `via`, though its comment promised the success + line's shape, so the 627 404s in fifteen hours of live log (nearly all + scanners) were the only lines with no `ip`, `ua` or `ref`. Both lines + now take those fields from one `requester()` + (`server/plugins/request-log.ts`); still one line per request, HEAD + still logged as HEAD, onion requests still `ip: null`. - [x] `/api/posts?project=` is a 404, like `/api/projects/`, not an empty list. - [x] Answer HEAD wherever GET is answered. Nitro routes by filename suffix, so @@ -365,6 +372,21 @@ record. There is no run log. - [ ] Move the type checker to TypeScript 7 once 7.1 ships its API and vue-tsc adopts it — the 6.0 API is a bridge, not a destination. +- [ ] Move to Nuxt 4.6 once it has had a few days, or a 4.6.1, in the wild. + Published 2026-10-05 22:11 UTC: 420 commits and a CLI major, and its + security section matters here — the internal error route reachable from + outside, unhandled error data handed to the error page, error-render + recursion tracked by a client-controllable header. Trialled the same + night in a scratch worktree (`npm install nuxt@^4.6.0`, `npm dedupe`): typecheck, + 226 tests, build, `audit:runtime`, `check`, `a11y` and `a11y:browser` + all clean; 23 routes, error-route probes included, answer as on 4.5.2; + pages byte-identical in size; still one JSON log line per request. + The only visible-to-a-machine change: the CSS minifier rounds one line + height to 16 px where it was 15.98, so the home page drifts by ≤0.05 px + — no difference to the eye. Not landed because the run ends in an + unattended deploy of a release an hour old. Needs Node `^24.15.0`; the + build image has 24.21. + - [x] Silenced Nitro's own `[request error]` stack-trace block on a 404, without replacing the error handler. Nitro logs it when the error is `fatal`, and `fatal` is only load-bearing on the client, where it is what makes a 404 @@ -489,6 +511,16 @@ record. There is no run log. Proved in a browser: an injected `