docker-v: an unallowlisted `-v /etc:/x` in a NEW .go file two directories down
under internal/ and under cmd/ convicts; controls: the clean tree and the same
line in a _test.go pass. The three shared felhom.eu scripts run against a
scratch clone of THIS repo in a scratch workspace (siblings symlinked), the
felhom-agent b78a0ff pattern: a missing cited .go/.md path, a version literal
in CLAUDE.md effective text and R-419's prose-only Observations note convict;
the real files, the version inside an HTML comment and both genuine markers
pass. DECOY_SHARED_DIR judges a mutated copy for the red-proof. All four in
COVERS, so their EXEMPT entries in decoy_coverage_gate.py can go.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
launcher, layout remove-dialog, deploy, debug, import, remote-backup, shared
backups, system settings and the password-changed flash. Formal-form ceiling
13 -> 0 (the ratchet now refuses any new one the gate's stems see). 119 parity
fixtures changed by exactly those bytes. flash.login.password_changed listed
as REWORDED in the go-parity map; pinned by TestR516_FormalFormsAreGone.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
- item 7: the storage page shows the disconnect time in local time (fmtTimeStr), not the raw RFC3339 UTC.
- item 8: a disconnected drive no longer has two banners - the health check's warning for it is dropped
when the dedicated alert.storage.disconnected banner was built (it stays on the wire).
- item 9: alert.deadapp.group says "nezd meg" (te-form); formal ceiling 14 -> 13.
- item 10: the disk/memory/CPU/temperature health banners show the dashboard's own sentence
(health.* keys, hu + en) via HealthReport.WarningMsgs/IssueMsgs; the wire text is unchanged.
checkResources split out of RunHealthCheck as the test seam.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
A held app's page now says, inside the hold panel, that the app's own log from
before it was stopped was kept, with a Napló link to the logs page that serves
it (0b93e1a). Shown only for an update hold with a non-empty kept log
(Manager.HoldLogKept). New copy through the bundle (hu + en), parity case
app_info_hold_log captured when born.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
backup_integrity_ok / backup_integrity_failed now take facts and push bundle
keys (Hungarian bytes unchanged - go-parity, pinned verbatim by
TestR585_IntegrityHungarianIsUnchanged). The interrupted-operation alert
(backup_failed, customer-enabled by default) used to send the operator's
ENGLISH sentence to every household; NotifyInterruptedOperation composes it
per language, the English byte-identical to the operator's log line. The
now-callerless NotifyBackupFailed is removed. local_api_endpoint_drift is
operator-only (no customer toggle) and already English by design - not
changed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Where the app has its own switch (after_setup.env), both presses restart
it; the cards now say so (app_info.restarts_once, hu+en). Two parity
cases born with the line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Item 1: the always-visible 'Debug' menu item reads 'Hibakeresés' in
Hungarian (key layout.hibakereses; English stays 'Debug'). Item 4: four
formal forms on the storage pages (Adjon hozzá, Csatlakoztasson,
Válassza ki x2, Önnek) are now te-form. The parity fixtures change by
exactly these bytes (a deliberate copy change, not a regeneration);
HU_FORMAL_CEILING 17 -> 14. Item 2 was already fixed (slice 1).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
db_dump_failed, the off-box backup_failed and offbox_enlarge_blocked
(whose sentence IS the household's mail - no hub customerMessages entry)
now take facts and push a bundle key, so an English household reads
English. Hungarian bytes unchanged (go-parity). Added to
convertedProducers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
memoryVerdict returns the warning's bundle KEY (KeyMemoryOvercommitWarning)
and DeployStack hands it on; the API renders it per request
(deployWarningData). msgHU, whose only use this was, is gone.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Since v0.292.0 (R-841) cloudflared's health check asks /ready, so a lost
connection reads unhealthy; the cloudflared row now also carries the tag
"Kivulrol nem erheto el" with a plain title, in hu+en. New parity case
dashboard_tunnel_offline (born as a key).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
InjectMissingFields skips a new generated secret that is in after_install's
env list and named by no compose definition: after_install does not run
again, so the app never received the value and the reveal would answer
a login the app does not have (calibre-web ADMIN_USER, 2026-10-01).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
RemoveStack journals itself before compose down and clears on every
return; at start a found journal finishes the remove through the same
RemoveStack (once, before the boot reconciler), keeping drive data and
backups even if the household had asked to delete them (no unattended
deletion at boot; logged).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
AppInfo.WithAddress fills the catalog placeholder (<label>.DOMAIN -> the
effective subdomain + customer domain, bare DOMAIN -> the domain) on a
copy; appDetailHandler applies it. Pinned per catalog template (both
languages) and on the rendered page.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Replaces 365eff6 for main, which now carries R-615 (0b349e2). The plain
URL is cloned; the Basic credentials ride per git command as
http.<origin>.extraHeader via GIT_CONFIG_COUNT. R-615's origin compare
now compares credential-free forms against the configured URL, so a set
token never re-clones; a stored origin with user:token@ is rewritten
without it. Pinned by TestR616_* incl. TestR616_TokenSetSameRepoNoRecloneOriginClean.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
A rotation that reached bootstrap.json but not controller.yaml left the
agent channel at 401 across restarts while the endpoint-only detector
stayed silent. The tokens are now compared (constant time, boolean only);
the token case gets its own banner key and operator message. Still
detection only - nothing is reconciled (R-78).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
An export to a registered network drive (Kind=network) with no bundle
password now answers 400 with a household sentence (hu+en); with a
password it runs; a local drive is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
STEMS now comes from felhom.eu/scripts/customer_copy_vocab.py (RETRIEVAL_STEMS), read the
way controller_gates.py reads the other shared checkers (CI clones felhom.eu beside this repo);
an absent or empty vocabulary is INCONCLUSIVE (exit 2), never a pass. Decoys: vocabulary
absent -> rc 2; an invented stem in a doctored shared list convicts a template using it.
hub_copy_gate.check_drift on this file now reads 'ok — single source'.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
waitForHealthy's 3 s settle / 5 s poll / 90 s timeout become package vars (production values
unchanged, pinned by TestR488_HealthWaitDefaultsAreProduction); TestMain shortens them once.
Measured before: 51 of 599 tests held 442 of 444 s, two not-running fixtures 279 s alone.
newOffboxManager now installs a failing SSH fake: TestOffbox_ConfirmedReset was spawning a
real 'ssh felhom@nas.local' and waiting out its 10 s ConnectTimeout.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
39 Hungarian literals: 38 are debug-page copy (every writeDebugJSON message/error, rendered
into the result line by debug.html, plus the pre-0.83 agent notice rendered in the log
viewer) -> 30 debug.api.* keys (hu byte-equal, parity gate green; en added). One is PAYLOAD:
the 'Teszt esemény' text sent to the hub as the test event's message stays a literal. The
diagnostic dump carries no Hungarian. TestR574_DebugPageCopyFollowsTheLanguage pins hu bytes
and en rendering.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Checks 4 (ARITY) and 5 (NO-CONCAT) in i18n_go_parity.py: at every message-helper call
(MsgError/MsgErrorf/Text/Msgf/msg/msgLang/msgHU/note) whose key is a literal hu.json knows,
the argument count must equal the Hungarian value's printf verbs; a known key literal may
never be an operand of +. 485 calls judged, 0 defects today. Decoys: a dropped argument and
a key+suffix concatenation on a real producer convict; a nested-paren argument is accepted.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Readers (report builder, health check, /api/system, web primaryHDDPath, metrics collector,
AutoDiscoverStoragePaths' fallback parameter) now use the storage registry only. An old
controller.yaml still carrying paths.hdd_path keeps loading (non-strict YAML), pinned by
TestR492_OldConfigWithHDDPathStillLoads.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
stop/start/restart/update, remove and delete matched "protected", "not found", "not deployed",
"still running", "not orphaned" in err.Error(). New sentinels in internal/stacks
(stack_errors.go) carried by the producers in manager.go/delete.go via util.KindErrorf (message
bytes unchanged); api.stackOpStatusFor maps them. Tests: reworded-message table per family,
wiring check over all three handlers, producers keep kind + words.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
TestR578NothingTakesTheSettingsLockInsideASettingsCallback (internal/settings) derives from
source the *Settings methods that take s.mu, the callback runners among them, and per package
every helper that transitively reaches one; a call to any of those inside a func literal
passed to a runner (or a non-literal callback) fails with file:line. Decoy:
TestR578GateConvictsAHelperChain. REUSE.md lookalike row added.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
agentapi FormatResult / FormatStatusResult gain fs_uuid (agent R-25 half). runStorageInit mounts
it — from the synchronous answer or the polled status — instead of re-resolving the UUID from the
/dev path, so a node that moved between format and mount cannot redirect the mount. An agent that
sends no field keeps the old resolve, logged as a WARN each time (the window is open then).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
R-615: a changed git.repo_url was inert (the clone's stored origin was fetched for ever). The sync
now compares the configured URL with the clone's origin: another repository -> drop the cache and
clone it; the same repository with new credentials -> set-url.
R-607: a catalog move that changed no stack directory (a deployed, pinned app keeps its stored
definition) skipped the rescan, so CatalogImages and the update badge stayed stale, and the sync
said 'nincs változás'. The sync now notes the cache commit before and after, rescans when it moved,
says so, and returns catalog_moved.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
One unplugged drive sent storage_disconnected plus an app_start_failed per app on it (five operator
mails). The dead-app job now hands the notifier the apps of a disconnected drive (its recorded
StoppedStacks plus apps whose HDD_PATH is that drive) as not down; the storage event already names
them. The dashboard's dead list is unchanged. The hub-side cooldown (F6/F7) and a household mail
for a lost drive are not in this change.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
The agent_channel_unauthorized alert's remedy is a re-bootstrap (a restart), and an unseeded->up
first observation was silent, so following the instruction guaranteed no recovery event. A down
alert now leaves a marker in the data dir; the first UP after a restart sends the recovery and
clears it. A restart with no alert outstanding stays silent.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
Diagnosed: 9202's paperless-ngx app.yaml named <drive>/userdata/paperless-ngx, written by the
2026-09-22 drill harness through POST /api/stacks/<n>/deploy, which accepted it (RefuseAsAppNamespace
matches by prefix). The boot sweep then asked a folder whether it was a mountpoint and HELD the app.
The deploy page offers drive roots only, so a household could not reach this; every other caller
is now refused with a sentence naming the drive (settings.DriveOfSubPath).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
GetLogs (logs page, /api/stacks/<n>/logs, the operator's remote diagnostics) returned "" for a held
app — no containers — while captureHoldLogs had kept the app's own log under
hold-logs/<ts>/compose-logs.txt. It now serves the newest non-empty kept log, under a header naming
the file, when the live log is empty; a running app's own output always wins. GetLogs gains a test
seam (logsComposeFn) so the test never reaches docker.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
POST /backup/offbox/clear (reveal-then-confirm) forgets the target and deletes ssh_key + known_hosts;
nothing on the target is touched. repo_password is kept whenever anything could depend on it (hub
sealed package — the R-241 rule; escrowed; a successful run; snapshots) and deleted only otherwise.
Refused for the Felhom tier (rclone-pinned), while the single-flight is held, and while an
abandonment countdown runs. i18n parity fixtures of backups_remote gain the additive block only.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
internal/sockheal: 60 s of refusals (never a timeout, only after Docker answered once) → exit 75 so
Docker's restart policy brings the controller back on the current socket; every 5 min it restarts any
other socket user (traefik) holding an older inode. Measured on 9202: only a docker.socket restart
re-creates the file; dockerd crash / docker.service restart keep it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS