Skip to content

fix(desktop): keep host seeds in the clear, and keep the ones we cannot read - #185

Merged
kamrul1157024 merged 1 commit into
mainfrom
fix/desktop-host-seed-plaintext
Sep 7, 2026
Merged

fix(desktop): keep host seeds in the clear, and keep the ones we cannot read#185
kamrul1157024 merged 1 commit into
mainfrom
fix/desktop-host-seed-plaintext

Conversation

@kamrul1157024

Copy link
Copy Markdown
Owner

What broke

Installing a new desktop build looked like the app had forgotten every host it was paired with.

Each host's device seed went through Electron's safeStorage. On macOS that is the Keychain, and the Keychain binds an item to the code signature that wrote it. electron-builder.yml sets identity: null, so every build is ad-hoc signed with a fresh identity — the new build could not read what the old one wrote.

From there the loss was automatic:

  1. load() skipped any host whose seed would not decrypt (if (!seed) continue).
  2. persist() rebuilds the file from what is in memory.
  3. So the first pair or rename after the upgrade erased the skipped hosts from hosts.json for good.

What changed

  • Nothing is encrypted. The seed authenticates to a daemon on loopback or a tailnet, sits beside a plaintext helios.db, and hosts.json is already 0600 — the Keychain was buying very little and cost real data. safeStorage is gone from the file.
  • An entry left over from the encrypting builds is kept verbatim through a write rather than dropped. It is unreadable, but unreadable is not a reason to delete someone's host.

Anyone upgrading past this keeps their old entries in the file; a host that was encrypted still has to be paired again, because no key for it exists on the machine any more.

Test plan

  • desktop/e2e/hosts-store.spec.ts — seeds a readable host beside a legacy encrypted: true one, renames through the Hosts pane to force a write, asserts the legacy entry survived and the readable one is written back with no encrypted flag. Fails on main, passes here.
  • npm test — 292 pass
  • npx playwright test — 58 pass
  • npm run typecheck

…ot read

The seed for each paired daemon went through Electron's safeStorage. On macOS
that is the Keychain, which binds an item to the code signature that wrote it,
and these builds are ad-hoc signed with a fresh identity every time. So an
upgrade could not read the seeds the previous build had written: load() skipped
every host that failed to decrypt, and the next persist() — a pair, a rename —
rebuilt the file from memory and erased them. Installing a new DMG looked like
the app had forgotten every host it had.

The protection was not worth that. The seed authenticates to a daemon on
loopback or a tailnet, next to a plaintext helios.db, and hosts.json is already
0600. So nothing is encrypted now, and an entry left over from the builds that
did encrypt is carried through a write instead of being dropped — unreadable is
not a reason to delete someone's host.
@kamrul1157024
kamrul1157024 merged commit aa7041d into main Sep 7, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant