Repository navigation
fix(remote): never overwrite remote-hosts.json while it cannot be read - #1575
Merged
vastsa merged 3 commits intoOct 12, 2026
Merged
Conversation
A transient read error (antivirus/backup holding the file) or a file written by a newer app version previously degraded to an empty list, and the next pairing atomically wrote that list back — silently wiping every paired host, device tokens included (vastsa#1572). Split the read into a discriminated state. Reads stay permissive (the UI shows an empty list as before, nothing on disk changes); upsert and remove now write only from a verifiably readable file: an unreadable or future-version file makes the write throw REMOTE_REGISTRY_UNWRITABLE / REMOTE_REGISTRY_VERSION_UNSUPPORTED, and a corrupt file is quarantined under remote-hosts.json.corrupt-<timestamp> with its bytes preserved before pairing continues.
Concurrent pairings can read the same corrupt registry, race quarantine, and overwrite records. Serialize each read-modify-write so every mutation observes the preceding completed state.
…stry-serialization
Owner
|
Thanks for the fix. The data-loss scenario was real, and your read/write guard addressed the root cause. I added registry-wide mutation serialization and a concurrent-pairing regression test so separate host pairings cannot race through quarantine and writeback. Merged as d5fa57d. |
This branch was previously deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1572
Summary
readFileContentsbecomes a discriminated read state:ok/absent/unreadable(IO error) /corrupt(JSON parse failure) /unsupportedVersion(shape or version mismatch).list) stay permissive exactly as before — the UI shows an empty list while the file is unavailable, and nothing on disk changes.upsert,remove) may only proceed from a verifiably readable file: an unreadable file makes the write throwREMOTE_REGISTRY_UNWRITABLE, a future-version file throwsREMOTE_REGISTRY_VERSION_UNSUPPORTED, and a corrupt file is quarantined toremote-hosts.json.corrupt-<timestamp>(bytes preserved) before pairing continues.Rationale
#1572: a transient read error (AV/backup holding the file on Windows) or a version-skewed file degraded to an empty list, and the next pairing atomically wrote that list back — silently wiping every paired host and its encrypted device tokens. The in-code comment acknowledged "any upsert would overwrite it"; this change makes the overwrite impossible instead of acknowledged.
Quarantine (rather than refuse) for the corrupt case keeps pairing available on a genuinely broken file while the old records stay recoverable; refusing on unreadable/version-skew is strict because those files are intact and worth protecting as-is.
Affected specs / ADRs / E2E scenarios
pairing-boot-add-removeE2E-style suite: re-run green (5/5); no new scenario added since the failure mode is a filesystem-level fault injection covered by the new unit tests.Validation (on latest
origin/maineec988c)apps/desktop/test/remote-host-registry.test.mjs: 11/11 — 8 existing (one, "shape-mismatched … preserved on disk until rewritten", still passes unchanged sincelistbehavior is untouched) plus 3 new: unreadable file (directory at the registry path) rejectsupsert/removeand survives untouched; future-version file is listed empty but never overwritten; corrupt file is quarantined with its bytes intact and pairing proceeds.tsc --noEmitinapps/desktop: pass.settings-remote-hosts(3),boot-remote-hosts(8),pairing-boot-add-remove(5): green.remote-host-ssh-password: 5 failures — pre-existing Windows-environment failures inssh-askpass/ssh-transport(POSIX helper + fixture spawn), a file this change never touches (verified: it imports onlyssh-askpass.ts).Compatibility / remaining risk
remote-hosts.json.corrupt-<timestamp>files accumulate only when corruption actually happens (rare), each preserving the pre-corruption bytes.