v0.254.0 — the saved notes follow the language, and the switch becomes a globe (R-557 slice 2 release C; SLICE 2 CLOSED)
gates / gates (push) Successful in 23s

The notes a background run SAVES — last night's backup line, the last error, the proof
result, the restore outcome — are written in the BOX's language at the moment they are
written. A household that switches sees the previous run's note in the old language until
the next run rewrites it: the operator's §16 option 1, stated rather than hidden.
EndRestoreOp no longer receives a Hungarian literal from anywhere.

The language switch is a globe. Two text links wrapped in the sidebar footer and asked the
reader to recognise "Magyar"/"English" as links; a globe is the one symbol every web user
already reads as "language", so nobody has to read Hungarian to escape Hungarian. It is
<details>/<summary> — a menu with no script, drawn inline because the icon sprite lives
only in layout.html and the visitor pages have their own shell.

Those visitor pages get the same globe, and a visitor's choice stays theirs: a display-only
felhom_lang cookie that langFor reads ONLY when there is no session. A signed-in household
can never inherit a language a previous visitor picked in the same browser. POST /lang is
CSRF-exempt for a narrow reason written at the exemption — its only achievable effect is the
language of the page the victim's own browser shows them — and safeBackPath refuses
//evil.example as well as https://, because "starts with /" alone is not the test. §16 taken:
a successful claim carries the cookie into the household's setting.

TWO PARITY EXCEPTIONS, MEASURED: 106 fixtures compared with a real diff — exactly two change
shapes (the dashboard footer, the globe in the shells) and 5 byte-identical, which are the
three pages that must not change.

I INTRODUCED A DEADLOCK AND THE SUITE CAUGHT IT BY HANGING. UpdateOffboxStatus holds the
settings write lock while running its callback; boxLang() wants the read lock; sync.RWMutex
is not reentrant. On a real box an off-site run would have hung forever HOLDING the settings
lock. Fixed by resolving the language before the callback, and guarded by a test that names
the file and line in a second instead of hanging for 25 minutes.

MinAgent: 0.131.0 (unchanged). No hub release needed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-18 14:19:31 +02:00
parent eb6aa58aa7
commit 48f3336956
142 changed files with 2199 additions and 318 deletions
+72 -1
View File
@@ -2196,5 +2196,76 @@
"err.integrations.onlyoffice_no_subdomain": "the OnlyOffice subdomain is not known",
"err.integrations.occ_failed": "the occ command failed (%s): %s (output: %s)",
"err.integrations.occ_disable_failed": "occ app:disable failed: %s (output: %s)",
"err.notify.hub_error": "hub error (%d): %s"
"err.notify.hub_error": "hub error (%d): %s",
"note.offsite.fail_quota": "The remote backup did not fit inside its storage limit",
"note.offsite.fail_orphaned": "The remote store was made with an earlier key that is no longer available",
"note.offsite.fail_no_repo": "There is no backup store at the remote target",
"note.offsite.fail_no_units": "There was nothing to back up: none of the selected apps has a backup",
"note.offsite.fail_transport": "The remote store cannot be reached (network or sign-in)",
"note.offsite.fail_unknown": "The remote backup failed for an unknown reason",
"note.offsite.fail_head": "The remote backup failed",
"note.offsite.whole_unit_gap": "(the whole app — it has no local backup unit)",
"note.offsite.no_local_unit": "These apps did NOT go into the remote backup, because they have no local backup unit yet: %s. The next backup usually makes one — if they are still listed after the second run, tell your operator.",
"note.offsite.not_installed": "These apps are selected for remote backup but are not installed, so they cannot be backed up: %s. If you no longer need them, clear their selection on the Remote backup page.",
"note.offsite.quota_partial": "Warning: because of the storage limit, %d apps got only a settings and database backup: %s.",
"note.offsite.quota_usage": "The remote backup uses %d%% of its limit (%d/%d GB).",
"note.tier2.reason_manual": "your own choice",
"note.tier2.reason_other_drive": "another data drive",
"note.tier2.label_internal_ssd": "internal SSD (system)",
"note.tier2.reason_no_second": "no second data drive — only the database and settings fit on the internal SSD; large files need a second drive",
"note.tier2.same_disk": "the target you picked is on the same physical disk",
"note.tier2.ssd_partial": "Only the settings, the database and the required data fit on the internal SSD — the optional content was not copied.",
"note.tier2.no_space_ssd": "not enough space on the internal SSD — backing large files off the drive needs a second drive (or remote storage)",
"note.tier2.no_other_drive": "there is no second physical drive — the second backup needs one",
"note.tier2.unit_preserved": "The data package on the main drive was incomplete, so the complete one already in the second copy was kept. The copy's data package is therefore older than this backup.",
"note.restore.interrupted": "The restore was interrupted (the box restarted) — start it again.",
"note.undo.present": "the earlier state is backed up: %s",
"note.undo.partial": "the earlier state is only PARTLY backed up — present: %s; MISSING: %s",
"note.undo.absent": "the backup of the earlier state is NOT where it should be (%s)",
"note.undo.none": "no restorable copy of the earlier state was made",
"note.reconstitute.copy_latest": "latest",
"note.reconstitute.held": "restoring the data of %s stopped at %s, and the earlier state could not be put back either. The app stays stopped, for safety, so your data cannot be damaged further. Get in touch with us",
"layout.nyelv": "Language",
"note.restore.failed": "The restore failed: %s",
"note.restore.full_failed": "The full restore failed: %s",
"note.restore.recover_failed": "The recovery failed: %s",
"note.restore.shares_failed": "Restoring the shares failed: %s",
"note.restore.shares_recover_failed": "Recovering the shares failed: %s",
"note.restore.unit_failed": "Restore failed: %s",
"note.restore.file_failed": "File restore failed: %s",
"note.restore.full_unit_failed": "Full restore failed: %s",
"note.restore.recovered_files": "The missing files of %s are back among your live data.",
"note.restore.shares_prepared": "The shares are restored — you can now put them back among your live data.",
"note.restore.at_scratch": " into the check folder",
"note.restore.at_scratch_named": " into the check folder: %s",
"note.restore.scratch_full": "The full backup of %s is restored%s — together with your own files. Your existing data is unchanged.",
"note.restore.scratch_state": "The settings and the database of %s are restored%s. Your own files (documents, pictures, uploads) did NOT come back — this check restore brings back only the app's settings and database. If you need your files, start “Prepare a full restore” on this page. Your existing data is unchanged.",
"note.restore.files_count": "%d files",
"note.restore.and_volumes": " and %d data volumes",
"note.restore.and_database": " and the database",
"note.restore.volumes_count": "%d data volumes",
"note.restore.the_database": "the database",
"note.restore.dumps_at": " (backup: %s)",
"note.restore.reconstituted": "%s: %s restored%s — the app has restarted.",
"note.restore.no_db_in_backup": " WARNING: this app HAS a database, but the backup held no database dump, so the database did NOT come back. The state from before the restore is backed up: %s",
"note.restore.no_db_at_all": " This app has no database.",
"note.restore.all_files_present": "Every file that was checked is in place.",
"note.restore.files_from_second": "%s: %d files restored from the second copy.",
"note.restore.scratch_incomplete": "The restore copy is incomplete — the last download did not finish. Start “Prepare a full restore” again.",
"note.unit_restore_data": "%s: %s restored — the app has restarted.",
"note.unit_restore_settings_only": "%s: the settings are back — the app has restarted. WARNING: this backup held only the settings, no data. The app's data did NOT come back from this backup.",
"note.unit_restore_counts_unknown": "The restore of %s ran — the app has restarted. This backup has no backup unit, so we cannot tell you what came back from it. Check inside the app that your data is there.",
"note.unit_restore_none_returned": "%s: WARNING — the backup lists %d data volumes and %d database dumps, but none of them came back. Your data is unchanged. Ask for help before you try again.",
"note.tier2_no_coverage": "This app's data cannot be restored from this copy — the app was not stopped. Use the Start restore button on the Backup → Restore page.",
"note.tier2_unit_available": "This app's data is not in files but in the app's own database and volumes — the app was not stopped. You can bring those back from the same copy with the “Full restore from the copy” button beside this one. Careful: that action OVERWRITES the current data, while this button only fills in the missing files.",
"note.tier2_unit_not_covered": "The app's database and internal volumes are not part of this restore.",
"note.tier2_unit_restore_source": "The restore came from the copy on the second drive (%s).",
"note.tier2_unit_restore_source_unproven": "The restore came from the copy on the second drive (%s — that is the time of the last backup ATTEMPT; we cannot prove it succeeded).",
"note.tier2_unit_action_label": "Full restore from the copy",
"note.tier2_unit_confirm_base": "This action OVERWRITES the app's current data – its database and its internal volumes too – with the copy on the second drive. Anything made since the copy is lost.",
"note.tier2_unit_confirm_date_fmt": " The copy is from: %s.",
"note.tier2_unit_confirm_date_unproven_fmt": " The copy is from: %s – that is the time of the last backup attempt; we cannot prove it succeeded.",
"note.tier2_unit_confirm_contrast": " The “Restore files” button beside it only fills in the missing files and overwrites nothing. The app stops while either one runs.",
"note.tier2_unit_stale_clause": " WARNING: this copy's data package is older than the latest backup — the package on the main drive was incomplete, so the complete one was kept. The restore uses the package named above.",
"note.tier2_unit_stale_notice_fmt": "The copy's data package is older than the latest backup (%s): the package on the main drive was incomplete, so the complete one already there was kept."
}