2 Commits
Author SHA1 Message Date
sneak 9b39b865ca Make the HSTS max-age configurable with HSTS_MAX_AGE (closes #271)
check / check (push) Successful in 16m1s
HSTS_MAX_AGE, a Go duration, sets the max-age of the
Strict-Transport-Security header. Unset, it is ten years (87600h);
0 sends no header at all. A negative value, or one that is not a
whole number of seconds, stops startup like an unparseable one.
includeSubDomains and preload stay in the header. The value is shown
on /settings and in the startup log, and the README's HSTS section
now says what each part of the header commits the operator to.

Model: opus-5-5
2026-10-13 00:13:15 +00:00
clawbot 9199544999 Document that DATA_DIR must be on a local filesystem (closes #295)
check / check (push) Successful in 12m55s
SQLite in WAL mode maps each database's -shm file into memory, and
network filesystems do not support that reliably, so the README now
says DATA_DIR must be local and names NFS and SMB/CIFS as unsupported.
It also says what a disk I/O error with code 522 points at and what to
try first. ZFS is not called unsupported: the one failure seen there
was never reproduced.

Model: opus-5-5
2026-10-13 01:29:42 +02:00
+15 -7
View File
@@ -895,9 +895,10 @@ Strict-Transport-Security: max-age=315360000; includeSubDomains; preload
``` ```
`HSTS_MAX_AGE` sets `max-age`, in seconds. `includeSubDomains` and `preload` are `HSTS_MAX_AGE` sets `max-age`, in seconds. `includeSubDomains` and `preload` are
always part of the header. A browser that sees the header refuses plaintext HTTP always part of the header. A browser that receives the header over HTTPS refuses
to that hostname until `max-age` runs out, whatever else is served there, so plaintext HTTP to that hostname until `max-age` runs out, whatever else is
terminate TLS on a hostname you are prepared to keep on HTTPS. served there, so terminate TLS on a hostname you are prepared to keep on HTTPS.
Browsers ignore the header when it arrives over plain HTTP.
`includeSubDomains` extends this to every subdomain of the hostname webhooker is `includeSubDomains` extends this to every subdomain of the hostname webhooker is
served at. Served at `hooks.example.com`, it covers every name under served at. Served at `hooks.example.com`, it covers every name under
@@ -911,10 +912,10 @@ every browser that ships the list, even for users who have never visited it, and
removal takes months to reach those browsers, so treat a listing as permanent. removal takes months to reach those browsers, so treat a listing as permanent.
Setting `HSTS_MAX_AGE=0` later stops the header being sent, but a browser that Setting `HSTS_MAX_AGE=0` later stops the header being sent, but a browser that
already saw it does not forget it. That browser keeps refusing plaintext HTTP already received it over HTTPS does not forget it. That browser keeps refusing
until the `max-age` it saw runs out. Only a `max-age=0` header received over plaintext HTTP until the `max-age` it saw runs out. Only a `max-age=0` header
HTTPS clears it sooner. webhooker never sends one; a reverse proxy in front of received over HTTPS clears it sooner. webhooker never sends one; a reverse proxy
it can. in front of it can.
## Backup, Restore, and Upgrades ## Backup, Restore, and Upgrades
@@ -963,6 +964,13 @@ with an error naming it: for `webhooker.db` the server and `webhooker resetpw`
stop, and an event or archive database fails as a damaged one does (see stop, and an event or archive database fails as a damaged one does (see
[Database Architecture](#database-architecture)). [Database Architecture](#database-architecture)).
**`DATA_DIR` must be on a local filesystem.** Under WAL, SQLite maps each
database's `-shm` file into memory, which network filesystems (NFS, SMB/CIFS) do
not support reliably, so they are not supported. webhooker does not check which
filesystem `DATA_DIR` is on. An error reading `disk I/O error (522)`
(`SQLITE_IOERR_SHORT_READ`) points at the storage under `DATA_DIR`, not at
locking; moving `DATA_DIR` to a plain local disk is the first thing to try.
Configuration is **not** in `DATA_DIR` — it comes from the environment and from Configuration is **not** in `DATA_DIR` — it comes from the environment and from
a `.env` file read out of the process working directory. Back that up with your a `.env` file read out of the process working directory. Back that up with your
deployment config, separately. deployment config, separately.