Decisions 151-156 recorded; design build evidence (R-638 measured safe, R-528 stopped by its measurement, R-518/R-717/R-762 built); golden 0.301.0; 07/08/09 updated; R-892, R-893 filed
gates / gates (push) Successful in 2m38s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-06 16:05:55 +02:00
parent 01d5dbd3e0
commit 8db4425d98
64 changed files with 4197 additions and 9 deletions
@@ -0,0 +1,2 @@
26: repo_url: https://gitea.dooplex.hu/admin/app-catalog-drill.git
@@ -0,0 +1,10 @@
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
drill: 8647f6f DRILL docmost: the OLD definition (6d8cd87^) for R-638 slice 0 | images: ['docmost/docmost:0.95.0', 'postgres:16-alpine', 'redis:7-alpine']
docmost: /api/auth/setup http=200 rc=0
docmost: login as the seeded user http=200 ok=True
[2] old version installed, pinned={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:18-alpine', 'docmost-redis': 'redis:7-alpine'}, live tables 48
[3] the ladder entry used: [({'docmost': 'docmost/docmost:0.95.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}, {'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'})]
drill: fb47929 DRILL docmost: the NEW definition (6d8cd87) + its one ladder entry, R-638 slice 0 | images: ['docmost/docmost:0.96.0', 'postgres:16-alpine', 'redis:7-alpine']
[3] update -> None, pinned={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:18-alpine', 'docmost-redis': 'redis:7-alpine'}, live tables 48, added by the migration: 0 []
docmost: login as the seeded user http=200 ok=True
RESULT the update did not migrate (no done, or no new table) — slice 0 cannot measure; stopping before restore
@@ -0,0 +1,117 @@
{
"app": "docmost",
"controller": "gitea.dooplex.hu/admin/felhom-controller:0.299.0",
"pinned_old": {
"docmost": "docmost/docmost:0.96.0",
"docmost-postgres": "postgres:18-alpine",
"docmost-redis": "redis:7-alpine"
},
"tables_old_live": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"oauth_authorization_codes",
"oauth_clients",
"oauth_grants",
"oauth_tokens",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"public_spaces",
"scim_tokens",
"shares",
"siem_destinations",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"update_final_phase": null,
"pinned_new": {
"docmost": "docmost/docmost:0.96.0",
"docmost-postgres": "postgres:18-alpine",
"docmost-redis": "redis:7-alpine"
},
"tables_new_live": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"oauth_authorization_codes",
"oauth_clients",
"oauth_grants",
"oauth_tokens",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"public_spaces",
"scim_tokens",
"shares",
"siem_destinations",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"tables_added_by_migration": [],
"seed_after_update": true
}
@@ -0,0 +1,247 @@
{
"app": "docmost",
"controller": "gitea.dooplex.hu/admin/felhom-controller:0.299.0",
"pinned_old": {
"docmost": "docmost/docmost:0.95.0",
"docmost-postgres": "postgres:16-alpine",
"docmost-redis": "redis:7-alpine"
},
"tables_old_live": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"scim_tokens",
"shares",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"update_final_phase": "done",
"pinned_new": {
"docmost": "docmost/docmost:0.96.0",
"docmost-postgres": "postgres:16-alpine",
"docmost-redis": "redis:7-alpine"
},
"tables_new_live": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"oauth_authorization_codes",
"oauth_clients",
"oauth_grants",
"oauth_tokens",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"public_spaces",
"scim_tokens",
"shares",
"siem_destinations",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"tables_added_by_migration": [
"oauth_authorization_codes",
"oauth_clients",
"oauth_grants",
"oauth_tokens",
"public_spaces",
"siem_destinations"
],
"seed_after_update": true,
"restore": {
"snapshot_id": "helyi",
"http": "HTTP/2 302",
"seconds": 32.4,
"state_after": "running",
"hold_after": null
},
"imported_db_dump_line": [
"2026/10/06 12:55:31 dbdump.go:820: [INFO] [backup] Imported DB dump docmost-postgres.sql into docmost-postgres (postgres)"
],
"tables_after_restore": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"scim_tokens",
"shares",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"seed_after_restore": true,
"unit_files": [
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/compose/.felhom.yml",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/compose/docker-compose.yml",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/compose/app.yaml",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/data-stamps.json",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_postgres_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_storage.tar",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/manifest.json",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/.felhom-tier2-layout"
],
"dump_files_seen": [
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_postgres_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/volume-dumps/docmost_docmost_storage.tar"
],
"tables_in_copy_dump": [
"ai_chat_messages",
"ai_chats",
"api_keys",
"attachments",
"audit",
"auth_accounts",
"auth_providers",
"backlinks",
"base_properties",
"base_rows",
"base_views",
"billing",
"comments",
"favorites",
"file_tasks",
"group_users",
"groups",
"kysely_migration",
"kysely_migration_lock",
"labels",
"notifications",
"page_access",
"page_history",
"page_labels",
"page_permissions",
"page_transclusion_references",
"page_transclusions",
"page_verifications",
"page_verifiers",
"pages",
"scim_tokens",
"shares",
"space_members",
"spaces",
"templates",
"user_mfa",
"user_sessions",
"user_tokens",
"users",
"watchers",
"workspace_invitations",
"workspaces"
],
"dump_used_for_control": [
"/mnt/felhom-drives/scratch_hdd/backups/secondary/docmost/recovery-unit/db-dumps/docmost-postgres.sql"
],
"migration_tables_left_after_restore": [],
"live_equals_copy": true,
"pinned_after_restore": {
"docmost": "docmost/docmost:0.95.0",
"docmost-postgres": "postgres:16-alpine",
"docmost-redis": "redis:7-alpine"
},
"state_after_restore": "running",
"verdict": "SAFE"
}
@@ -0,0 +1,30 @@
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
docmost already installed on 9202 — removing it first (scratch box)
drill: 686eafd DRILL docmost: the OLD definition (6d8cd87^) for R-638 slice 0 | images: ['docmost/docmost:0.95.0', 'postgres:16-alpine', 'redis:7-alpine']
docmost: /api/auth/setup http=200 rc=0
docmost: login as the seeded user http=200 ok=True
[2] old version installed, pinned={'docmost': 'docmost/docmost:0.95.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}, live tables 42
[3] the ladder entry used: [({'docmost': 'docmost/docmost:0.95.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}, {'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'})]
drill: 4dee823 DRILL docmost: the NEW definition (6d8cd87) + its one ladder entry, R-638 slice 0 | images: ['docmost/docmost:0.96.0', 'postgres:16-alpine', 'redis:7-alpine']
[3] update -> done, pinned={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}, live tables 48, added by the migration: 6 ['oauth_authorization_codes', 'oauth_clients', 'oauth_grants', 'oauth_tokens', 'public_spaces', 'siem_destinations']
docmost: login as the seeded user http=200 ok=True
[4] restorable copies offered: [{"time": "2026-10-06T12:53:06Z", "short_id": "helyi", "tier": 1, "drive_label": "Bels\u0151 SSD (rendszer)"}]
2026/10/06 12:55:25 handlers.go:1831: [WARN] [web] Restore requested (async): stack=docmost, snapshot=helyi from 172.18.0.5:40656
2026/10/06 12:55:25 restore_unit.go:381: [INFO] [backup] Restore docmost: the data belongs to [docmost/docmost:0.95.0 postgres:16-alpine redis:7-alpine] (written 2026-10-06T12:53:06Z); the app ran [docmost/docmost:0.96.0@sha256:b56947fcfd08aab8fae12a377e1792784786adbf8b96e4281f14ef4fc072685a postgre
2026/10/06 12:55:25 restore_unit.go:393: [INFO] [backup] Restoring docmost from recovery unit /mnt/sys_drive/felhom-data/backups/primary/docmost: images=3, secrets recovered=2/2, data_keys=0
2026/10/06 12:55:27 restore.go:152: [INFO] [backup] Restoring Docker volume docmost_docmost_postgres_data for docmost
2026/10/06 12:55:28 restore.go:152: [INFO] [backup] Restoring Docker volume docmost_docmost_redis_data for docmost
2026/10/06 12:55:28 restore.go:152: [INFO] [backup] Restoring Docker volume docmost_docmost_storage for docmost
2026/10/06 12:55:29 restore.go:195: [INFO] [backup] Restored 3 Docker volume(s) for docmost
2026/10/06 12:55:29 restore_db.go:87: [INFO] [backup] Restore docmost: replaying DB dump into docmost-postgres (postgres)
2026/10/06 12:55:31 dbdump.go:820: [INFO] [backup] Imported DB dump docmost-postgres.sql into docmost-postgres (postgres)
2026/10/06 12:55:31 restore_db.go:97: [INFO] [backup] Restore docmost: replayed 1 DB dump(s)
2026/10/06 12:55:56 restore_unit.go:478: [INFO] [backup] Restore-from-unit completed: docmost — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
2026/10/06 12:55:56 handlers.go:1843: [INFO] [web] Restore completed (async): stack=docmost in 30.474627183s (volumes 3/3, dbs 1/1)
docmost: login as the seeded user http=200 ok=True
[5] Imported DB dump lines: ['2026/10/06 12:55:31 dbdump.go:820: [INFO] [backup] Imported DB dump docmost-postgres.sql into docmost-postgres (postgres)']
[5] seed read back after restore: True
[5] live tables after restore 42; the copy's own dump lists 42; equal=True; migration tables still there: []
[5] pinned after restore {'docmost': 'docmost/docmost:0.95.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}, state running
RESULT docmost: SAFE
@@ -0,0 +1,45 @@
# R-638 option A — full controller suite after slices 1+2, 2026-10-06T13:10:21Z
$ cd felhom-controller/controller && go build ./... && go vet ./... && go test ./... -count=1
build rc=0 vet rc=0 test rc=0
--- FAIL lines (none expected):
--- tail:
ok gitea.dooplex.hu/admin/felhom-controller/internal/appbackup 0.036s
ok gitea.dooplex.hu/admin/felhom-controller/internal/appexport 0.627s
? gitea.dooplex.hu/admin/felhom-controller/internal/assets [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 1.713s
ok gitea.dooplex.hu/admin/felhom-controller/internal/backupwindow 0.011s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootrecon 0.011s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootstrap 14.027s
ok gitea.dooplex.hu/admin/felhom-controller/internal/channelhealth 0.009s
? gitea.dooplex.hu/admin/felhom-controller/internal/cloudflare [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/config 0.009s
ok gitea.dooplex.hu/admin/felhom-controller/internal/crashboot 0.011s
? gitea.dooplex.hu/admin/felhom-controller/internal/crypto [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/dockerexec 0.192s
ok gitea.dooplex.hu/admin/felhom-controller/internal/family 0.025s
ok gitea.dooplex.hu/admin/felhom-controller/internal/fillwatch 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/i18n 0.376s
ok gitea.dooplex.hu/admin/felhom-controller/internal/infra 0.014s
? gitea.dooplex.hu/admin/felhom-controller/internal/integrations [no test files]
? gitea.dooplex.hu/admin/felhom-controller/internal/logx [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/mailrelay 0.016s
ok gitea.dooplex.hu/admin/felhom-controller/internal/metrics 0.009s
ok gitea.dooplex.hu/admin/felhom-controller/internal/monitor 0.008s
ok gitea.dooplex.hu/admin/felhom-controller/internal/nightchain 0.009s
ok gitea.dooplex.hu/admin/felhom-controller/internal/notify 0.942s
ok gitea.dooplex.hu/admin/felhom-controller/internal/offsiteapply 0.014s
ok gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.087s
? gitea.dooplex.hu/admin/felhom-controller/internal/recovery [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/report 2.599s
ok gitea.dooplex.hu/admin/felhom-controller/internal/scheduler 0.456s
? gitea.dooplex.hu/admin/felhom-controller/internal/selftest [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/selfupdate 0.045s
ok gitea.dooplex.hu/admin/felhom-controller/internal/settings 0.542s
ok gitea.dooplex.hu/admin/felhom-controller/internal/setup 0.011s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sockheal 0.007s
ok gitea.dooplex.hu/admin/felhom-controller/internal/stacks 397.006s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sync 0.406s
ok gitea.dooplex.hu/admin/felhom-controller/internal/system 0.063s
ok gitea.dooplex.hu/admin/felhom-controller/internal/util 0.040s
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 63.736s
? gitea.dooplex.hu/admin/felhom-controller/scripts [no test files]
@@ -0,0 +1,19 @@
# R-638 slices 1+2 GREEN after the fix (felhom-controller 3124278 + fix, uncommitted at run time) — 2026-10-06T13:03:17Z
$ cd felhom-controller/controller && go test ./internal/backup/ -run '^TestR638_' -v -count=1
=== RUN TestR638_FallbackReplaysBeforeTheAppStarts
--- PASS: TestR638_FallbackReplaysBeforeTheAppStarts (0.00s)
=== RUN TestR638_FallbackThroughUnitRestoreKeepsTheOrder
--- PASS: TestR638_FallbackThroughUnitRestoreKeepsTheOrder (0.00s)
=== RUN TestR638_FallbackNoDumpTakesOneFullStart
--- PASS: TestR638_FallbackNoDumpTakesOneFullStart (0.00s)
=== RUN TestR638_FallbackRefusesWhenNoDBServiceIdentifiable
--- PASS: TestR638_FallbackRefusesWhenNoDBServiceIdentifiable (0.01s)
=== RUN TestR638_FallbackVolumeFailureSkipsTheReplay
--- PASS: TestR638_FallbackVolumeFailureSkipsTheReplay (0.00s)
=== RUN TestR638_UnitVolumeFailureNeverCallsTheImporter
--- PASS: TestR638_UnitVolumeFailureNeverCallsTheImporter (0.00s)
=== RUN TestR638_UnitVolumeSuccessStillReplays
--- PASS: TestR638_UnitVolumeSuccessStillReplays (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.020s
rc=0
@@ -0,0 +1,20 @@
# R-638 slice 1 RED-PROOF on UNCHANGED code (felhom-controller 3124278 + the new test file only) — 2026-10-06T13:01:56Z
$ cd felhom-controller/controller && go test ./internal/backup/ -run '^TestR638_Fallback' -v -count=1
=== RUN TestR638_FallbackReplaysBeforeTheAppStarts
r638_restore_order_test.go:89: the FULL stack was already up when the replay fired (calls before the replay: "stop,start") — a newer app can migrate the restored data before the copy is loaded (R-638)
--- FAIL: TestR638_FallbackReplaysBeforeTheAppStarts (0.00s)
=== RUN TestR638_FallbackThroughUnitRestoreKeepsTheOrder
r638_restore_order_test.go:118: sequence = "stop,start", want stop → db-only start → replay → full start
--- FAIL: TestR638_FallbackThroughUnitRestoreKeepsTheOrder (0.00s)
=== RUN TestR638_FallbackNoDumpTakesOneFullStart
--- PASS: TestR638_FallbackNoDumpTakesOneFullStart (0.00s)
=== RUN TestR638_FallbackRefusesWhenNoDBServiceIdentifiable
r638_restore_order_test.go:151: expected a refusal: a dump exists but no database service can be started for it
--- FAIL: TestR638_FallbackRefusesWhenNoDBServiceIdentifiable (0.00s)
=== RUN TestR638_FallbackVolumeFailureSkipsTheReplay
r638_restore_order_test.go:178: the volume failure must surface as the data-error outcome naming the volume, got: <nil>
--- FAIL: TestR638_FallbackVolumeFailureSkipsTheReplay (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.009s
FAIL
rc=1
@@ -0,0 +1,11 @@
# R-638 slice 2 RED-PROOF on UNCHANGED code (felhom-controller 3124278 + the new test file only) — 2026-10-06T13:02:03Z
$ cd felhom-controller/controller && go test ./internal/backup/ -run '^TestR638_Unit' -v -count=1
=== RUN TestR638_UnitVolumeFailureNeverCallsTheImporter
r638_restore_order_test.go:211: the importer ran after a failed volume leg: [/tmp/TestR638_UnitVolumeFailureNeverCallsTheImporter760559713/001/drive/backups/primary/app/db-dumps/app-postgres.sql]
--- FAIL: TestR638_UnitVolumeFailureNeverCallsTheImporter (0.00s)
=== RUN TestR638_UnitVolumeSuccessStillReplays
--- PASS: TestR638_UnitVolumeSuccessStillReplays (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/backup 0.009s
FAIL
rc=1
@@ -0,0 +1,3 @@
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
drill: 9a724b6 DRILL romm: the OLD definition (15f9ebf^) for R-638 slice 0 | images: ['rommapp/romm:5.0.0', 'mariadb:11.4', 'redis:7-alpine']
RESULT the install did not complete
@@ -0,0 +1,199 @@
{
"app": "romm",
"controller": "gitea.dooplex.hu/admin/felhom-controller:0.299.0",
"pinned_old": {
"romm": "rommapp/romm:5.0.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"tables_old_live": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collections"
],
"update_final_phase": "done",
"pinned_new": {
"romm": "rommapp/romm:5.3.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"tables_new_live": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"memory_card_versions",
"memory_cards",
"music_favorite_tracks",
"music_playlist_tracks",
"music_playlists",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_file_doc_meta",
"rom_file_user",
"rom_files",
"rom_identity_keys",
"rom_notes",
"rom_similarity",
"rom_user",
"roms",
"roms_facets",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"streaming_container_adoptions",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collection_roms",
"virtual_collections"
],
"tables_added_by_migration": [
"memory_card_versions",
"memory_cards",
"music_favorite_tracks",
"music_playlist_tracks",
"music_playlists",
"rom_file_doc_meta",
"rom_file_user",
"rom_identity_keys",
"rom_similarity",
"roms_facets",
"streaming_container_adoptions",
"virtual_collection_roms"
],
"seed_after_update": false,
"restore": {
"snapshot_id": "helyi",
"http": "HTTP/2 302",
"seconds": 56.7,
"state_after": "running",
"hold_after": null
},
"imported_db_dump_line": [
"2026/10/06 13:13:07 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)"
],
"tables_after_restore": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collections"
],
"seed_after_restore": true,
"unit_files": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/pre-restore-20261006T131058Z-romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/.felhom.yml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/docker-compose.yml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/app.yaml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/data-stamps.json",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_config.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_db_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/manifest.json"
],
"dump_files_seen": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_config.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_db_data.tar"
],
"tables_in_copy_dump": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collections"
],
"dump_used_for_control": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/romm-mariadb.sql"
],
"migration_tables_left_after_restore": [],
"live_equals_copy": true,
"pinned_after_restore": {
"romm": "rommapp/romm:5.0.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"state_after_restore": "running",
"verdict": "SAFE"
}
@@ -0,0 +1,38 @@
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
drill: 903cc03 DRILL romm: the OLD definition (15f9ebf^) for R-638 slice 0 | images: ['rommapp/romm:5.0.0', 'mariadb:11.4', 'redis:7-alpine']
romm: POST /api/users http=201
romm: login as the seeded user http=200 ok=True
[2] old version installed, pinned={'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, live tables 27
[3] the ladder entry used: [({'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, {'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'})]
drill: 04a94d3 DRILL romm: the NEW definition (15f9ebf) + its one ladder entry, R-638 slice 0 | images: ['rommapp/romm:5.3.0', 'mariadb:11.4', 'redis:7-alpine']
[3] update -> done, pinned={'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, live tables 39, added by the migration: 12 ['memory_card_versions', 'memory_cards', 'music_favorite_tracks', 'music_playlist_tracks', 'music_playlists', 'rom_file_doc_meta', 'rom_file_user', 'rom_identity_keys', 'rom_similarity', 'roms_facets', 'streaming_container_adoptions', 'virtual_collection_roms']
romm: login as the seeded user http=502 ok=False
romm: body <html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.29.8</center>
</body>
</html>
[4] restorable copies offered: [{"time": "2026-10-06T13:10:41Z", "short_id": "helyi", "tier": 1, "drive_label": "SCRATCH HDD"}]
2026/10/06 13:12:51 handlers.go:1831: [WARN] [web] Restore requested (async): stack=romm, snapshot=helyi from 172.18.0.5:40656
2026/10/06 13:12:51 restore_unit.go:381: [INFO] [backup] Restore romm: the data belongs to [rommapp/romm:5.0.0 mariadb:11.4 redis:7-alpine] (written 2026-10-06T13:10:41Z); the app ran [rommapp/romm:5.3.0@sha256:dc586cb3a2c7316fcffb3dc273171b1964523f0f409e989295d26d2199df1d4e mariadb:11.4@sha256:70cc
2026/10/06 13:12:51 restore_unit.go:393: [INFO] [backup] Restoring romm from recovery unit /mnt/felhom-drives/scratch_hdd/backups/primary/romm: images=3, secrets recovered=3/3, data_keys=0
2026/10/06 13:13:01 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_config for romm
2026/10/06 13:13:01 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_db_data for romm
2026/10/06 13:13:02 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_redis_data for romm
2026/10/06 13:13:03 restore.go:195: [INFO] [backup] Restored 3 Docker volume(s) for romm
2026/10/06 13:13:03 restore_db.go:87: [INFO] [backup] Restore romm: replaying DB dump into romm-db (mariadb)
2026/10/06 13:13:07 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)
2026/10/06 13:13:07 restore_db.go:97: [INFO] [backup] Restore romm: replayed 1 DB dump(s)
2026/10/06 13:13:47 restore_unit.go:478: [INFO] [backup] Restore-from-unit completed: romm — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
2026/10/06 13:13:47 handlers.go:1843: [INFO] [web] Restore completed (async): stack=romm in 55.871437852s (volumes 3/3, dbs 1/1)
romm: login as the seeded user http=200 ok=True
[5] Imported DB dump lines: ['2026/10/06 13:13:07 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)']
[5] seed read back after restore: True
[5] live tables after restore 27; the copy's own dump lists 27; equal=True; migration tables still there: []
[5] pinned after restore {'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, state running
RESULT romm: SAFE
remove -> 200
@@ -0,0 +1,197 @@
{
"app": "romm",
"controller": "gitea.dooplex.hu/admin/felhom-controller:0.299.0",
"pinned_old": {
"romm": "rommapp/romm:5.0.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"tables_old_live": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collections"
],
"update_final_phase": "done",
"pinned_new": {
"romm": "rommapp/romm:5.3.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"tables_new_live": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"memory_card_versions",
"memory_cards",
"music_favorite_tracks",
"music_playlist_tracks",
"music_playlists",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_file_doc_meta",
"rom_file_user",
"rom_files",
"rom_identity_keys",
"rom_notes",
"rom_similarity",
"rom_user",
"roms",
"roms_facets",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"streaming_container_adoptions",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collection_roms",
"virtual_collections"
],
"tables_added_by_migration": [
"memory_card_versions",
"memory_cards",
"music_favorite_tracks",
"music_playlist_tracks",
"music_playlists",
"rom_file_doc_meta",
"rom_file_user",
"rom_identity_keys",
"rom_similarity",
"roms_facets",
"streaming_container_adoptions",
"virtual_collection_roms"
],
"seed_after_update": false,
"restore": {
"snapshot_id": "helyi",
"http": "HTTP/2 302",
"seconds": 56.7,
"state_after": "running",
"hold_after": null
},
"imported_db_dump_line": [
"2026/10/06 13:04:43 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)"
],
"tables_after_restore": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"roms_metadata",
"saves",
"screenshots",
"sibling_roms",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users",
"virtual_collections"
],
"seed_after_restore": true,
"unit_files": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/pre-restore-20261006T130227Z-romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/.felhom.yml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/docker-compose.yml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/compose/app.yaml",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/data-stamps.json",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_config.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_db_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/manifest.json"
],
"dump_files_seen": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/pre-restore-20261006T130227Z-romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/romm-mariadb.sql",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_config.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_redis_data.tar",
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/volume-dumps/romm_romm_db_data.tar"
],
"tables_in_copy_dump": [
"alembic_version",
"client_tokens",
"collections",
"collections_roms",
"device_save_sync",
"devices",
"firmware",
"hidden_entities",
"permission_group_grants",
"permission_groups",
"platforms",
"play_sessions",
"rom_files",
"rom_notes",
"rom_user",
"roms",
"saves",
"screenshots",
"smart_collections",
"states",
"sync_sessions",
"track_meta",
"user_permission_overrides",
"users"
],
"dump_used_for_control": [
"/mnt/felhom-drives/scratch_hdd/backups/primary/romm/db-dumps/pre-restore-20261006T130227Z-romm-mariadb.sql"
],
"migration_tables_left_after_restore": [],
"live_equals_copy": false,
"pinned_after_restore": {
"romm": "rommapp/romm:5.0.0",
"romm-db": "mariadb:11.4",
"romm-redis": "redis:7-alpine"
},
"state_after_restore": "running",
"verdict": "NOT SAFE / NOT SHOWN"
}
@@ -0,0 +1,39 @@
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.299.0
drill: 04b0daf DRILL romm: the OLD definition (15f9ebf^) for R-638 slice 0 | images: ['rommapp/romm:5.0.0', 'mariadb:11.4', 'redis:7-alpine']
romm: POST /api/users http=201
romm: login as the seeded user http=200 ok=True
[2] old version installed, pinned={'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, live tables 27
[3] the ladder entry used: [({'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, {'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'})]
drill: 7b95d47 DRILL romm: the NEW definition (15f9ebf) + its one ladder entry, R-638 slice 0 | images: ['rommapp/romm:5.3.0', 'mariadb:11.4', 'redis:7-alpine']
[3] update -> done, pinned={'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, live tables 39, added by the migration: 12 ['memory_card_versions', 'memory_cards', 'music_favorite_tracks', 'music_playlist_tracks', 'music_playlists', 'rom_file_doc_meta', 'rom_file_user', 'rom_identity_keys', 'rom_similarity', 'roms_facets', 'streaming_container_adoptions', 'virtual_collection_roms']
romm: login as the seeded user http=502 ok=False
romm: body <html>
<head><title>502 Bad Gateway</title></head>
<body>
<center><h1>502 Bad Gateway</h1></center>
<hr><center>nginx/1.29.8</center>
</body>
</html>
[4] restorable copies offered: [{"time": "2026-10-06T13:02:12Z", "short_id": "helyi", "tier": 1, "drive_label": "SCRATCH HDD"}]
2026/10/06 13:04:28 handlers.go:1831: [WARN] [web] Restore requested (async): stack=romm, snapshot=helyi from 172.18.0.5:40656
2026/10/06 13:04:28 restore_unit.go:381: [INFO] [backup] Restore romm: the data belongs to [rommapp/romm:5.0.0 mariadb:11.4 redis:7-alpine] (written 2026-10-06T13:02:12Z); the app ran [rommapp/romm:5.3.0@sha256:dc586cb3a2c7316fcffb3dc273171b1964523f0f409e989295d26d2199df1d4e mariadb:11.4@sha256:70cc
2026/10/06 13:04:28 restore_unit.go:393: [INFO] [backup] Restoring romm from recovery unit /mnt/felhom-drives/scratch_hdd/backups/primary/romm: images=3, secrets recovered=3/3, data_keys=0
2026/10/06 13:04:38 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_config for romm
2026/10/06 13:04:38 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_db_data for romm
2026/10/06 13:04:39 restore.go:152: [INFO] [backup] Restoring Docker volume romm_romm_redis_data for romm
2026/10/06 13:04:39 restore.go:195: [INFO] [backup] Restored 3 Docker volume(s) for romm
2026/10/06 13:04:40 restore_db.go:87: [INFO] [backup] Restore romm: replaying DB dump into romm-db (mariadb)
2026/10/06 13:04:43 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)
2026/10/06 13:04:43 restore_db.go:97: [INFO] [backup] Restore romm: replayed 1 DB dump(s)
2026/10/06 13:05:23 restore_unit.go:478: [INFO] [backup] Restore-from-unit completed: romm — 3 volume(s) of 3 listed, 1 database(s) of 1 listed
2026/10/06 13:05:23 handlers.go:1843: [INFO] [web] Restore completed (async): stack=romm in 55.385810686s (volumes 3/3, dbs 1/1)
romm: login as the seeded user http=200 ok=True
[5] Imported DB dump lines: ['2026/10/06 13:04:43 dbdump.go:820: [INFO] [backup] Imported DB dump romm-mariadb.sql into romm-db (mariadb)']
[5] seed read back after restore: True
[5] live tables after restore 27; the copy's own dump lists 24; equal=False; migration tables still there: []
[5] pinned after restore {'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}, state running
RESULT romm: NOT SAFE / NOT SHOWN
remove -> 200
@@ -0,0 +1,76 @@
Tue Oct 6 12:41:22 UTC 2026
Unable to find image 'alpine:3.20' locally
3.20: Pulling from library/alpine
25f1d6b1951a: Already exists
Digest: sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
Status: Downloaded newer image for alpine:3.20
started r528-hog (alpine:3.20, --memory 64m, swap 64m)
== before
low 0
high 0
max 0
oom 0
oom_kill 0
oom_group_kill 0
sock_throttled 0
OOMKilled=false running=true exit=0 restarts=0
hog 1 exec rc=137
Error response from daemon: container 573bd9b193fea38ab8301c6d9e4752211aaffb1a47009ed60d5b3cbb433ff5e5 is not running
hog 2 exec rc=1
Error response from daemon: container 573bd9b193fea38ab8301c6d9e4752211aaffb1a47009ed60d5b3cbb433ff5e5 is not running
hog 3 exec rc=1
== after three hogs (child processes of the running container)
Error response from daemon: container 573bd9b193fea38ab8301c6d9e4752211aaffb1a47009ed60d5b3cbb433ff5e5 is not running
OOMKilled=true running=false exit=137 restarts=0
== docker events (oom) in the last 2 minutes
1791290484 oom r528-hog
=== run 2: the hog raises its own oom_score_adj to 1000, so the kernel picks it, not the container's main process
Tue Oct 6 12:41:40 UTC 2026
started r528-hog (main: a sleep loop)
memory.oom.group=0
== before
oom_kill 0
OOMKilled=false running=true exit=0
hog 1 exec rc=137
Error response from daemon: container 4152ea857b6ad23ccc31b95774ff83bc7843c8532fd0dce64c21486cf88a7b56 is not running
hog 2 exec rc=1
Error response from daemon: container 4152ea857b6ad23ccc31b95774ff83bc7843c8532fd0dce64c21486cf88a7b56 is not running
hog 3 exec rc=1
== after three hogs
Error response from daemon: container 4152ea857b6ad23ccc31b95774ff83bc7843c8532fd0dce64c21486cf88a7b56 is not running
OOMKilled=true running=false exit=137 restarts=0
== docker oom events since this run started
1791290484 oom r528-hog
=== run 3: main process protected (--oom-score-adj -1000), hog at oom_score_adj 1000
Tue Oct 6 12:41:59 UTC 2026
started
pid1 oom_score_adj=0
hog 1 exec rc=137
Error response from daemon: container eba82647cdb95aebcda6410686d6b59f2178cdf645d84d852b136a00be5bac74 is not running
hog 2 exec rc=1
Error response from daemon: container eba82647cdb95aebcda6410686d6b59f2178cdf645d84d852b136a00be5bac74 is not running
hog 3 exec rc=1
== after three hogs
Error response from daemon: container eba82647cdb95aebcda6410686d6b59f2178cdf645d84d852b136a00be5bac74 is not running
OOMKilled=true running=false exit=137 restarts=0
== docker oom events since this run started
1791290484 oom r528-hog
1791290501 oom r528-hog
=== run 4: --memory 256m, main = sleep infinity (no forks), hog = one 400 MB block (dd bs=400M)
Tue Oct 6 12:42:18 UTC 2026
started
hog 1 exec rc=137
hog 2 exec rc=137
hog 3 exec rc=137
== after three hogs
oom 5
oom_kill 3
oom_group_kill 0
OOMKilled=true running=true exit=0 restarts=0
== docker oom events since this run started
1791290538 oom r528-hog
1791290541 oom r528-hog
1791290543 oom r528-hog
@@ -0,0 +1,11 @@
Tue Oct 6 12:42:54 UTC 2026
running containers: 21
engine 29.8.2
unreadable: cloudflared
bash: line 11: bc: command not found
one full read: s wall; readable 20, unreadable 1
bash: line 12: /usr/bin/time: No such file or directory
=== again, timed with bash's own clock
run 1: one full read of 21 containers took 1637 ms wall
run 2: one full read of 21 containers took 1642 ms wall
bash time: 1.661 s wall, 0.474 s user, 0.438 s sys
@@ -0,0 +1,5 @@
r528-hog
Untagged: alpine:3.20
Untagged: alpine@sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
Deleted: sha256:bf8527eb54c3680e728d5b4b383a8ba730d72dae7236fbc8dff97ed6b224a731
0
@@ -0,0 +1,107 @@
R-518 option A (decision 156) — controller suite after the fix (local clone, branch r518-one-stop-per-tier,
working tree = fix + tests + copy; base felhom-controller 3124278).
$ cd controller && go build ./... && go vet ./... && echo BUILD_VET_OK
BUILD_VET_OK
$ cd controller && go test ./... (full output — 43 lines, no FAIL line)
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 10.666s
ok gitea.dooplex.hu/admin/felhom-controller/internal/agentapi 0.182s
ok gitea.dooplex.hu/admin/felhom-controller/internal/api 0.145s
ok gitea.dooplex.hu/admin/felhom-controller/internal/appbackup 0.028s
ok gitea.dooplex.hu/admin/felhom-controller/internal/appexport 0.615s
? gitea.dooplex.hu/admin/felhom-controller/internal/assets [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 1.777s
ok gitea.dooplex.hu/admin/felhom-controller/internal/backupwindow 0.006s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootrecon 0.008s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootstrap 14.029s
ok gitea.dooplex.hu/admin/felhom-controller/internal/channelhealth 0.005s
? gitea.dooplex.hu/admin/felhom-controller/internal/cloudflare [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/config 0.006s
ok gitea.dooplex.hu/admin/felhom-controller/internal/crashboot 0.007s
? gitea.dooplex.hu/admin/felhom-controller/internal/crypto [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/dockerexec 0.189s
ok gitea.dooplex.hu/admin/felhom-controller/internal/family 0.026s
ok gitea.dooplex.hu/admin/felhom-controller/internal/fillwatch 0.011s
ok gitea.dooplex.hu/admin/felhom-controller/internal/i18n 0.395s
ok gitea.dooplex.hu/admin/felhom-controller/internal/infra 0.012s
? gitea.dooplex.hu/admin/felhom-controller/internal/integrations [no test files]
? gitea.dooplex.hu/admin/felhom-controller/internal/logx [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/mailrelay 0.018s
ok gitea.dooplex.hu/admin/felhom-controller/internal/metrics 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/monitor 0.007s
ok gitea.dooplex.hu/admin/felhom-controller/internal/nightchain 0.011s
ok gitea.dooplex.hu/admin/felhom-controller/internal/notify 0.910s
ok gitea.dooplex.hu/admin/felhom-controller/internal/offsiteapply 0.014s
ok gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.089s
? gitea.dooplex.hu/admin/felhom-controller/internal/recovery [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/report 2.587s
ok gitea.dooplex.hu/admin/felhom-controller/internal/scheduler 0.457s
? gitea.dooplex.hu/admin/felhom-controller/internal/selftest [no test files]
ok gitea.dooplex.hu/admin/felhom-controller/internal/selfupdate 0.043s
ok gitea.dooplex.hu/admin/felhom-controller/internal/settings 0.555s
ok gitea.dooplex.hu/admin/felhom-controller/internal/setup 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sockheal 0.008s
ok gitea.dooplex.hu/admin/felhom-controller/internal/stacks 397.094s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sync 0.385s
ok gitea.dooplex.hu/admin/felhom-controller/internal/system 0.064s
ok gitea.dooplex.hu/admin/felhom-controller/internal/util 0.041s
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 63.621s
? gitea.dooplex.hu/admin/felhom-controller/scripts [no test files]
Filter proof (the new/renamed tests ran, after the fix):
$ go test ./internal/quiesce/ -run 'TestFirstTierSnapshot_ResumesAppWhileUploadContinues|TestManualPress_RunsOnlyLocalTier|TestBothTiersDue_FirstCycleRunsOnlyFirstTier|TestLeftoverTier_RunsInNextCycleInItsOwnWindow|TestNotify_BothFailingTiersAreReported|TestManualRun_' -v -count=1
=== RUN TestNotify_BothFailingTiersAreReported
--- PASS: TestNotify_BothFailingTiersAreReported (0.00s)
=== RUN TestManualRun_AbsentTierSkipped
--- PASS: TestManualRun_AbsentTierSkipped (0.00s)
=== RUN TestManualRun_UnknownStorageNotSkipped
--- PASS: TestManualRun_UnknownStorageNotSkipped (0.00s)
=== RUN TestBothTiersDue_FirstCycleRunsOnlyFirstTier
--- PASS: TestBothTiersDue_FirstCycleRunsOnlyFirstTier (0.00s)
=== RUN TestFirstTierSnapshot_ResumesAppWhileUploadContinues
--- PASS: TestFirstTierSnapshot_ResumesAppWhileUploadContinues (0.00s)
=== RUN TestLeftoverTier_RunsInNextCycleInItsOwnWindow
--- PASS: TestLeftoverTier_RunsInNextCycleInItsOwnWindow (0.00s)
=== RUN TestManualPress_RunsOnlyLocalTier
--- PASS: TestManualPress_RunsOnlyLocalTier (0.00s)
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.022s
$ go test ./internal/quiesce/ -run TestManualRunTiers_ -v -count=1
=== RUN TestManualRunTiers_NoPrimaryAvailable_BacksUpFirstAvailable
--- PASS: TestManualRunTiers_NoPrimaryAvailable_BacksUpFirstAvailable (0.00s)
PASS
$ go test ./internal/web/ -run 'TestR518_|TestI18n' -v -count=1 (top-level RUN lines)
=== RUN TestI18nParity
=== RUN TestI18nEnglishPages
=== RUN TestI18nParityCoversEveryMarker
=== RUN TestI18nJSContextValuesAreSafe
=== RUN TestI18nDirectRenderPagesFollowLanguage
=== RUN TestI18nDirectRenderPagesHaveNoAdminChrome
=== RUN TestR518_BackupButtonStatesTheShortLocalOnlyStop
PASS
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 7.899s
$ python3 controller/scripts/controller_gates.py --fast (felhom.eu sibling present; summary)
template-id OK (exit 0)
emoji OK (exit 0)
native-confirm OK (exit 0)
offbox-rename OK (exit 0)
app-row-dedup OK (exit 0)
mojibake OK (exit 0)
docker-v OK (exit 0)
secret-markup OK (exit 0)
retrieval-promise OK (exit 0)
debug-routes OK (exit 0)
reuse-refs OK (exit 0)
instructions OK (exit 0)
observations OK (exit 0)
minagent-header OK (exit 0)
i18n OK (exit 0)
go-parity OK (exit 0)
gofmt OK (exit 0)
golden-notice ADVISORY (exit 0, advisory)
all controller gates OK
@@ -0,0 +1,13 @@
$ cd controller && go test ./internal/quiesce/ -run 'TestFirstTierSnapshot_ResumesAppWhileUploadContinues' -v -count=1
RESULT: FAILED (exit code 1) — run on the UNCHANGED quiesce.go (felhom-controller 3124278), new test only.
=== RUN TestFirstTierSnapshot_ResumesAppWhileUploadContinues
tiers_test.go:223: the apps were still STOPPED when the second `snapshotted` poll answered (restarts per poll = [0 0 0 0]) — they must resume at the first tier's snapshot (decision 156)
--- FAIL: TestFirstTierSnapshot_ResumesAppWhileUploadContinues (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.010s
FAIL
Reading: two tiers due; local answered snapshotted, snapshotted, snapshotted, done. The restart count
sampled at each local poll was 0 at every poll — the apps stayed stopped through the whole local
upload (R-82: early resume only on the LAST tier).
@@ -0,0 +1,12 @@
$ cd controller && go test ./internal/quiesce/ -run 'TestManualPress_RunsOnlyLocalTier' -v -count=1
RESULT: FAILED (exit code 1) — run on the UNCHANGED quiesce.go (felhom-controller 3124278), new test only.
=== RUN TestManualPress_RunsOnlyLocalTier
tiers_test.go:298: a manual press must back up ONLY the local (primary) tier; started=[felhom-pbs local]
--- FAIL: TestManualPress_RunsOnlyLocalTier (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.006s
FAIL
Reading: the old manual press (allTiersForManualRun) requested EVERY advertised tier, in agent
order — here the off-site tier felhom-pbs was started too (listed first in this test on purpose).
@@ -0,0 +1,7 @@
$ go test ./internal/quiesce/ -run TestManualRunTiers_NoPrimaryAvailable_BacksUpNothing -v (manualRunTiers as the helper wrote it: the off-site tier stands in)
=== RUN TestManualRunTiers_NoPrimaryAvailable_BacksUpNothing
tier_skip_test.go:94: want no tier (the local copy's storage is absent), got [{target:felhom-pbs ageSecs:<nil> state: primary:false}]
--- FAIL: TestManualRunTiers_NoPrimaryAvailable_BacksUpNothing (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.005s
FAIL
@@ -0,0 +1,2 @@
Loaded image: gitea.dooplex.hu/admin/felhom-controller:0.301.0
gitea.dooplex.hu/admin/felhom-controller:0.301.0 Up 20 seconds (healthy)
@@ -0,0 +1,9 @@
15:27:05 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
15:27:25 [1] deployed, controller state=running, pinned={'wishlist': 'ghcr.io/cmintey/wishlist:v0.67.1'}
deploy: True
15:27:25 gate: wishlist is gated — passed as the household (cookie set)
15:27:26 wishlist: /signup http=200 type=success
seed ok: True
v24.20.0
[]
@@ -0,0 +1,63 @@
$ cd controller && go build ./... && go vet ./... && go test ./... -count=1
ok gitea.dooplex.hu/admin/felhom-controller/cmd/controller 10.753s
ok gitea.dooplex.hu/admin/felhom-controller/internal/agentapi 0.202s
ok gitea.dooplex.hu/admin/felhom-controller/internal/api 0.179s
ok gitea.dooplex.hu/admin/felhom-controller/internal/appbackup 0.037s
ok gitea.dooplex.hu/admin/felhom-controller/internal/appexport 0.621s
ok gitea.dooplex.hu/admin/felhom-controller/internal/backup 1.771s
ok gitea.dooplex.hu/admin/felhom-controller/internal/backupwindow 0.008s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootrecon 0.012s
ok gitea.dooplex.hu/admin/felhom-controller/internal/bootstrap 14.026s
ok gitea.dooplex.hu/admin/felhom-controller/internal/channelhealth 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/config 0.007s
ok gitea.dooplex.hu/admin/felhom-controller/internal/crashboot 0.007s
ok gitea.dooplex.hu/admin/felhom-controller/internal/dockerexec 0.200s
ok gitea.dooplex.hu/admin/felhom-controller/internal/family 0.023s
ok gitea.dooplex.hu/admin/felhom-controller/internal/fillwatch 0.009s
ok gitea.dooplex.hu/admin/felhom-controller/internal/i18n 0.395s
ok gitea.dooplex.hu/admin/felhom-controller/internal/infra 0.013s
ok gitea.dooplex.hu/admin/felhom-controller/internal/mailrelay 0.017s
ok gitea.dooplex.hu/admin/felhom-controller/internal/metrics 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/monitor 0.007s
ok gitea.dooplex.hu/admin/felhom-controller/internal/nightchain 0.010s
ok gitea.dooplex.hu/admin/felhom-controller/internal/notify 0.958s
ok gitea.dooplex.hu/admin/felhom-controller/internal/offsiteapply 0.015s
ok gitea.dooplex.hu/admin/felhom-controller/internal/quiesce 0.086s
ok gitea.dooplex.hu/admin/felhom-controller/internal/report 2.590s
ok gitea.dooplex.hu/admin/felhom-controller/internal/scheduler 0.455s
ok gitea.dooplex.hu/admin/felhom-controller/internal/selfupdate 0.048s
ok gitea.dooplex.hu/admin/felhom-controller/internal/settings 0.580s
ok gitea.dooplex.hu/admin/felhom-controller/internal/setup 0.009s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sockheal 0.006s
ok gitea.dooplex.hu/admin/felhom-controller/internal/stacks 397.076s
ok gitea.dooplex.hu/admin/felhom-controller/internal/sync 0.393s
ok gitea.dooplex.hu/admin/felhom-controller/internal/system 0.065s
ok gitea.dooplex.hu/admin/felhom-controller/internal/util 0.030s
ok gitea.dooplex.hu/admin/felhom-controller/internal/web 64.818s
rc=0
$ cd controller && python3 scripts/controller_gates.py --fast
==============================================================================
== summary
==============================================================================
template-id OK (exit 0)
emoji OK (exit 0)
native-confirm OK (exit 0)
offbox-rename OK (exit 0)
app-row-dedup OK (exit 0)
mojibake OK (exit 0)
docker-v OK (exit 0)
secret-markup OK (exit 0)
retrieval-promise OK (exit 0)
debug-routes OK (exit 0)
reuse-refs OK (exit 0)
instructions OK (exit 0)
observations OK (exit 0)
minagent-header OK (exit 0)
i18n OK (exit 0)
go-parity OK (exit 0)
gofmt OK (exit 0)
golden-notice ADVISORY (exit 0, advisory)
all controller gates OK
@@ -0,0 +1,29 @@
wishlist installed from the earlier probe — removing it first (scratch box)
9202 controller: gitea.dooplex.hu/admin/felhom-controller:0.301.0
wishlist: /signup http=200 type=success
[1] the household's first account: True | DB: ['DB enableSignup=<no row: default open> users=1']
[2] the household opens the app (setup done) -> 200 {'data': {'opened': True}, 'error': '', 'ok': True}
[2] DB after the close command: ['DB enableSignup=false users=1']
2026/10/06 13:31:53 after_setup.go:156: [INFO] [stacks] wishlist: the app's own sign-up switch CLOSED (the gate opened (household); env [], command true)
[3] stranger, straight at the app: {"type":"error","error":{"message":"This instance is invite only"}} HTTP 401
[3] DB: ['DB enableSignup=false users=1']
[4] the household opens the 15-minute window -> 200 {'data': {'open_until': '2026-10-06T13:47:34Z'}, 'error': '', 'ok': True}
[4] DB inside the window: ['DB enableSignup=true users=1']
[4] a family member, straight at the app: {"type":"success","status":200,"data":"[{\"success\":1},true]"} HTTP 200
[4] DB: ['DB enableSignup=true users=2']
2026/10/06 13:32:34 signup_block.go:142: [INFO] [stacks] wishlist: the household opened sign-up until 2026-10-06T13:47:34Z — the loop closes it again
2026/10/06 13:32:34 after_setup.go:205: [INFO] [stacks] wishlist: the app's own sign-up switch opened for the household's window (the household's window; env [], open_command true)
[5] the switch reads CLOSED again 906 s after the check began: ['DB enableSignup=false users=2']
[5] stranger after the window, straight at the app: {"type":"error","error":{"message":"This instance is invite only"}} HTTP 401
[5] DB: ['DB enableSignup=false users=2']
2026/10/06 13:32:34 signup_block.go:142: [INFO] [stacks] wishlist: the household opened sign-up until 2026-10-06T13:47:34Z — the loop closes it again
2026/10/06 13:32:34 after_setup.go:205: [INFO] [stacks] wishlist: the app's own sign-up switch opened for the household's window (the household's window; env [], open_command true)
2026/10/06 13:47:44 after_setup.go:156: [INFO] [stacks] wishlist: the app's own sign-up switch CLOSED (the loop (window ended or retry); env [], command true)
[5] app page record: {"after_setup": {"at": "2026-10-06T13:47:44Z", "ok": true}, "setup_gate": {"state": "open", "since": "2026-10-06T13:30:49Z", "hosts": ["wishlist.enkisfelhom.hu"], "opened_at": "2026-10-06T13:31:53Z", "opened_by": "household", "signup_open_until": "2026-10-06T13:47:34Z", "native_lock": "applied"}}
15:48:49 [X] stop -> 200 {'ok': True, 'message': 'Stack wishlist stop completed'}
15:49:21 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'wishlist', 'volumes_removed': ['wishlist_wishlist_data', 'wishlist_wishlist_uploads'], 'hdd_paths_removed': [], 'hdd_paths_pre
15:49:29 [X] after remove: deployed=False leftovers='/opt/docker/stacks/wishlist'
remove -> 200
@@ -0,0 +1,14 @@
# R-717 red-proof: TestAfterSetupR717_TheCloseRunsAgainAfterAnUpdate
# mutation in controller/internal/stacks/update.go:
# --- removed
# m.markNativeLockForReapply(name, dir) // R-717: the loop closes the app's own sign-up switch again after an update
# +++ put instead
# // RED-PROOF: no re-close after an update
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_TheCloseRunsAgainAfterAnUpdate$ -count=1 -v
=== RUN TestAfterSetupR717_TheCloseRunsAgainAfterAnUpdate
after_setup_r717_test.go:232: the update did not re-run the close: closes=1 open=true
--- FAIL: TestAfterSetupR717_TheCloseRunsAgainAfterAnUpdate (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.017s
FAIL
# exit code: 1
@@ -0,0 +1,14 @@
# R-717 red-proof: TestAfterSetupR717_AFailedCloseIsRetriedNotTrusted
# mutation in controller/internal/stacks/signup_block.go:
# --- removed
# gap = nativeLockOpenRetry
# +++ put instead
# // RED-PROOF: the 30-minute retry also after a window
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_AFailedCloseIsRetriedNotTrusted$ -count=1 -v
=== RUN TestAfterSetupR717_AFailedCloseIsRetriedNotTrusted
after_setup_r717_test.go:210: the loop did not retry the close
--- FAIL: TestAfterSetupR717_AFailedCloseIsRetriedNotTrusted (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.012s
FAIL
# exit code: 1
@@ -0,0 +1,19 @@
# R-717 red-proof: TestAfterSetupR717_AFailedOpenStaysClosedAndSaysSo
# mutation in controller/internal/stacks/after_setup.go:
# --- removed
# if cerr := m.applyNativeLock(name, true, "an opening that failed"); cerr != nil {
# +++ put instead
# if cerr := error(nil); cerr != nil { // RED-PROOF: no re-close after a failed open
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_AFailedOpenStaysClosedAndSaysSo$ -count=1 -v
=== RUN TestAfterSetupR717_AFailedOpenStaysClosedAndSaysSo
after_setup_r717_test.go:170: a failed open left the switch OPEN
[INFO] [stacks] gapp: setup gate CLOSED before the first start — only the household reaches [gapp.example.hu] until the first setup is done
[INFO] [stacks] gapp: setup gate OPENED by household — the app is reached as without a gate
[INFO] [stacks] gapp: the app's own sign-up switch CLOSED (the gate opened (household); env [], command true)
[ERROR] [stacks] gapp: the app's own sign-up switch could NOT be opened (the household's window): after_setup open_command did not report "SIGNUP-OPENED" (err exit 1) — closing it again; a new family member cannot sign up in the app itself
[INFO] [stacks] gapp: the household opened sign-up until 2026-10-06T13:27:46Z — the loop closes it again
--- FAIL: TestAfterSetupR717_AFailedOpenStaysClosedAndSaysSo (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.010s
FAIL
# exit code: 1
@@ -0,0 +1,21 @@
# R-717 red-proof: TestAfterSetupR717_NoOpenCommandLogsOnceAndKeepsTheWindow
# mutation in controller/internal/stacks/after_setup.go:
# --- removed
# if _, seen := m.nativeOpenMissingLogged.LoadOrStore(name, true); !seen {
# +++ put instead
# if seen := false; !seen { // RED-PROOF: no once-guard
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_NoOpenCommandLogsOnceAndKeepsTheWindow$ -count=1 -v
=== RUN TestAfterSetupR717_NoOpenCommandLogsOnceAndKeepsTheWindow
after_setup_r717_test.go:253: the missing open_command was logged 2 times, want once:
[INFO] [stacks] gapp: setup gate CLOSED before the first start — only the household reaches [gapp.example.hu] until the first setup is done
[INFO] [stacks] gapp: setup gate OPENED by household — the app is reached as without a gate
[INFO] [stacks] gapp: the app's own sign-up switch CLOSED (the gate opened (household); env [], command true)
[WARN] [stacks] gapp: the template closes the app's own sign-up switch with a command but has no open_command — the household's window cannot reopen it (only the address block opens)
[INFO] [stacks] gapp: the household opened sign-up until 2026-10-06T13:27:53Z — the loop closes it again
[WARN] [stacks] gapp: the template closes the app's own sign-up switch with a command but has no open_command — the household's window cannot reopen it (only the address block opens)
[INFO] [stacks] gapp: the household opened sign-up until 2026-10-06T13:27:53Z — the loop closes it again
--- FAIL: TestAfterSetupR717_NoOpenCommandLogsOnceAndKeepsTheWindow (0.00s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.011s
FAIL
# exit code: 1
@@ -0,0 +1,16 @@
# R-717 red-proof: TestAfterSetupR717_OpenThenCloseEachRunOnce
# mutation in controller/internal/stacks/after_setup.go:
# --- removed
# if err == nil && len(spec.OpenCommand) > 0 {
# err = m.runAfterSetupCommand(name, dir, spec, spec.OpenCommand, spec.OpenSuccess, "open_command")
# }
# +++ put instead
# // RED-PROOF: the open_command is never run (the code before R-717)
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_OpenThenCloseEachRunOnce$ -count=1 -v
=== RUN TestAfterSetupR717_OpenThenCloseEachRunOnce
after_setup_r717_test.go:97: the window did not open the app's own switch: opens=0 closes=1 open=false
--- FAIL: TestAfterSetupR717_OpenThenCloseEachRunOnce (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.012s
FAIL
# exit code: 1
@@ -0,0 +1,22 @@
# R-717 red-proof: TestAfterSetupR717_ARestartInsideTheWindowEndsClosed
# mutation in controller/internal/stacks/after_setup.go:
# --- removed
# m.mutateAppConfig(name, dir, "after_setup_opening", func(cfg *AppConfig) bool {
# if cfg.SetupGate == nil {
# return false
# }
# cfg.SetupGate.NativeLock = NativeLockOpening
# return true
# })
# if c := LoadAppConfig(dir); c == nil || c.SetupGate == nil || c.SetupGate.NativeLock != NativeLockOpening {
# +++ put instead
# // RED-PROOF: no "opening" mark before anything opens
# if c := LoadAppConfig(dir); c == nil || c.SetupGate == nil {
$ cd controller && go test ./internal/stacks/ -run ^TestAfterSetupR717_ARestartInsideTheWindowEndsClosed$ -count=1 -v
=== RUN TestAfterSetupR717_ARestartInsideTheWindowEndsClosed
after_setup_r717_test.go:149: a restart left sign-up OPEN in the app past its window (record at the crash: native="applied")
--- FAIL: TestAfterSetupR717_ARestartInsideTheWindowEndsClosed (0.01s)
FAIL
FAIL gitea.dooplex.hu/admin/felhom-controller/internal/stacks 0.014s
FAIL
# exit code: 1
@@ -0,0 +1,14 @@
Tue Oct 6 12:57:05 UTC 2026
wger-files Up 4 minutes (healthy)
wger Up 4 minutes (healthy)
wger-files /static/css/workout-manager.css: 200 2442B text/css
wger-files /static/bootstrap-compiled.css: 200 277029B
control - the same file straight at wger (Django, DEBUG=False): 404
login page links: /static/css/workout-manager.7007d84ce531.css /static/bootstrap-compiled.80a6279921f8.css /static/css/bootstrap-custom.400ad578123c.css
static volume: 283M; files: 22725
wger-files 9.938MiB / 32MiB
wger 246MiB / 384MiB
the page's own link /static/css/workout-manager.7007d84ce531.css: 200 2481B
the page's own link /static/bootstrap-compiled.80a6279921f8.css: 200 277042B
the page's own link /static/css/bootstrap-custom.400ad578123c.css: 200 1006B
a directory listing is refused: 403
@@ -0,0 +1,126 @@
[12:50:17] scratch drive folders cleared before FROM (R-656): none existed
[12:50:17] MV-wger: deploying wger at FROM {'wger': 'wger/server:2.7'}
[12:52:15] FROM settled=True in 92.6s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[12:52:15] fixture: the BOX walk's own (Wger), through upgrade_boxport
[12:52:16] wger: the generated admin password does not log in (POST /en/user/login -> 200) — running the template's own after_install command (the app's CLI, as the product does after an install)
[12:52:18] wger: after_install :: version 8.3.1, blocking by username FELHOM_AFTER_INSTALL_OK
[12:52:19] wger: POST /api/v2/weightentry/ http=201
[12:52:19] wger: readback of the seeded weight entry http=200 found=True
[12:52:19] C1 (seed reads back BEFORE): True
[12:52:20] MV-wger: swapping to TO {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
[12:52:36] TO up -d rc=0
[12:53:07] TO settled=True in 31.1s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[12:53:07] migration lines observed: 6
[12:53:08] wger: readback of the seeded weight entry http=200 found=True
[12:53:08] RESULT (seed reads back AFTER): True
[12:53:08] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:8000
[12:53:23] + 15s wger=284M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=284
[12:53:38] + 30s wger=284M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=574
[12:53:53] + 46s wger=281M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=863
[12:54:09] + 61s wger=262M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1152
[12:54:24] + 76s wger=259M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1441
[12:54:39] + 91s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1731
[12:54:54] + 106s wger=257M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2021
[12:55:09] + 121s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2309
[12:55:24] + 136s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2600
[12:55:40] + 152s wger=257M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=2887
[12:55:55] + 167s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3175
[12:56:10] + 182s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3461
[12:56:25] + 197s wger=257M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3747
[12:56:40] + 212s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=4037
[12:56:55] + 227s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=4325
[12:57:11] + 242s wger=259M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=15M kills=0 rs=0 reqs=4613
[12:57:26] + 258s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=4897
[12:57:41] + 273s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5184
[12:57:56] + 288s wger=258M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5471
[12:58:11] + 303s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5763
[12:58:26] + 318s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6051
[12:58:41] + 334s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6335
[12:58:57] + 349s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6623
[12:59:12] + 364s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6912
[12:59:27] + 379s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7197
[12:59:42] + 394s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7483
[12:59:57] + 409s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7769
[13:00:12] + 424s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8058
[13:00:28] + 440s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8347
[13:00:43] + 455s wger=260M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8635
[13:00:58] + 470s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8920
[13:01:13] + 485s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9208
[13:01:28] + 500s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9497
[13:01:43] + 515s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9787
[13:01:59] + 530s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10074
[13:02:14] + 546s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10364
[13:02:29] + 561s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10653
[13:02:44] + 576s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10942
[13:02:59] + 591s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=11230
[13:03:14] + 606s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=11518
[13:03:15] memory watch: killed=False tight=[] requests=11519 codes={'302': 11519}
[13:03:15] MV-wger: ABORT — putting the FROM images back
[13:03:58] wger: readback of the seeded weight entry http=200 found=True
[13:03:58] ABORT: app came back in 31.3s; data present=True
{
"harness_version": 5,
"edge": "MV-wger",
"app": "wger",
"note": "definition step to wger@files",
"from": {
"wger": "wger/server:2.7"
},
"to": {
"wger": "wger/server:2.7",
"wger-files": "nginx:1.30.5-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "\u001b[2Kwger | Performing database migrations",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"memory": {
"soak_s": 606.7,
"requested_s": 600,
"requests": 11519,
"codes": {
"302": 11519
},
"first_kill": null,
"containers": {
"wger": {
"limit": 402653184,
"peak": 402653184,
"peak_pct": 1.0,
"anon_peak_sampled": 202338304,
"anon_peak_pct": 0.503,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
},
"wger-files": {
"limit": 33554432,
"peak": 16572416,
"peak_pct": 0.494,
"anon_peak_sampled": 5337088,
"anon_peak_pct": 0.159,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
}
},
"unmeasured": [],
"load": "reached"
},
"marks": [],
"bench_overrides": null,
"duration_s": 31.1,
"measured_at": "2026-10-06T13:03:58Z",
"evidence": "evidence/MV-wger",
"scratch_cleared": [],
"files_changed": [],
"files_changed_detail": [],
"files_ignored": [],
"total_s": 821.0
}
@@ -0,0 +1,14 @@
{
"wger": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"wger-files": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,245 @@
wger | *** Using settings from env: settings.main
wger | level=INFO ts=2026-10-06 15:03:27,479 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | Performing database migrations
wger | level=INFO ts=2026-10-06 15:03:29,033 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger | Operations to perform:
wger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
wger | Running migrations:
wger | No migrations to apply.
wger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
wger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
wger | level=INFO ts=2026-10-06 15:03:32,185 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger | Set site URL to fitness.gate.invalid
wger | Using django's development server on port 8000...
wger | level=INFO ts=2026-10-06 15:03:34,510 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | level=INFO ts=2026-10-06 15:03:35,797 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | level=INFO ts=2026-10-06 15:03:35,810 module=autoreload path=/home/wger/.local/lib/python3.12/site-packages/django/utils/autoreload.py line=681 message=Watching for file changes with StatReloader
wger | Performing system checks...
wger |
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger |
wger | System check identified 1 issue (0 silenced).
wger | October 06, 2026 - 15:03:36
wger | Django version 6.0.8, using settings 'settings.main'
wger | Starting development server at http://0.0.0.0:8000/
wger | Quit the server with CONTROL-C.
wger |
wger | WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
wger | For more information on production servers see: https://docs.djangoproject.com/en/6.0/howto/deployment/
wger | Can't find name for static file reference: images/logos/logo-social.png. This is expected in some node packages.
wger | Can't find name for static file reference: fontawesomefree/css/all.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/workout-manager.css. This is expected in some node packages.
wger | Can't find name for static file reference: bootstrap-compiled.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/bootstrap-custom.css. This is expected in some node packages.
wger | Can't find name for static file reference: images/favicon.png. This is expected in some node packages.
wger | [06/Oct/2026 15:03:56] "HEAD / HTTP/1.1" 302 0
wger | [06/Oct/2026 15:03:56] "HEAD /en/ HTTP/1.1" 302 0
wger | Can't find name for static file reference: images/logos/logo-social.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/favicon.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/logo-font.svg. This is expected in some node packages.
wger | Can't find name for static file reference: bootstrap-compiled.css. This is expected in some node packages.
wger | Can't find name for static file reference: fontawesomefree/css/all.min.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/landing_page.css. This is expected in some node packages.
wger | Can't find name for static file reference: node/bootstrap/dist/js/bootstrap.bundle.min.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/language.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/forms.js. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/logo-font.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/hero.avif. This is expected in some node packages.
wger | Can't find name for static file reference: images/hero.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-1.avif. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-1.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-2.avif. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-2.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-3.avif. This is expected in some node packages.
wger | Can't find name for static file reference: images/screens-3.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/community.avif. This is expected in some node packages.
wger | Can't find name for static file reference: images/community.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/dumbbell.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/code.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/fork.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/logo-font-inverse.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/bg.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ca.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/cs.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/de.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/el.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-au.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-gb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ar.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-co.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-mx.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ni.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ve.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fi.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/he.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/hr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/it.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ko.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/mk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt-br.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ro.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ru.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sv.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ta.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/th.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/tr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/uk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hans.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hant.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/play-store/badge.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/app-store/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/flathub/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/fdroid/get-it-on.svg. This is expected in some node packages.
wger | [06/Oct/2026 15:03:56] "HEAD /en/software/features HTTP/1.1" 200 0
wger | Can't find name for static file reference: images/logos/logo-social.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/favicon.png. This is expected in some node packages.
wger | Can't find name for static file reference: css/workout-manager.css. This is expected in some node packages.
wger | Can't find name for static file reference: bootstrap-compiled.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/bootstrap-custom.css. This is expected in some node packages.
wger | Can't find name for static file reference: fontawesomefree/css/all.min.css. This is expected in some node packages.
wger | Can't find name for static file reference: node/@wger-project/react-components/build/assets/index.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/language-menu.css. This is expected in some node packages.
wger | Can't find name for static file reference: node/bootstrap/dist/js/bootstrap.bundle.min.js. This is expected in some node packages.
wger | Can't find name for static file reference: node/htmx.org/dist/htmx.min.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/nutrition.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/language.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/forms.js. This is expected in some node packages.
wger | Can't find name for static file reference: node/@wger-project/react-components/build/main.js. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/logo-bg-white.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/play-store/badge.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/app-store/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/flathub/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/bg.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ca.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/cs.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/de.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/el.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-au.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-gb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ar.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-co.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-mx.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ni.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ve.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fi.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/he.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/hr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/it.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ko.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/mk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt-br.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ro.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ru.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sv.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ta.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/th.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/tr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/uk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hans.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hant.svg. This is expected in some node packages.
wger | Can't find name for static file reference: mfa/js/webauthn-json.js. This is expected in some node packages.
wger | Can't find name for static file reference: mfa/js/webauthn.js. This is expected in some node packages.
wger | Can't find name for static file reference: account/js/onload.js. This is expected in some node packages.
wger | [06/Oct/2026 15:03:57] "GET /en/user/login HTTP/1.1" 200 39022
wger | level=WARNING ts=2026-10-06 15:03:57,689 module=log path=/home/wger/.local/lib/python3.12/site-packages/django/utils/log.py line=249 message=Forbidden: /api/v2/weightentry/
wger | [06/Oct/2026 15:03:57] "GET /api/v2/weightentry/?weight=60.37 HTTP/1.1" 403 58
wger | Can't find name for static file reference: images/logos/logo-social.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/favicon.png. This is expected in some node packages.
wger | Can't find name for static file reference: css/workout-manager.css. This is expected in some node packages.
wger | Can't find name for static file reference: bootstrap-compiled.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/bootstrap-custom.css. This is expected in some node packages.
wger | Can't find name for static file reference: fontawesomefree/css/all.min.css. This is expected in some node packages.
wger | Can't find name for static file reference: node/@wger-project/react-components/build/assets/index.css. This is expected in some node packages.
wger | Can't find name for static file reference: css/language-menu.css. This is expected in some node packages.
wger | Can't find name for static file reference: node/bootstrap/dist/js/bootstrap.bundle.min.js. This is expected in some node packages.
wger | Can't find name for static file reference: node/htmx.org/dist/htmx.min.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/nutrition.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/language.js. This is expected in some node packages.
wger | Can't find name for static file reference: js/forms.js. This is expected in some node packages.
wger | Can't find name for static file reference: node/@wger-project/react-components/build/main.js. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/logo-bg-white.png. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/play-store/badge.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/app-store/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/logos/flathub/black.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/bg.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ca.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/cs.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/de.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/el.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-au.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/en-gb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ar.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-co.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-mx.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ni.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/es-ve.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fi.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/fr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/he.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/hr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/it.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ko.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/mk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/nb.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/pt-br.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ro.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ru.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sl.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/sv.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/ta.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/th.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/tr.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/uk.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hans.svg. This is expected in some node packages.
wger | Can't find name for static file reference: images/icons/flags/zh-hant.svg. This is expected in some node packages.
wger | Can't find name for static file reference: mfa/js/webauthn-json.js. This is expected in some node packages.
wger | Can't find name for static file reference: mfa/js/webauthn.js. This is expected in some node packages.
wger | Can't find name for static file reference: account/js/onload.js. This is expected in some node packages.
wger | [06/Oct/2026 15:03:57] "GET /en/user/login HTTP/1.1" 200 39022
wger | level=INFO ts=2026-10-06 15:03:58,072 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=295 message=AXES: Successful login by {username: "********************", ip_address: "********************", user_agent: "curl/8.14.1", path_info: "/en/user/login"}.
wger | level=INFO ts=2026-10-06 15:03:58,086 module=database path=/home/wger/.local/lib/python3.12/site-packages/axes/handlers/database.py line=425 message=AXES: Cleaned up 1 expired access attempts from database that were older than 2026-10-06 12:58:57.858736+00:00
wger | Can't find name for static file reference: images/logos/logo-social.png. This is expected in some node packages.
wger | [06/Oct/2026 15:03:58] "POST /en/user/login HTTP/1.1" 302 0
wger | [06/Oct/2026 15:03:58] "GET /api/v2/weightentry/?weight=199.99 HTTP/1.1" 200 52
wger | [06/Oct/2026 15:03:58] "GET /api/v2/weightentry/?weight=60.37 HTTP/1.1" 200 158
@@ -0,0 +1,6 @@
wger | Performing database migrations
wger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
wger | Running migrations:
wger | No migrations to apply.
wger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
wger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
@@ -0,0 +1,60 @@
[12:50:17] scratch drive folders cleared before FROM (R-656): none existed
[12:50:17] MV-wger: deploying wger at FROM {'wger': 'wger/server:2.7'}
[12:52:15] FROM settled=True in 92.6s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[12:52:15] fixture: the BOX walk's own (Wger), through upgrade_boxport
[12:52:16] wger: the generated admin password does not log in (POST /en/user/login -> 200) — running the template's own after_install command (the app's CLI, as the product does after an install)
[12:52:18] wger: after_install :: version 8.3.1, blocking by username FELHOM_AFTER_INSTALL_OK
[12:52:19] wger: POST /api/v2/weightentry/ http=201
[12:52:19] wger: readback of the seeded weight entry http=200 found=True
[12:52:19] C1 (seed reads back BEFORE): True
[12:52:20] MV-wger: swapping to TO {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
[12:52:36] TO up -d rc=0
[12:53:07] TO settled=True in 31.1s :: {"wger": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}, "wger-files": {"status": "running", "health": "healthy", "restarts": 0, "exit": 0}}
[12:53:07] migration lines observed: 6
[12:53:08] wger: readback of the seeded weight entry http=200 found=True
[12:53:08] RESULT (seed reads back AFTER): True
[12:53:08] memory watch: 600s, 4 callers on 1 path(s) at 172.18.0.2:8000
[12:53:23] + 15s wger=284M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=284
[12:53:38] + 30s wger=284M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=574
[12:53:53] + 46s wger=281M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=863
[12:54:09] + 61s wger=262M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1152
[12:54:24] + 76s wger=259M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1441
[12:54:39] + 91s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=1731
[12:54:54] + 106s wger=257M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2021
[12:55:09] + 121s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2309
[12:55:24] + 136s wger=258M/384M peak=384M kills=0 rs=0 wger-files=11M/32M peak=14M kills=0 rs=0 reqs=2600
[12:55:40] + 152s wger=257M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=2887
[12:55:55] + 167s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3175
[12:56:10] + 182s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3461
[12:56:25] + 197s wger=257M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=3747
[12:56:40] + 212s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=4037
[12:56:55] + 227s wger=258M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=14M kills=0 rs=0 reqs=4325
[12:57:11] + 242s wger=259M/384M peak=384M kills=0 rs=0 wger-files=12M/32M peak=15M kills=0 rs=0 reqs=4613
[12:57:26] + 258s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=4897
[12:57:41] + 273s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5184
[12:57:56] + 288s wger=258M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5471
[12:58:11] + 303s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=5763
[12:58:26] + 318s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6051
[12:58:41] + 334s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6335
[12:58:57] + 349s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6623
[12:59:12] + 364s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=6912
[12:59:27] + 379s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7197
[12:59:42] + 394s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7483
[12:59:57] + 409s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=7769
[13:00:12] + 424s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8058
[13:00:28] + 440s wger=259M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8347
[13:00:43] + 455s wger=260M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8635
[13:00:58] + 470s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=8920
[13:01:13] + 485s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9208
[13:01:28] + 500s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9497
[13:01:43] + 515s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=9787
[13:01:59] + 530s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10074
[13:02:14] + 546s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10364
[13:02:29] + 561s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10653
[13:02:44] + 576s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=10942
[13:02:59] + 591s wger=257M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=11230
[13:03:14] + 606s wger=256M/384M peak=384M kills=0 rs=0 wger-files=13M/32M peak=15M kills=0 rs=0 reqs=11518
[13:03:15] memory watch: killed=False tight=[] requests=11519 codes={'302': 11519}
[13:03:15] MV-wger: ABORT — putting the FROM images back
[13:03:58] wger: readback of the seeded weight entry http=200 found=True
[13:03:58] ABORT: app came back in 31.3s; data present=True
@@ -0,0 +1,71 @@
wger-files | /docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
wger | *** Using settings from env: settings.main
wger-files | /docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
wger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
wger-files | 10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
wger-files | 10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
wger-files | /docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
wger | level=INFO ts=2026-10-06 14:52:37,325 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | Running in production mode, running collectstatic now
wger | level=INFO ts=2026-10-06 14:52:39,095 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger |
wger | 11362 static files copied to '/home/wger/static', 11362 post-processed.
wger | Performing database migrations
wger | level=INFO ts=2026-10-06 14:52:57,357 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger | Operations to perform:
wger | Apply all migrations: account, actstream, allauth_idp_oidc, auth, authtoken, axes, config, contenttypes, core, easy_thumbnails, exercises, gallery, gym, mailer, manager, measurements, mfa, nutrition, sessions, sites, socialaccount, token_blacklist, trophies, weight
wger | Running migrations:
wger | No migrations to apply.
wger | Your models in app(s): 'exercises', 'gallery' have changes that are not yet reflected in a migration, and so won't be applied.
wger | Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
wger | level=INFO ts=2026-10-06 14:53:00,351 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger | Set site URL to fitness.gate.invalid
wger | Using django's development server on port 8000...
wger | level=INFO ts=2026-10-06 14:53:02,636 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger | level=INFO ts=2026-10-06 14:53:03,920 module=apps path=/home/wger/.local/lib/python3.12/site-packages/axes/apps.py line=53 message=AXES: BEGIN version 8.3.1, blocking by username
wger-files | /docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
wger-files | /docker-entrypoint.sh: Configuration complete; ready for start up
wger-files | 2026/10/06 12:52:36 [notice] 1#1: using the "epoll" event method
wger-files | 2026/10/06 12:52:36 [notice] 1#1: nginx/1.30.5
wger-files | 2026/10/06 12:52:36 [notice] 1#1: built by gcc 15.2.0 (Alpine 15.2.0)
wger-files | 2026/10/06 12:52:36 [notice] 1#1: OS: Linux 7.0.14-20-pve
wger-files | 2026/10/06 12:52:36 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 524288:524288
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker processes
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 30
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 31
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 32
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 33
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 34
wger-files | 2026/10/06 12:52:36 [notice] 1#1: start worker process 35
wger-files | 127.0.0.1 - - [06/Oct/2026:12:53:06 +0000] "GET / HTTP/1.1" 200 896 "-" "Wget" "-"
wger | level=INFO ts=2026-10-06 14:53:03,931 module=autoreload path=/home/wger/.local/lib/python3.12/site-packages/django/utils/autoreload.py line=681 message=Watching for file changes with StatReloader
wger | Performing system checks...
wger |
wger | System check identified some issues:
wger |
wger | WARNINGS:
wger | ?: (axes.W006) AXES_LOCKOUT_PARAMETERS does not contain 'ip_address'. This configuration allows attackers to bypass rate limits by rotating User-Agents or Cookies.
wger | HINT: Add 'ip_address' to AXES_LOCKOUT_PARAMETERS.
wger |
wger | System check identified 1 issue (0 silenced).
wger | October 06, 2026 - 14:53:04
wger | Django version 6.0.8, using settings 'settings.main'
wger | Starting development server at http://0.0.0.0:8000/
wger | Quit the server with CONTROL-C.
wger |
wger | WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI or ASGI server instead.
wger | For more information on production servers see: https://docs.djangoproject.com/en/6.0/howto/deployment/
wger | [06/Oct/2026 14:53:05] "HEAD / HTTP/1.1" 302 0
wger | [06/Oct/2026 14:53:05] "HEAD /en/ HTTP/1.1" 302 0
wger | [06/Oct/2026 14:53:06] "HEAD /en/software/features HTTP/1.1" 200 0
@@ -0,0 +1,14 @@
{
"wger": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
},
"wger-files": {
"status": "running",
"health": "healthy",
"restarts": 0,
"exit": 0
}
}
@@ -0,0 +1,66 @@
{
"harness_version": 5,
"edge": "MV-wger",
"app": "wger",
"note": "definition step to wger@files",
"from": {
"wger": "wger/server:2.7"
},
"to": {
"wger": "wger/server:2.7",
"wger-files": "nginx:1.30.5-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"migration_observed": "\u001b[2Kwger | Performing database migrations",
"abort": "starts-and-serves",
"abort_detail": null,
"engine_state_after": null,
"memory": {
"soak_s": 606.7,
"requested_s": 600,
"requests": 11519,
"codes": {
"302": 11519
},
"first_kill": null,
"containers": {
"wger": {
"limit": 402653184,
"peak": 402653184,
"peak_pct": 1.0,
"anon_peak_sampled": 202338304,
"anon_peak_pct": 0.503,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
},
"wger-files": {
"limit": 33554432,
"peak": 16572416,
"peak_pct": 0.494,
"anon_peak_sampled": 5337088,
"anon_peak_pct": 0.159,
"oom_kills": 0,
"restarts": 0,
"oomkilled_flag": false,
"measured": true
}
},
"unmeasured": [],
"load": "reached"
},
"marks": [],
"bench_overrides": null,
"duration_s": 31.1,
"measured_at": "2026-10-06T13:03:58Z",
"evidence": "evidence/MV-wger",
"scratch_cleared": [],
"files_changed": [],
"files_changed_detail": [],
"files_ignored": [],
"total_s": 821.0
}
@@ -0,0 +1,32 @@
{
"app": "wger",
"venue": "box 9202 (drill catalog, controller 0.301.0 test image), the product's guarded Update (R-762 definition step)",
"from": {
"wger": "wger/server:2.7"
},
"to": {
"wger": "wger/server:2.7",
"wger-files": "nginx:1.30.5-alpine"
},
"verdict": "proven",
"seed_read_before": true,
"seed_read_after": true,
"healthy_after": true,
"duration_s": 59.4,
"final_phase": "done",
"measured_at": "2026-10-06T13:35:32Z",
"note": "written by hand from step.txt: wgerstep.py crashed AFTER the step when it decoded the PNG body as text; the after-checks were re-read binary-safe (step.txt '[5] (finished by hand\u2026)')",
"r762": {
"photo": "/media/gallery/1/36151595-cdde-4493-849d-72a4ff32d493.png",
"photo_before": "404",
"photo_after": "200 178 image/png",
"css_before": "404 x3",
"css_after": "200 x3 (the login page's own hashed links)",
"control_unknown_static": "404",
"memory": [
"wger-files 7.613MiB / 32MiB",
"wger 241MiB / 384MiB"
]
},
"evidence": "felhom.eu/documentation/audits/design-build-2026-10-06/F/box/step.txt"
}
@@ -0,0 +1,27 @@
drill: c182492 DRILL wger: un-hidden for the R-762 box step (DRILL only)
[1] installed, pinned={'wger': 'wger/server:2.7'}
wger: the generated admin password does not log in (POST /en/user/login -> 403) — running the template's own after_install command (the app's CLI, as the product does after an install)
wger: after_install :: version 8.3.1, blocking by username FELHOM_AFTER_INSTALL_OK
wger: POST /api/v2/weightentry/ http=201
wger: readback of the seeded weight entry http=200 found=True
[2] POST /api/v2/gallery/ (a 178 B PNG) -> 201
[2] the photo /media/gallery/1/36151595-cdde-4493-849d-72a4ff32d493.png read BEFORE the step -> 404; CSS before {'/static/css/workout-manager.css': '404', '/static/bootstrap-compiled.css': '404', '/static/css/bootstrap-custom.css': '404'}
drill: 095a292 DRILL wger: the R-762 definition (wger-files) + its ladder entry (box proof) | to: {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'}
phase +0.0s backing-up | err=None
phase +12.3s safety-dump | err=None
phase +13.3s pulling | err=None
phase +16.4s copying | err=None
phase +27.7s starting | err=None
phase +28.7s verifying | err=None
phase +59.4s done | err=None
wger: readback of the seeded weight entry http=200 found=True
[5] (finished by hand after the script's text decode of the PNG body failed) the photo /media/gallery/1/36151595-cdde-4493-849d-72a4ff32d493.png -> 200 178 image/png (posted: 178 B)
[5] the login page's own CSS links: {'/static/css/workout-manager.7007d84ce531.css': '200 2481 text/css', '/static/bootstrap-compiled.80a6279921f8.css': '200 277042 text/css', '/static/css/bootstrap-custom.400ad578123c.css': '200 1006 text/css'}
[5] control, an unknown file under /static/ -> 404 153 text/html
[5] memory: ['wger-files 7.613MiB / 32MiB', 'wger 241MiB / 384MiB']
[5] pinned: {'wger': 'wger/server:2.7', 'wger-files': 'nginx:1.30.5-alpine'} state: running
[5] badges after: {'hu': [{'title': 'Ez az alkalmazás a legfrissebb elérhető változatot futtatja.', 'text': 'Naprakész'}], 'en': [{'title': 'This app is running the newest version available.', 'text': 'Up to date'}]}
15:37:45 [X] stop -> 200 {'ok': True, 'message': 'Stack wger stop completed'}
15:38:17 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'wger', 'volumes_removed': ['wger_wger_data', 'wger_wger_media', 'wger_wger_static'], 'hdd_paths_removed': [], 'hdd_paths_prese
15:38:25 [X] after remove: deployed=False leftovers='/opt/docker/stacks/wger'
remove -> 200
@@ -0,0 +1,65 @@
test_all_three_conditions_allow (__main__.BenchAdminSeedGuard.test_all_three_conditions_allow) ... ok
test_each_condition_alone_refuses (__main__.BenchAdminSeedGuard.test_each_condition_alone_refuses) ... ok
test_the_bench_seeds_through_the_admin_invite_and_never_shows_the_token (__main__.BenchAdminSeedGuard.test_the_bench_seeds_through_the_admin_invite_and_never_shows_the_token) ... /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py:177: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/.felhom-h-mxcpia_3' mode='r' encoding='utf-8'>
headers.append(open(extra[i + 1][1:]).read().strip())
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
test_the_bench_venue_names_itself (__main__.BenchAdminSeedGuard.test_the_bench_venue_names_itself) ... ok
test_the_box_walk_never_signs_in_as_admin (__main__.BenchAdminSeedGuard.test_the_box_walk_never_signs_in_as_admin) ... ok
test_a_household_shaped_box_is_never_seeded (__main__.BoxAdminSeedGuard.test_a_household_shaped_box_is_never_seeded) ... ok
test_a_refused_invite_is_inconclusive (__main__.BoxAdminSeedGuard.test_a_refused_invite_is_inconclusive) ... ok
test_all_conditions_allow (__main__.BoxAdminSeedGuard.test_all_conditions_allow) ... ok
test_each_condition_alone_refuses (__main__.BoxAdminSeedGuard.test_each_condition_alone_refuses) ... ok
test_only_a_drill_address_is_invited (__main__.BoxAdminSeedGuard.test_only_a_drill_address_is_invited) ... ok
test_the_test_box_seeds_through_the_invite_inside_the_box (__main__.BoxAdminSeedGuard.test_the_test_box_seeds_through_the_invite_inside_the_box) ... ok
test_names_changed_added_removed_and_nothing_else (__main__.ChangedFiles.test_names_changed_added_removed_and_nothing_else) ... ok
test_never_a_bare_root_never_outside (__main__.ClearScratch.test_never_a_bare_root_never_outside) ... /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py:52: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/bench-scratch-cy0e4bk1/hdd/appdata/x/config/config.php' mode='w' encoding='utf-8'>
open(p, "w").write("last run")
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
test_the_apps_own_folders_are_cleared_and_said (__main__.ClearScratch.test_the_apps_own_folders_are_cleared_and_said) ... /mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py:52: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/bench-scratch-eox1rlxi/hdd/appdata/nextcloud/config/config.php' mode='w' encoding='utf-8'>
open(p, "w").write("last run")
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py:52: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/bench-scratch-eox1rlxi/hdd/appdata/immich/config/config.php' mode='w' encoding='utf-8'>
open(p, "w").write("last run")
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
test_nothing_new_is_refused (__main__.DefinitionEdge.test_nothing_new_is_refused) ... ok
test_the_edge_names_the_new_service (__main__.DefinitionEdge.test_the_edge_names_the_new_service) ... ERROR
test_the_to_render_reads_the_new_definition (__main__.DefinitionEdge.test_the_to_render_reads_the_new_definition) ... FAIL
test_answers_of_any_code_are_the_app_answering (__main__.LoadVerdict.test_answers_of_any_code_are_the_app_answering) ... ok
test_every_request_errored_is_inconclusive (__main__.LoadVerdict.test_every_request_errored_is_inconclusive) ... ok
test_under_half_is_inconclusive_and_none_is_inconclusive (__main__.LoadVerdict.test_under_half_is_inconclusive_and_none_is_inconclusive) ... ok
test_a_listed_name_that_grew_or_vanished_still_marks (__main__.MarkerIgnore.test_a_listed_name_that_grew_or_vanished_still_marks) ... ok
test_a_moved_tree_the_file_walk_cannot_name_keeps_the_mark (__main__.MarkerIgnore.test_a_moved_tree_the_file_walk_cannot_name_keeps_the_mark) ... ok
test_an_unlisted_changed_file_still_marks (__main__.MarkerIgnore.test_an_unlisted_changed_file_still_marks) ... ok
test_every_entry_has_a_reason (__main__.MarkerIgnore.test_every_entry_has_a_reason) ... ok
test_the_list_belongs_to_its_app (__main__.MarkerIgnore.test_the_list_belongs_to_its_app) ... ok
test_the_six_measured_markers_do_not_mark_and_each_carries_a_reason (__main__.MarkerIgnore.test_the_six_measured_markers_do_not_mark_and_each_carries_a_reason) ... ok
test_env_is_0600_and_shredded (__main__.SecretHygiene.test_env_is_0600_and_shredded) ... ok
test_evidence_files_are_redacted (__main__.SecretHygiene.test_evidence_files_are_redacted) ... ok
test_redact_longest_first (__main__.SecretHygiene.test_redact_longest_first) ... ok
test_secret_values_are_the_generated_fields_and_the_seed_password (__main__.SecretHygiene.test_secret_values_are_the_generated_fields_and_the_seed_password) ... ok
======================================================================
ERROR: test_the_edge_names_the_new_service (__main__.DefinitionEdge.test_the_edge_names_the_new_service)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py", line 376, in test_the_edge_names_the_new_service
self.assertEqual(e["to_template"], "app1@new")
~^^^^^^^^^^^^^^^
KeyError: 'to_template'
======================================================================
FAIL: test_the_to_render_reads_the_new_definition (__main__.DefinitionEdge.test_the_to_render_reads_the_new_definition)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts/test_upgrade_bench.py", line 384, in test_the_to_render_reads_the_new_definition
self.assertIn("nginx:1.30.5-alpine", (wd / "docker-compose.yml").read_text())
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 'nginx:1.30.5-alpine' not found in 'services:\n app1:\n image: app/one:1.0\n'
----------------------------------------------------------------------
Ran 30 tests in 0.028s
FAILED (failures=1, errors=1)
@@ -0,0 +1,21 @@
== vouch 2026-10-06T14:04:05Z: agent 0.149.0, golden 0.301.0, min_agent 0.131.0
HTTP/1.1 303 See Other
Location: /configuration?flash=artifacts_set
== floors 2026-10-06T14:04:35Z: 0.301.0
demo-hp: Location: /customers/demo-hp?flash=floor_set
demo-felhom: Location: /customers/demo-felhom?flash=floor_set
tester-1: Location: /customers/tester-1?flash=floor_set
2026/10/06 16:04:35 [INFO] Artifact manifest set: agent=0.149.0 golden=0.301.0 min_agent="0.131.0" wrapper_sha=false bundle_sha="e182c82dcf4a67faa3bcb74dbe4ffa7b06e0b27dc8451cb7574d6339ce91ad66"
2026/10/06 16:04:35 [INFO] Customer demo-hp controller-version floor override set to "0.301.0" (declared MinAgent "")
2026/10/06 16:04:35 [INFO] Customer demo-felhom controller-version floor override set to "0.301.0" (declared MinAgent "")
2026/10/06 16:04:36 [INFO] Customer tester-1 controller-version floor override set to "0.301.0" (declared MinAgent "")
== floors again 2026-10-06T14:04:56Z: 0.301.0 with min_agent 0.131.0 (the first save declared none; allowed because the floor equals the golden, re-saved to match the 0.300.0 floors)
demo-hp: Location: /customers/demo-hp?flash=floor_set
demo-felhom: Location: /customers/demo-felhom?flash=floor_set
tester-1: Location: /customers/tester-1?flash=floor_set
2026/10/06 16:04:35 [INFO] Customer demo-hp controller-version floor override set to "0.301.0" (declared MinAgent "")
2026/10/06 16:04:35 [INFO] Customer demo-felhom controller-version floor override set to "0.301.0" (declared MinAgent "")
2026/10/06 16:04:36 [INFO] Customer tester-1 controller-version floor override set to "0.301.0" (declared MinAgent "")
2026/10/06 16:04:56 [INFO] Customer demo-hp controller-version floor override set to "0.301.0" (declared MinAgent "0.131.0")
2026/10/06 16:04:57 [INFO] Customer demo-felhom controller-version floor override set to "0.301.0" (declared MinAgent "0.131.0")
2026/10/06 16:04:57 [INFO] Customer tester-1 controller-version floor override set to "0.301.0" (declared MinAgent "0.131.0")
@@ -0,0 +1,158 @@
"""r638slice0.py <app> — R-638 slice 0, MEASUREMENT ONLY, on scratch 9202 (drill catalog). `09` §3 decision 154.
1 the drill's <app> template is put at the OLD definition (the commit before the R-462 move), ladder emptied;
2 install fresh through the product; seed through the app's own door; read back (C1); the live table list;
3 a DRILL commit moves it to the NEW definition with the one proven ladder entry; sync; the product's guarded
Update (its backing-up phase makes the Tier-2 copy at the OLD version); the table list after the migration;
4 the named unit restore of that pre-update copy through the household's button (POST /backup/restore);
5 positive observable: the controller's `Imported DB dump` line + the seed read back;
control from a different channel: the live table list against the table list READ FROM THE COPY'S OWN DUMP FILE
on the Tier-2 drive — every table the migration added must be GONE;
6 removed through the product (KEEP=1 keeps it).
Evidence: ../B/<app>.txt + ../B/<app>-tables.json."""
import json, os, re, subprocess, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP = sys.argv[1]
CFG = {
"docmost": {"sub": "docmost", "old": "6d8cd87^", "new": "6d8cd87", "db": "docmost-postgres",
"tables": "psql -U \"$POSTGRES_USER\" -d \"$POSTGRES_DB\" -Atc \"select tablename from pg_tables where schemaname='public' order by 1\""},
"romm": {"sub": "romm", "extra": {"HDD_PATH": "/mnt/felhom-drives/scratch_hdd"}, "old": "15f9ebf^", "new": "15f9ebf", "db": "romm-db",
"tables": "MYSQL_PWD=\"$MYSQL_PASSWORD\" mariadb -u\"$MYSQL_USER\" \"$MYSQL_DATABASE\" -Nse 'show tables'"},
}[APP]
LIVE = "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu"
D = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
HERE = os.path.dirname(os.path.abspath(__file__))
EV = os.path.join(HERE, "..", "B")
log = open(os.path.join(EV, f"{APP}.txt"), "a", buffering=1)
REC = {"app": APP}
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
def git(*a, cwd=D):
return subprocess.run(["git", "-C", cwd, *a], check=True, capture_output=True, text=True).stdout
def live_tables():
out = w.guest(f"docker exec -i {CFG['db']} sh -s 2>&1 <<'SQLX'\n{CFG['tables']}\nSQLX\n")
return sorted(t for t in out.split() if re.fullmatch(r"[A-Za-z0-9_]+", t))
def set_template(rev, ladder_entries, msg):
git("pull", "-q", "--rebase", "origin", "main")
comp = git("show", f"{rev}:templates/{APP}/docker-compose.yml", cwd=LIVE)
open(f"{D}/templates/{APP}/docker-compose.yml", "w").write(comp)
sys.path.insert(0, f"{LIVE}/scripts")
import ladder
fy = open(f"{LIVE}/templates/{APP}/.felhom.yml").read()
entries, _, _ = ladder.parse(fy)
head = fy.split("\nupdate_ladder:")[0].rstrip("\n") + "\n"
if ladder_entries:
keep = [e for e in entries if e in ladder_entries]
for e in keep:
head = ladder.append_entry(head, e)
open(f"{D}/templates/{APP}/.felhom.yml", "w").write(head)
subprocess.run(["rm", "-rf", f"{D}/templates/{APP}/steps"], check=True)
subprocess.run(["git", "-C", D, "add", "-A", f"templates/{APP}"], check=True)
git("commit", "-q", "--allow-empty", "-m", msg)
git("push", "-q", "origin", "main")
say("drill:", git("log", "--oneline", "-1").strip(), "| images:", re.findall(r"image:\s*(\S+)", comp))
return entries
fx = fixtures.FIXTURES[APP]
w.login()
REC["controller"] = w.guest("docker inspect -f '{{.Config.Image}}' felhom-controller").strip()
say("9202 controller:", REC["controller"])
if w.stack(APP).get("deployed"):
say(f"{APP} already installed on 9202 — removing it first (scratch box)")
w.remove(APP)
sys.path.insert(0, f"{LIVE}/scripts")
import ladder
all_entries = ladder.parse(open(f"{LIVE}/templates/{APP}/.felhom.yml").read())[0]
set_template(CFG["old"], [], f"DRILL {APP}: the OLD definition ({CFG['old']}) for R-638 slice 0")
old_imgs = re.findall(r"image:\s*(\S+)", git("show", f"{CFG['old']}:templates/{APP}/docker-compose.yml", cwd=LIVE))
w.sync_rescan(APP, old_imgs[0]) # wait until the box's catalog carries the OLD definition (run 1 installed the live one)
if not w.deploy(APP, CFG["sub"], CFG.get("extra")):
sys.exit(say("RESULT the install did not complete") or 1)
tok = fx.seed(w, CFG["sub"], say)
if tok is None or not fx.verify(w, CFG["sub"], tok, say):
sys.exit(say("RESULT C1 failed — the seed did not read back before") or 1)
REC["pinned_old"] = (w.stack(APP).get("app_config") or {}).get("pinned_images")
if old_imgs[0] not in (REC["pinned_old"] or {}).values():
sys.exit(say(f"RESULT the box installed {REC['pinned_old']}, not the OLD definition — stopping") or 1)
REC["tables_old_live"] = live_tables()
say(f"[2] old version installed, pinned={REC['pinned_old']}, live tables {len(REC['tables_old_live'])}")
new_comp = git("show", f"{CFG['new']}:templates/{APP}/docker-compose.yml", cwd=LIVE)
new_imgs = dict(re.findall(r"^ ([a-z0-9-]+):\s*\n(?:.*\n)*?\s+image:\s*(\S+)", new_comp, re.M))
step = all_entries[:1] # docmost 0.95.0 -> 0.96.0 (pg16), romm 5.0.0 -> 5.3.0 (mariadb 11.4)
say(f"[3] the ladder entry used: {[ (e['from'], e['to']) for e in step ]}")
set_template(CFG["new"], step, f"DRILL {APP}: the NEW definition ({CFG['new']}) + its one ladder entry, R-638 slice 0")
w.sync_rescan(APP, next(iter(step[0]["to"].values())) if step else None)
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
res = w.press_update(APP, poll=1, cap_s=1800)
REC["update_final_phase"] = res.get("final_phase")
REC["pinned_new"] = (w.stack(APP).get("app_config") or {}).get("pinned_images")
time.sleep(10)
REC["tables_new_live"] = live_tables()
added = sorted(set(REC["tables_new_live"]) - set(REC["tables_old_live"]))
REC["tables_added_by_migration"] = added
say(f"[3] update -> {res.get('final_phase')}, pinned={REC['pinned_new']}, live tables {len(REC['tables_new_live'])}, "
f"added by the migration: {len(added)} {added}")
REC["seed_after_update"] = fx.verify(w, CFG["sub"], tok, say)
if res.get("final_phase") != "done" or not added:
say("RESULT the update did not migrate (no done, or no new table) — slice 0 cannot measure; stopping before restore")
json.dump(REC, open(os.path.join(EV, f"{APP}-tables.json"), "w"), indent=2)
sys.exit(1)
snaps = w.snapshots(APP)
say(f"[4] restorable copies offered: {json.dumps(snaps)[:600]}")
pick = snaps[0] if snaps else None
sid = pick and (pick.get("id") or pick.get("snapshot_id") or pick.get("short_id"))
since_r = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
r = w.restore(APP, sid)
REC["restore"] = {k: r.get(k) for k in ("snapshot_id", "http", "seconds", "state_after", "hold_after")}
time.sleep(15)
lines = w.guest(f"docker logs --since {since_r} felhom-controller 2>&1 | grep -v DEBUG | grep -i -E 'restor|Imported DB dump|replay|volume' | cut -c1-300")
log.write(lines + "\n")
REC["imported_db_dump_line"] = [l for l in lines.splitlines() if "Imported DB dump" in l]
REC["tables_after_restore"] = live_tables()
REC["seed_after_restore"] = fx.verify(w, CFG["sub"], tok, say)
# control from a different channel: the table list in the copy's own dump file on the Tier-2 drive
REC["unit_files"] = w.guest(f"find /mnt/felhom-drives/scratch_hdd/backups -path '*{APP}*' -type f 2>/dev/null | head -40").split()
# the RESTORED copy's dump — never a `pre-restore-*` safety dump the restore itself wrote (run 3 of romm read one)
dumps = "\n".join(f for f in REC["unit_files"] if re.search(r"(\.sql(\.gz|\.zst)?$|dump)", f) and "pre-restore" not in f)
REC["dump_files_seen"] = dumps.split()
copy_tables = []
if dumps.split():
f0 = dumps.split()[0]
cat = "zcat" if f0.endswith(".gz") else ("zstdcat" if f0.endswith(".zst") else "cat")
# tables AND views: `SHOW TABLES` / pg_tables list what the live side lists, so the copy side must name the same kinds
# (romm has three views; run 3 counted only CREATE TABLE and read 24 of 27). pg_tables lists tables only.
kinds = "TABLE|VIEW" if APP == "romm" else "TABLE"
out = w.guest(f"{cat} '{f0}' | grep -o -E 'CREATE (ALGORITHM=[A-Z]+ )?(DEFINER=[^ ]+ )?(SQL SECURITY [A-Z]+ )?({kinds}) (IF NOT EXISTS )?[`\"]?([a-z]+\\.)?[`\"]?[A-Za-z0-9_]+' | sed -E 's/.*[ .`\"]//' | sort -u")
copy_tables = sorted(set(t for t in out.split() if re.fullmatch(r"[A-Za-z0-9_]+", t)))
REC["tables_in_copy_dump"] = copy_tables
REC["dump_used_for_control"] = dumps.split()[:1]
left = sorted(set(REC["tables_after_restore"]) & set(added))
REC["migration_tables_left_after_restore"] = left
REC["live_equals_copy"] = REC["tables_after_restore"] == copy_tables
say(f"[5] Imported DB dump lines: {REC['imported_db_dump_line']}")
say(f"[5] seed read back after restore: {REC['seed_after_restore']}")
say(f"[5] live tables after restore {len(REC['tables_after_restore'])}; the copy's own dump lists {len(copy_tables)}; "
f"equal={REC['live_equals_copy']}; migration tables still there: {left}")
REC["pinned_after_restore"] = (w.stack(APP).get("app_config") or {}).get("pinned_images")
REC["state_after_restore"] = w.stack(APP).get("state")
say(f"[5] pinned after restore {REC['pinned_after_restore']}, state {REC['state_after_restore']}")
ok = bool(REC["imported_db_dump_line"]) and REC["seed_after_restore"] and not left and REC["live_equals_copy"]
REC["verdict"] = "SAFE" if ok else "NOT SAFE / NOT SHOWN"
say(f"RESULT {APP}: {REC['verdict']}")
json.dump(REC, open(os.path.join(EV, f"{APP}-tables.json"), "w"), indent=2)
if not os.environ.get("KEEP"):
say(f"remove -> {w.remove(APP)}")
@@ -0,0 +1,93 @@
"""r717live.py — R-717 live proof on scratch 9202 (test controller 0.301.0, drill catalog with wishlist's after_setup).
1 wishlist installed fresh; the household's first account (the fixture, through the setup gate);
2 the household opens the app (POST /apps/<slug>/setup-gate/open) → after_setup's close command runs;
the DB value read (enableSignup=false) + the controller's own lines;
3 a STRANGER straight at the app (the container's address — past the address block, the strictest case):
sign-up refused; the user count before/after;
4 the household's 15-minute window (POST /apps/<slug>/signup-window) → the open command; DB = true; a family member
signs up straight at the app → success; the user count +1;
5 after the window ends (polled; ~15 min): the close command ran again; DB = false; a stranger refused; count unchanged.
Evidence: ../E/live.txt."""
import json, os, secrets, sys, time
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP, SUB = "wishlist", "wishlist"
HERE = os.path.dirname(os.path.abspath(__file__))
log = open(os.path.join(HERE, "..", "E", "live.txt"), "a", buffering=1)
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
DBJS = r'''const {DatabaseSync}=require('node:sqlite');const db=new DatabaseSync('/usr/src/app/data/prod.db',{readOnly:true});
const v=db.prepare("select value from system_config where key='enableSignup' and groupId='global'").get();
const n=db.prepare("select count(*) as n from user").get();console.log('DB enableSignup='+(v?v.value:'<no row: default open>')+' users='+n.n);'''
def db():
return w.guest("docker exec -i wishlist node - <<'JS'\n" + DBJS + "\nJS\n").strip().splitlines()[-1:]
def stranger(tag):
"""A sign-up straight at the app's own address inside the box — past the box's address block."""
u = f"{tag}{secrets.token_hex(3)}"
body = f"name={tag}&username={u}&email={u}%40example.invalid&password=Drill-{secrets.token_hex(8)}&tokenId="
out = w.guest(f"""ip=$(docker inspect -f '{{{{range .NetworkSettings.Networks}}}}{{{{.IPAddress}}}} {{{{end}}}}' wishlist | awk '{{print $1}}')
curl -s -m 30 -H 'Host: {SUB}.{w.DOMAIN}' -H 'Origin: https://{SUB}.{w.DOMAIN}' -H 'x-sveltekit-action: true' \\
-H 'Content-Type: application/x-www-form-urlencoded' -w '\\nHTTP %{{http_code}}' --data '{body}' http://$ip:3000/signup""")
return " ".join(out.split())[-220:]
def ctl_lines(since):
return w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -v DEBUG | grep -i -E 'after_setup|native|signup|sign-up|window' | cut -c1-300")
w.login()
if w.stack(APP).get("deployed"):
say("wishlist installed from the earlier probe — removing it first (scratch box)")
w.remove(APP)
w.sync_rescan()
time.sleep(5)
w.sync_rescan()
say("9202 controller:", w.guest("docker inspect -f '{{.Config.Image}}' felhom-controller").strip())
if not w.deploy(APP, SUB):
sys.exit(say("RESULT install failed") or 1)
fx = fixtures.FIXTURES[APP]
tok = fx.seed(w, SUB, say)
say("[1] the household's first account:", tok is not None, "| DB:", db())
t0 = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
code, d = w.ctl("POST", f"/apps/{APP}/setup-gate/open", {})
say("[2] the household opens the app (setup done) ->", code, str(d)[:160])
time.sleep(25)
say("[2] DB after the close command:", db())
log.write(ctl_lines(t0) + "\n")
say("[3] stranger, straight at the app:", stranger("stranger"))
say("[3] DB:", db())
t1 = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
code, d = w.ctl("POST", f"/apps/{APP}/signup-window", {})
say("[4] the household opens the 15-minute window ->", code, str(d)[:160])
time.sleep(25)
say("[4] DB inside the window:", db())
say("[4] a family member, straight at the app:", stranger("family"))
say("[4] DB:", db())
log.write(ctl_lines(t1) + "\n")
t2 = time.time()
while time.time() - t2 < 1500:
time.sleep(30)
v = db()
if v and "enableSignup=false" in v[0]:
say(f"[5] the switch reads CLOSED again {round(time.time() - t2)} s after the check began: {v}")
break
else:
say("[5] the switch did NOT close within 25 minutes:", db())
say("[5] stranger after the window, straight at the app:", stranger("late"))
say("[5] DB:", db())
log.write(ctl_lines(t1) + "\n")
st = w.stack(APP)
say("[5] app page record:", json.dumps({k: (st.get("app_config") or {}).get(k) for k in ("after_setup", "setup_gate")})[:600])
if not os.environ.get("KEEP"):
say("remove ->", w.remove(APP))
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Point 9202 at the drill catalog, or put the saved controller.yaml back. `09` §6.5."""
import io, os, re, sys
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
VOL = "/var/lib/docker/volumes/felhom-controller-data/_data"
SAVE = f"{VOL}/controller.yaml.pre-designbuild"
DRILL = "https://gitea.dooplex.hu/admin/app-catalog-drill.git"
def creds():
for l in io.open(os.path.expanduser("~/.git-credentials")).read().split("\n"):
m = re.match(r"https://(admin):([^@]+)@gitea\.dooplex\.hu", l)
if m: return m.group(1), m.group(2)
sys.exit("no admin credential")
if sys.argv[1] == "drill":
u, t = creds()
out = w.guest(f"""set -e
test -f {SAVE} || cp -p {VOL}/controller.yaml {SAVE}
python3 - <<'PY'
import re
p = "{VOL}/controller.yaml"; s = open(p).read()
s = re.sub(r'(^\\s+repo_url: ).*$', r'\\g<1>{DRILL}', s, count=1, flags=re.M)
s = re.sub(r'(^git:(?:\\n\\s+.*)*?\\n\\s+token: ).*$', r'\\g<1>"{t}"', s, count=1, flags=re.M)
s = re.sub(r'(^git:(?:\\n\\s+.*)*?\\n\\s+username: ).*$', r'\\g<1>"{u}"', s, count=1, flags=re.M)
open(p, "w").write(s)
PY
rm -rf {VOL}/catalog-cache {VOL}/data/catalog-cache
docker restart felhom-controller >/dev/null
grep -n 'repo_url' {VOL}/controller.yaml
""")
print(out.replace(t, "<token>"))
elif sys.argv[1] == "restore":
print(w.guest(f"""set -e
cp -p {SAVE} {VOL}/controller.yaml
rm -rf {VOL}/catalog-cache {VOL}/data/catalog-cache
docker restart felhom-controller >/dev/null
grep -n 'repo_url' {VOL}/controller.yaml; grep -c 'token: ""' {VOL}/controller.yaml || true
cmp {SAVE} {VOL}/controller.yaml && echo RESTORED-IDENTICAL"""))
@@ -0,0 +1,140 @@
"""wgerstep.py <new-definition-dir> — R-762 on scratch 9202 (drill catalog), ONE process, through the product.
1 DRILL: wger un-hidden (DRILL only); install fresh at the live definition; seed (the fixture) + read back (C1);
2 a progress photo posted to the gallery (the household's API with its session); its /media URL read: 404 expected
(the R-762 fault — the control that the later 200 is the fix, not the test);
3 the login page's own CSS links read: 404 expected;
4 a DRILL commit replaces the compose with the new definition + one ladder entry; sync; the product's guarded Update;
5 after: the seed, the same photo URL (200 + the same byte count), the CSS links (200), wger-files' memory;
6 box verdict JSON; removed through the product (KEEP=1 keeps it).
Evidence: ../F/box/step.txt + box-verdict-wger.json."""
import json, os, re, struct, subprocess, sys, tempfile, time, zlib
sys.path.insert(0, "/mnt/5_hdd/felhom.eu/git/app-catalog-felhom.eu/scripts")
import box_walk as w
import upgrade_fixtures_box as fixtures
APP, SUB = "wger", "fitness"
NEWDEF = sys.argv[1]
HERE = os.path.dirname(os.path.abspath(__file__))
EVD = os.path.join(HERE, "..", "F", "box"); os.makedirs(EVD, exist_ok=True)
log = open(f"{EVD}/step.txt", "a", buffering=1)
D = "/mnt/5_hdd/felhom.eu/drill/app-catalog-drill"
REC = {"app": APP}
def say(*a):
w.say(*a); log.write(" ".join(map(str, a)) + "\n")
def git(*a):
return subprocess.run(["git", "-C", D, *a], check=True, capture_output=True, text=True).stdout
def png(path, n=64):
"""A real PNG (n x n, random-ish colour) — the gallery validates the image."""
import secrets
rgb = secrets.token_bytes(3)
raw = b"".join(b"\x00" + rgb * n for _ in range(n))
def chunk(t, d):
return struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d) & 0xffffffff)
data = (b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", n, n, 8, 2, 0, 0, 0))
+ chunk(b"IDAT", zlib.compress(raw)) + chunk(b"IEND", b""))
open(path, "wb").write(data)
return len(data)
def css_links():
rc, code, page = w.app_curl(SUB, "/en/user/login")
links = re.findall(r'(/static/[^"]+\.css)', page or "")[:3]
return {l: w.app_curl(SUB, l)[1] for l in links}
fx = fixtures.FIXTURES[APP]
w.login()
if w.stack(APP).get("deployed"):
say("wger already installed — removing it first (scratch box)")
w.remove(APP)
git("pull", "-q", "--rebase", "origin", "main")
fy = f"{D}/templates/{APP}/.felhom.yml"
s = open(fy).read()
if "lifecycle: hidden" in s:
open(fy, "w").write(s.replace("lifecycle: hidden", "lifecycle: available", 1))
git("commit", "-q", "-am", "DRILL wger: un-hidden for the R-762 box step (DRILL only)")
git("push", "-q", "origin", "main")
say("drill:", git("log", "--oneline", "-1").strip())
w.sync_rescan()
time.sleep(5)
w.sync_rescan()
if not w.deploy(APP, SUB):
sys.exit(say("RESULT the install did not complete") or 1)
before = (w.stack(APP).get("app_config") or {}).get("pinned_images")
say(f"[1] installed, pinned={before}")
tok = fx.seed(w, SUB, say)
if tok is None or not fx.verify(w, SUB, tok, say):
sys.exit(say(f"RESULT C1 failed: {getattr(fx, 'tried', '')}") or 1)
jar = tempfile.mktemp(prefix="wger-jar-")
hdr, why = fx._login(w, SUB, tok["pw"], jar)
pic = tempfile.mktemp(prefix="wger-photo-", suffix=".png")
size = png(pic)
rc, code, out = w.app_curl(SUB, "/api/v2/gallery/", *hdr, "-F", f"image=@{pic};type=image/png", "-F", "date=2026-10-06",
"-F", "description=r762", method="POST")
say(f"[2] POST /api/v2/gallery/ (a {size} B PNG) -> {code}")
img = None
try:
img = json.loads(out).get("image")
except Exception:
say(f" body {out[:200]}")
path = re.sub(r"^https?://[^/]+", "", img or "")
REC["photo_path"], REC["photo_bytes"] = path, size
code_b = w.app_curl(SUB, path)[1] if path else None
REC["photo_before"] = code_b
REC["css_before"] = css_links()
say(f"[2] the photo {path} read BEFORE the step -> {code_b}; CSS before {REC['css_before']}")
git("pull", "-q", "--rebase", "origin", "main")
newc = open(os.path.join(NEWDEF, "docker-compose.yml")).read()
open(f"{D}/templates/{APP}/docker-compose.yml", "w").write(newc)
to = dict(re.findall(r"^ ([a-z0-9-]+):\s*\n(?:(?! [a-z0-9-]+:\s*\n).*\n)*?\s+image:\s*(\S+)", newc, re.M))
s = open(fy).read()
entry = {"from": before, "to": to, "verdict": "proven", "tested_at": "DRILL", "harness_version": 5,
"evidence": "DRILL (box proof in progress)", "marks": {"files_may_change": False, "needs_person": None, "memory_tight": False}}
s = s.rstrip("\n") + "\n - " + json.dumps(entry) + "\n"
s = re.sub(r'^ mem_limit: .*$', ' mem_limit: "416M"', s, count=1, flags=re.M)
open(fy, "w").write(s)
git("commit", "-q", "-am", f"DRILL wger: the R-762 definition (wger-files) + its ladder entry (box proof)")
git("push", "-q", "origin", "main")
say("drill:", git("log", "--oneline", "-1").strip(), "| to:", to)
w.sync_rescan(APP, to.get("wger-files"))
REC["badge_before"] = w.badges(APP)
since = w.guest("date -u +%Y-%m-%dT%H:%M:%SZ").strip()
res = w.press_update(APP, poll=1, cap_s=1800)
for p in res.get("phases", []):
log.write(f" phase +{p['t']}s {p['phase']} | err={p['error']}\n")
time.sleep(15)
read = fx.verify(w, SUB, tok, say)
code_a = w.app_curl(SUB, path)[1] if path else None
rc, _, body = w.app_curl(SUB, path, "-o", "/dev/null", "-w", "%{size_download}") if path else (1, None, "")
REC["photo_after"], REC["photo_after_bytes"] = code_a, body.strip()[-12:]
REC["css_after"] = css_links()
REC["memory"] = w.guest("docker stats --no-stream --format '{{.Name}} {{.MemUsage}}' | grep -i wger").strip().splitlines()
lines = w.guest(f"docker logs --since {since} felhom-controller 2>&1 | grep -E 'update {APP}' | grep -v DEBUG | cut -c1-400")
log.write(lines + "\n")
st = w.stack(APP); after = (st.get("app_config") or {}).get("pinned_images")
REC["badge_after"] = w.badges(APP)
say(f"[5] after: phase={res.get('final_phase')} pinned={after} seed={read} photo={code_a} ({REC['photo_after_bytes']}) "
f"css={REC['css_after']} mem={REC['memory']}")
ok = (res.get("final_phase") == "done" and read and after == to and code_a == "200"
and REC["css_after"] and all(c == "200" for c in REC["css_after"].values()))
verdict = {"app": APP, "venue": "box 9202 (drill catalog), the product's guarded Update (R-762 definition step)",
"from": before, "to": after, "verdict": "proven" if ok else "failed",
"seed_read_before": True, "seed_read_after": read, "healthy_after": st.get("state") == "running",
"duration_s": res.get("duration_s"), "final_phase": res.get("final_phase"), "measured_at": since,
"evidence": "felhom.eu/documentation/audits/design-build-2026-10-06/F/box/step.txt", "r762": REC}
json.dump(verdict, open(f"{EVD}/box-verdict-{APP}.json", "w"), indent=2)
say(f"RESULT {verdict['verdict']}")
for f in (jar, pic):
if os.path.exists(f):
os.unlink(f)
if not os.environ.get("KEEP"):
say(f"remove -> {w.remove(APP)}")