evidence: P1-fixes spikes A1/B1/E1/E2 (2026-09-15; secrets redacted)
gates / gates (push) Successful in 18s

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-15 10:06:43 +02:00
parent d0d0328671
commit 31a913c80f
4 changed files with 290 additions and 0 deletions
@@ -0,0 +1,25 @@
are supported and installed on your system.
+ docker rm -f a1spike-us a1spike-al
+ docker run -d --name a1spike-us --restart unless-stopped alpine:3 sleep 100000
Unable to find image 'alpine:3' locally
3: Pulling from library/alpine
Digest: sha256:28bd5fe8b56d1bd048e5babf5b10710ebe0bae67db86916198a6eec434943f8b
Status: Downloaded newer image for alpine:3
++ docker inspect -f '{{.Config.Image}}' a1spike-us
+ docker run -d --name a1spike-al --restart always alpine:3 sleep 100000
+ date -u +%FT%TZ
2026-09-15T07:35:50Z
+ docker kill a1spike-us a1spike-al
a1spike-us
a1spike-al
+ sleep 60
+ date -u +%FT%TZ
2026-09-15T07:36:50Z
+ docker inspect -f '{{.Name}} policy={{.HostConfig.RestartPolicy.Name}} status={{.State.Status}} exit={{.State.ExitCode}} restarts={{.RestartCount}}' a1spike-us a1spike-al
/a1spike-us policy=unless-stopped status=exited exit=137 restarts=0
/a1spike-al policy=always status=exited exit=137 restarts=0
+ docker version --format '{{.Server.Version}}'
29.8.0
+ docker rm -f a1spike-us a1spike-al
a1spike-us
a1spike-al
@@ -0,0 +1,135 @@
# B.1 FileBrowser Quantum admin-password spike — 2026-09-15, scratch LXC 9202 on demo-hp
# Throwaway containers fbspike-fresh / fbspike-existing on 127.0.0.1:18089 inside the guest; config rendered like RenderFileBrowserConfig; same entrypoint wrapper as RenderFileBrowserCompose.
# Generated passwords redacted: only length printed. Login probe = POST /api/auth/login?username=admin, header X-Password.
# 'tr: write error: Broken pipe' lines are the password generator (harmless).
2026-09-15T07:36:38Z
image: gtstef/filebrowser:1.3.3-stable
sha256:095fd20d87be10c175a5fa614aa1d0a94aa07eafa80f251a3069640e0a7c51f7
== A1 FRESH db, config key auth.adminPassword
tr: write error: Broken pipe
pw len=16
admin/admin -> 401
admin/<P1> -> 200
admin/wrong -> 401
2026/09/15 07:36:39 [INFO ] Auth Methods : [password]
2026/09/15 07:36:39 [INFO ] Resetting admin user to default username and password.
2026/09/15 07:36:40 POST | 401 | 172.17.0.1 | N/A | 72ms | "/api/auth/login?username=admin"
2026/09/15 07:36:40 POST | 200 | 172.17.0.1 | N/A | 80ms | "/api/auth/login?username=admin"
2026/09/15 07:36:40 POST | 401 | 172.17.0.1 | N/A | 63ms | "/api/auth/login?username=admin"
== A2 FRESH db, env FILEBROWSER_ADMIN_PASSWORD (no config key)
tr: write error: Broken pipe
pw len=16
admin/admin -> 401
admin/<P2> -> 200
== A3 FRESH db, NO key, NO env (today's controller shape)
admin/admin -> 200
admin/wrong -> 401
== A4 short password via config key (length rule)
(not ready)
2026/09/15 07:36:45 [INFO ] cache directory setup successfully: tmp
2026/09/15 07:36:45 [FATAL] store.Users.Save: password must be at least 5 characters long
2026/09/15 07:36:45 [DEBUG] Default SQLite driver initialized
2026/09/15 07:36:45 [WARN ] database file could not be found. If this is unexpected, please set the FILEBROWSER_DATABASE environment variable to the correct path.
admin/abc -> 000
admin/admin -> 000
2026/09/15 07:36:45 [FATAL] store.Users.Save: password must be at least 5 characters long
== B0 EXISTING db: create with defaults
admin/admin -> 200 (baseline, expect 200)
== B1 EXISTING db + config key added, restart
tr: write error: Broken pipe
pw len=16
admin/admin -> 401
admin/<P3> -> 200
== B2 EXISTING db + env var, restart (config key removed)
tr: write error: Broken pipe
admin/admin -> 401
admin/<P3> -> 401
admin/<P3E env> -> 200
== B3 EXISTING db: CLI 'set -u admin,<pw>' with container stopped
tr: write error: Broken pipe
2026/09/15 07:37:51 [DEBUG] Default SQLite driver initialized
2026/09/15 07:37:51 [INFO ] cache directory setup successfully: tmp
successfully updated user: admin
rc=0
admin/admin -> 401
admin/<P3> -> 401
admin/<P3E> -> 401
admin/<P4> -> 200
== B4 EXISTING db: CLI 'set' via docker exec on RUNNING container
tr: write error: Broken pipe
2026/09/15 07:37:53 [DEBUG] Default SQLite driver initialized
2026/09/15 07:37:54 [INFO ] cache directory setup successfully: tmp
2026/09/15 07:37:54 the database is locked, please close all other instances of filebrowser before starting.
admin/<P4> -> 200
admin/<P5> -> 401
after restart: admin/<P4> -> 200
admin/<P5> -> 401
== C OVERWRITE: hand-set pw (current db state) + config key with a DIFFERENT value, restart
(db currently holds whichever of P4/P5 answered 200 above)
tr: write error: Broken pipe
admin/<P4> -> 401
admin/<P5> -> 401
admin/<P6 config> -> 200
admin/admin -> 401
== C2 same via env var with a DIFFERENT value
tr: write error: Broken pipe
admin/<P5> -> 401
admin/<P6> -> 401
admin/<P7 env> -> 200
== TEARDOWN
0
ls: cannot access '/tmp/fbspike': No such file or directory
filebrowser Up 35 hours (healthy)
2026-09-15T07:38:07Z
######## PART 2 — REST API change attempt WITHOUT X-Password (refused), restart survival
2026-09-15T07:38:40Z
== D0 existing db, defaults
admin/admin -> 200
token len=355
GET /api/users?id=self -> {"editorQuickSave":false,"hideSidebarFileActions":false,"disableQuickToggles":false,"disableSearchOptions":false,"deleteWithoutConfirming":false,"preview":{"disableHideSidebar":false,"image":true,"video":true,"audio":true,"motionVideoPreview":true,"office":true,"popup":true,"autoplayMedia":true,"defaultMediaPlayer":false,"folder":true,"models":true},"stickySidebar":true,"darkMode":true,"locale":"e
new pw len=13
PUT form1 /api/users?id=1 -> 401 {"status":401,"message":"X-Password header is required to confirm your password"}
admin/admin -> 200 admin/<new> -> 401
PUT form2 /api/users?id=1 -> 401 {"status":401,"message":"X-Password header is required to confirm your password"}
admin/admin -> 200 admin/<new> -> 401
PUT form3 /api/users?id=self -> 400 {"status":400,"message":"no user not found, please provide a valid id or username"}
admin/admin -> 200 admin/<new> -> 401
== D1 restart with NO key, NO env: does the API-set password survive?
admin/admin -> 200 admin/<new> -> 401
2026/09/15 07:38:40 [INFO ] Resetting admin user to default username and password.
== TEARDOWN
0
ls: cannot access '/tmp/fbspike2': No such file or directory
filebrowser Up 35 hours (healthy)
2026-09-15T07:38:44Z
######## PART 3 — REST API change WITH X-Password: <current>, restart with no key, then key with different value
# (the 'Resetting admin user' line under D1 is from the container's FIRST start — docker logs keeps history across restart; the measured logins show no reset)
2026-09-15T07:39:00Z
== D0 existing db, defaults
admin/admin -> 200
token len=355
GET /api/users?id=self -> {"editorQuickSave":false,"hideSidebarFileActions":false,"disableQuickToggles":false,"disableSearchOptions":false,"deleteWithoutConfirming":false,"preview":{"disableHideSidebar":false,"image":true,"video":true,"audio":true,"motionVideoPreview":true,"office":true,"popup":true,"autoplayMedia":true,"defaultMediaPlayer":false,"folder":true,"models":true},"stickySidebar":true,"darkMode":true,"locale":"e
new pw len=16
PUT form1 /api/users?id=1 -> 204
admin/admin -> 401 admin/<new> -> 200
== D1 restart with NO key, NO env (hand-set via API): does the API-set password survive?
admin/admin -> 401 admin/<new> -> 200
2026/09/15 07:39:01 [INFO ] Resetting admin user to default username and password.
== D2 same db, now add config key with DIFFERENT value, restart (operator hand-set vs key)
key pw len=16
admin/<hand-set> -> 401 admin/<key> -> 200 admin/admin -> 401
== TEARDOWN
0
ls: cannot access '/tmp/fbspike2': No such file or directory
filebrowser Up 35 hours (healthy)
2026-09-15T07:39:06Z
######## SUMMARY (measured, image gtstef/filebrowser:1.3.3-stable)
(a) FRESH db: config.yaml `auth: adminPassword: <pw>` (A1) OR env FILEBROWSER_ADMIN_PASSWORD (A2) sets it at first start: admin/admin 401, <pw> 200. No key/no env (today's shape, A3): admin/admin 200.
(b) EXISTING db (admin/admin 200 proven): the SAME config key (B1) or env var (B2) re-applies on restart: admin/admin 401, new 200. CLI `filebrowser set -u admin,<pw> -a` works only with the container STOPPED (B3: 200); on a running container it fails "database is locked" (B4, no change). REST: PUT /api/users?id=1 {"which":["password"],"data":{"id":1,"username":"admin","password":"<pw>"}} with Bearer token AND header X-Password: <current password> -> 204 (D0); without X-Password -> 401 "X-Password header is required".
OVERWRITE: with the key (C, D2) or env (C2) present, EVERY start resets the admin password to the configured value — a hand-set password (CLI or API) is overwritten (hand-set 401, key 200). With NO key/env, a hand-set password survives restart (B3->B4 restart, D1).
Length rule: min 5 chars; a shorter configured value is FATAL at start ("store.Users.Save: password must be at least 5 characters long", container never serves — A4). 16-char alphanumeric accepted.
TEARDOWN: fbspike-* containers removed, /tmp/fbspike* removed in 9202 (0 fbspike containers listed); 9202's own `filebrowser` untouched (Up 35 hours throughout).
@@ -0,0 +1,61 @@
E.1 Vaultwarden invite-without-SMTP spike — 2026-09-15 ~07:35-07:37Z, demo-hp scratch LXC 9202
Container vwspike: vaultwarden/server:1.36.0-alpine (catalog image), env as catalog template with SIGNUPS_ALLOWED=false,
ADMIN_TOKEN=<hex, 64 chars>, _ENABLE_SMTP=false + SMTP_* empty (catalog default when app-email is off), 127.0.0.1:18480, data /tmp/vwspike.
Admin page defaults observed: invitations_allowed CHECKED, signups_verify unchecked, signups_allowed unchecked, _enable_smtp unchecked.
=== /api/config (features) ===
{"environment":{"api":"http://127.0.0.1:18480/api","cloudRegion":null,"identity":"http://127.0.0.1:18480/identity","notifications":"http://127.0.0.1:18480/notifications","sso":"","vault":"http://127.0.0.1:18480"},"featureStates":{"pm-19148-innovation-archive":true},"gitHash":"f21a3ada","object":"config","push":{"pushTechnology":0,"vapidPublicKey":null},"server":{"name":"Vaultwarden","url":"https://github.com/dani-garcia/vaultwarden"},"settings":{"disableUserRegistration":false},"version":"2025.12.0"}
=== 1. stranger registration ===
--- POST /identity/accounts/register email=idegen.probe@example.com
{"message":"Registration not allowed or user already exists","validationErrors":{"":["Registration not allowed or user already exists"]},"errorModel":{"message":"Registration not allowed or user already exists","object":"error"},"error":"","error_description":"","exceptionMessage":null,"exceptionStackTrace":null,"innerExceptionMessage":null,"object":"error"}
HTTP 400
--- POST /api/accounts/register email=idegen.probe@example.com
{"error":{"code":404,"description":"The requested resource could not be found.","reason":"Not Found"}}
HTTP 404
=== 2. admin login + invite ===
POST /admin login HTTP 200
{"_status":1,"avatarColor":null,"creationDate":"2026-09-15T07:36:18.177612Z","culture":"en-US","email":"meghivott.probe@example.com","emailVerified":true,"forcePasswordReset":false,"id":"8dbdfec6-2cfb-4794-9156-5a67cb27b57b","key":"","name":"meghivott.probe@example.com","object":"profile","organizations":[],"premium":true,"premiumFromOrganization":false,"privateKey":null,"providerOrganizations":[],"providers":[],"securityStamp":"4f4d1ee9-1295-4263-8fc2-96cb79236f86","twoFactorEnabled":false,"usesKeyConnector":false}
POST /admin/invite HTTP 200
[{"_status":1,"avatarColor":null,"createdAt":"2026-09-15 09:36:18 CEST","creationDate":"2026-09-15T07:36:18.177612Z","culture":"en-US","email":"meghivott.probe@example.com","emailVerified":true,"forcePasswordReset":false,"id":"8dbdfec6-2cfb-4794-9156-5a67cb27b57b","key":"","lastActive":null,"name":"meghivott.probe@example.com","object":"profile","organizations":[],"premium":true,"premiumFromOrganization":false,"privateKey":null,"providerOrganizations":[],"providers":[],"securityStamp":"4f4d1ee9-1295-4263-8fc2-96cb79236f86","twoFactorEnabled":false,"userEnabled":true,"usesKeyConnector":false}]
=== 3. invited registration ===
--- POST /identity/accounts/register email=meghivott.probe@example.com
{"captchaBypassToken":"","object":"register"}
HTTP 200
--- POST /api/accounts/register email=meghivott.probe@example.com
{"error":{"code":404,"description":"The requested resource could not be found.","reason":"Not Found"}}
HTTP 404
=== control: second stranger after invite ===
--- POST /identity/accounts/register email=idegen2.probe@example.com
{"message":"Registration not allowed or user already exists","validationErrors":{"":["Registration not allowed or user already exists"]},"errorModel":{"message":"Registration not allowed or user already exists","object":"error"},"error":"","error_description":"","exceptionMessage":null,"exceptionStackTrace":null,"innerExceptionMessage":null,"object":"error"}
HTTP 400
=== admin users after ===
[{"_status":0,"avatarColor":null,"createdAt":"2026-09-15 09:36:18 CEST","creationDate":"2026-09-15T07:36:18.177612Z","culture":"en-US","email":"meghivott.probe@example.com","emailVerified":true,"forcePasswordReset":false,"id":"8dbdfec6-2cfb-4794-9156-5a67cb27b57b","key":"2.AAAAAAAAAAAAAAAAAAAAAA==|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","lastActive":null,"name":"probe","object":"profile","organizations":[],"premium":true,"premiumFromOrganization":false,"privateKey":"2.AAAAAAAAAAAAAAAAAAAAAA==|AAAA|AAAA","providerOrganizations":[],"providers":[],"securityStamp":"032fbc2f-aa0e-4e0f-8390-485f3077493c","twoFactorEnabled":false,"userEnabled":true,"usesKeyConnector":false}]
=== config defaults (admin) ===
name="signups_allowed"
name="signups_verify"
name="invitations_allowed" checked
name="_enable_smtp"
=== newer web-vault flow (send-verification-email) ===
--- POST /identity/accounts/register/send-verification-email email=idegen3.probe@example.com (stranger)
{"message":"Registration not allowed or user already exists","validationErrors":{"":["Registration not allowed or user already exists"]},"errorModel":{"message":"Registration not allowed or user already exists","object":"error"},"error":"","error_description":"","exceptionMessage":null,"exceptionStackTrace":null,"innerExceptionMessage":null,"object":"error"}
HTTP 400
invite meghivott2 HTTP 200
--- POST /identity/accounts/register/send-verification-email email=meghivott2 (invited)
"<register_verify JWT redacted, returned inline because SMTP is off>
--- web vault register route present:
GET / HTTP 200
[2026-09-15 09:36:34.927][response][INFO] (register_verification_email) POST /identity/accounts/register/send-verification-email => 400 Bad Request
[2026-09-15 09:36:34.967][request][INFO] POST /admin/invite
[2026-09-15 09:36:34.968][response][INFO] (invite_user) POST /admin/invite application/json => 200 OK
[2026-09-15 09:36:34.981][request][INFO] POST /identity/accounts/register/send-verification-email
[2026-09-15 09:36:34.984][response][INFO] (register_verification_email) POST /identity/accounts/register/send-verification-email => 200 OK
Teardown: docker rm -f vwspike; rm -rf /tmp/vwspike /tmp/vw.sh /tmp/vw2.sh /tmp/vwtok /tmp/vwjar /tmp/vwjar2 /tmp/vwreg.json in 9202; image vaultwarden/server:1.36.0-alpine left pulled in 9202 (scratch).
@@ -0,0 +1,69 @@
E.2 — Paperless-ngx memory need, 20-document batch, PAPERLESS_TASK_WORKERS=1 x THREADS_PER_WORKER=1
Measured 2026-09-15 on scratch LXC 9202 (demo-hp). Guest 9201 not touched.
Setup: catalog templates/paperless-ngx/docker-compose.yml (app-catalog 882a43e) rendered as compose project
`plspike` (containers plspike-*), traefik labels/network dropped, webserver on 127.0.0.1:18765,
image ghcr.io/paperless-ngx/paperless-ngx:2.20.15, postgres:16-alpine (256M cap), redis:7-alpine (128M cap),
webserver cap raised to 2G so the peak is measured, not capped. Generated values filled by hand (redacted).
Input: 20 distinct 3-page PDFs (text-layer Helvetica, ~40 lines/page, distinct md5), posted concurrently
via POST /api/documents/post_document/ (token auth; token redacted, length 40).
Catalog today: paperless-webserver memory 768M (805306368 B), WORKERS=2, THREADS=1; postgres 256M; redis 128M
(each container has its own cap).
--- run output (verbatim) ---
cgroup: /sys/fs/cgroup/system.slice/docker-f4d92829277069229eb95b69a4696c509d779157bc96a799f571c8b13098795f.scope
limit memory.max: 2147483648
startup memory.peak (before upload): 624263168
memory.current before upload: 618651648
token length: 40
upload start 2026-09-15T07:38:32Z
200 doc05.pdf
200 doc08.pdf
200 doc02.pdf
200 doc03.pdf
200 doc12.pdf
200 doc09.pdf
200 doc07.pdf
200 doc18.pdf
200 doc04.pdf
200 doc01.pdf
200 doc06.pdf
200 doc15.pdf
200 doc20.pdf
200 doc11.pdf
200 doc13.pdf
200 doc14.pdf
200 doc19.pdf
200 doc17.pdf
200 doc10.pdf
200 doc16.pdf
07:38:38 tasks {'PENDING': 18, 'SUCCESS': 2}
07:38:48 tasks {'PENDING': 14, 'SUCCESS': 5, 'STARTED': 1}
07:38:58 tasks {'PENDING': 10, 'STARTED': 1, 'SUCCESS': 9}
07:39:08 tasks {'PENDING': 6, 'SUCCESS': 13, 'STARTED': 1}
07:39:19 tasks {'PENDING': 3, 'STARTED': 1, 'SUCCESS': 16}
07:39:29 tasks {'SUCCESS': 20}
upload end 2026-09-15T07:39:30Z
documents count: 20
final tasks: 20 {'SUCCESS': 20}
memory.peak after batch: 783294464
max sampled memory.current during batch (0.5s): 776806400
memory.events: low 0 high 0 max 0 oom 0 oom_kill 0 oom_group_kill 0 sock_throttled 0
plspike-webserver oomkilled=false restarts=0 status=running memlimit=2147483648
plspike-postgres oomkilled=false restarts=0 status=running memlimit=268435456
plspike-redis oomkilled=false restarts=0 status=running memlimit=134217728
--- computation ---
memory.peak (whole container lifetime incl. startup + batch) = 783294464 B (747.0 MiB)
startup-only peak before upload = 624263168 B (595.3 MiB)
cap = peak x 1.5 = 1174941696 B (1120.5 MiB) -> rounded UP to a 256M multiple = 1280M (1342177280 B)
--- caveats ---
- The PDFs carry a text layer; Paperless may skip/lighten OCR for them. BIGNIGHT's OOM named `gs` (ghostscript)
processes, so image-only scans may peak higher than this text-PDF batch. The 1.5x factor is the stated margin.
- With 1 worker the batch peak (783 MB) sits only ~2.7% under today's 768M cap (805 MB) — the single-worker
change alone would leave almost no headroom; the cap raise is needed too.
--- teardown ---
docker compose down -v (project plspike: 3 containers, 3 volumes, network), rm -rf /tmp/plspike in 9202.
Images pulled only for the spike (paperless-ngx:2.20.15, postgres:16-alpine, redis:7-alpine) removed —
none were used by 9202's other containers (pre-spike image list had none of them).