Commit Graph
10 Commits
Author SHA1 Message Date
clawbot f5aa81f024 DEBUG turns debug logging on for any non-empty value (closes #159)
check / check (push) Waiting to run
DEBUG was read with strconv.ParseBool, so a value such as "yes" stopped
the start. It now follows the code style guide: unset or empty is off,
and any other value, "false" and "0" included, turns debug logging on.
No value of DEBUG stops the start. The test that "yes" fails is replaced
by one covering unset, empty, "false", "0" and "yes". README.md no
longer gives false as the default, and says which values turn it on.

Model: opus-5-5
2026-10-19 05:48:20 +02:00
clawbot be6dc93eaa Unusable DATA_DIR error names DATA_DIR; report file number is atomic.Int64 (closes #148, closes #161)
check / check (push) Waiting to run
When the backend runs on its own, a DATA_DIR that could not be created
or read stopped the start with an error that named the path but not
DATA_DIR, and one where no file could be created let it start, to fail
at the first write of a report file. The start now creates a file in
DATA_DIR and removes it, and all three cases fail with
DATA_DIR "<value>": <reason>, the form config.New uses for the other
settings. Tests start the buffer with DATA_DIR below a regular file and
with DATA_DIR set to /proc.

The report file sequence counter is atomic.Int64 instead of
atomic.Uint64, as the Go style guide asks for int types. File names are
unchanged.

Model: opus-5-5
2026-10-19 03:15:35 +02:00
clawbot fd32f348b7 prepare-data-dir leaves files with a second name to their owner (closes #143)
check / check (push) Waiting to run
A hard link in /data shares its owner with a file that can be anywhere
on the same filesystem, so chowning it gave that file to the netwatch
user. A file with more than one name now keeps its owner and its path
is logged.

Owners are now set on the opened directory, file or link after
checking it, as chmodDir sets modes, so a hard link swapped in
afterwards is caught. Every other file is opened without following it;
a symbolic link is given to the netwatch user itself, and devices,
named pipes and sockets keep their owner. This is built only for
Linux: prepare-data-dir runs only in the Linux image, and macOS has
neither O_PATH nor AT_EMPTY_PATH; elsewhere it returns an error. A
failed owner change names the file.

Model: opus-5-5
2026-10-19 01:11:41 +02:00
clawbot 2532da55e9 Client address is the right-most untrusted X-Forwarded-For entry (closes #144)
check / check (push) Waiting to run
From a peer in TRUSTED_PROXIES, clientIP took the left-most
X-Forwarded-For entry, which the client writes itself behind a proxy
that appends to the header, so a client could choose the address the
rate limit counts. It now walks the entries from the right past trusted
proxies and takes the first that is not one. An entry with a port is
read as its address; with only trusted entries, or one that is not an
address at all, it uses the peer. Every X-Forwarded-For header line is
read, and X-Real-IP only when there is no X-Forwarded-For entry.

Model: opus-5-5
2026-10-19 00:28:18 +02:00
clawbot 2861be1c4d Stopping the server waits for report file writes under way (closes #122)
check / check (push) Canceled after 0s
The report buffer's stop now waits, through a sync.WaitGroup, for
flushLoop and for the write Append starts at 10 MiB before its final
flush. It used to return at once, so the process exiting cut such a
write off and its reports were lost.

A write under way that fails puts its reports back in the buffer, so
the final flush writes them; the stop fails only if that flush fails
too.

Tests hold the periodic write and the one at 10 MiB open in a synctest
bubble and find the stop waiting until each is released; a third makes
the held write fail and finds its report written by the final flush.

Model: opus-5-5
2026-10-14 14:47:01 +02:00
clawbot 2a47fb9a8e Unset TRUSTED_PROXIES trusts RFC 1918, empty trusts none (closes #129)
check / check (push) Canceled after 0s
The backend's own TRUSTED_PROXIES now follows the ruling for every app:
unset, it trusts 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16, and no
longer loopback; a list replaces that; set and empty, it trusts no
proxy.

viper reads an empty environment variable as unset, so an empty value
got the default. config.New now reads this one setting with
os.LookupEnv first. viper's AllowEmptyEnv would apply to every setting,
so an empty PORT or DEBUG would stop the start instead of counting as
unset, as README.md says it does.

bin/entrypoint.sh still starts the backend with
TRUSTED_PROXIES=127.0.0.1/32.

Model: opus-5-5
2026-10-14 14:01:37 +02:00
clawbot cff7b07bbc Reports refused while report files cannot be written (closes #123)
check / check (push) Canceled after 0s
A failed write of a report file used to drop its reports, and
POST /api/v1/reports kept answering 200 for as long as writes failed.
Now a failed write puts its reports back in the buffer, still counted
against DATA_DIR_MAX_BYTES, and Append refuses every report, which the
handler answers 500, until a write succeeds. The minute flush tries
the held reports again, so storing resumes with no restart once
writing works, and the buffer takes nothing new while writes fail.
Tests also cover a report appended while a write that fails runs, and
the cap once a failed write's reports are written.

Model: opus-5-5
2026-10-14 13:47:14 +02:00
clawbot e628dfe6cf A handler's panic is logged and counted as 500 (closes #125)
check / check (push) Canceled after 0s
Recoverer comes first, so it wrote the 500 only after the panic had
passed the request log and the metrics, which both recorded 200.
Write500OnPanic, placed after the request log and after the metrics,
writes the 500 as the panic passes and panics again, so Sentry and
Recoverer still get it. Recoverer now writes its 500 only when no
status was written, so the server logs no superfluous WriteHeader call.
The test also requires Recoverer's log line for the handler's panic, so
it fails if Write500OnPanic stops passing the panic on.

Model: opus-5-5
2026-10-14 13:30:11 +02:00
clawbot 73b39e485b Reports are taken only as application/json (closes #126)
check / check (push) Canceled after 0s
A page on another origin can POST text/plain or a form without a CORS
preflight, so its visitors' browsers could store reports although the
server sends no CORS headers. decodeJSON now refuses a request whose
Content-Type is not application/json (parameters such as charset
allowed), and the report handler answers it 415 with the usual error
body, storing nothing. A JSON POST waits for the preflight, which only
an origin in CORS_ALLOWED_ORIGINS passes; the page already sends
application/json.

Model: opus-5-5
2026-10-14 12:43:25 +02:00
clawbot 406e073aee Move to the layout REPO_POLICIES.md gives (closes #30)
check / check (push) Canceled after 0s
The Go module moves from backend/ to the root (go.mod, go.sum,
.golangci.yml, cmd/, internal/), the page's source to web/, and nginx's
two files to configs/. backend/ is gone: its LICENSE and .dockerignore
are deleted, and its .gitignore entries, README and scripts are folded
into the root ones. script/build builds the server binary after the
frontend; script/run and script/clean move up from backend/script/;
make test, lint, fmt and fmt-check do what the backend's did. Vite's
root is web/ and it still builds into dist/. go.mod ignores
node_modules/, whose npm packages can ship .go files. script/bootstrap
no longer installs gcc, which only the host Go tests needed.

Model: opus-5-5
2026-10-14 12:01:32 +02:00