Sync with the server and scan-to-join onboarding
ci / build (ubuntu-22.04) (push) Canceled after 0s
ci / build (windows-2025) (push) Canceled after 0s
ci / deny (push) Canceled after 0s

- Device key (DeviceSigner): Windows TPM via CNG with a DPAPI-sealed software
  fallback, Linux Secret Service with a 0600 file fallback (warned in Settings),
  macOS keychain for dev runs. The store used is recorded; no silent switching.
- SyncService (Tauri-free): server URL and pins, create account with the
  server's setup code, sync after unlock/save/browser save, every 15 min while
  unlocked and on demand, status events, integrity alarm, device list with
  fingerprint approval and revoke.
- Onboarding: "Use my phone" (join_start/poll, QR rendered locally, 6-digit
  code), Emergency Kit from PDF/PNG/JPEG (hayro + rqrr), paste setup code, new
  account. Then only the master password.
- From a signed-in desktop: "Add a phone or tablet" QR with countdown (cancel,
  lock and quit revoke the ticket) and "Approve a computer" via pasted join code.
- KESTROVE_DATA_DIR / KESTROVE_DEVICE_NAME for a second instance; ignored
  live test drives three instances against a real server.
- deny: ignore three unmaintained notices for zbus 5 sub-crates (Linux keyring).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-18 07:51:06 +09:00
co-authored by Claude Opus 5.5
parent 7701b65087
commit e19370f261
13 changed files with 3532 additions and 116 deletions
+31 -2
View File
@@ -1,7 +1,7 @@
:root { color-scheme: light dark; font: 14px system-ui, sans-serif; }
body { margin: 0; }
main { display: grid; grid-template-columns: 300px 1fr; height: 100vh; }
.center { display: grid; place-items: center; height: 100vh; grid-column: 1 / -1; }
main { display: grid; grid-template-columns: 300px 1fr; height: 100vh; overflow: auto; }
.center { display: grid; place-items: center; min-height: 100vh; padding: 16px 0; box-sizing: border-box; grid-column: 1 / -1; }
.card { display: grid; gap: 8px; width: 340px; }
input, button { font: inherit; padding: 8px; border-radius: 6px; border: 1px solid #8886; }
button { cursor: pointer; }
@@ -27,3 +27,32 @@ ul.plain li { list-style: none; padding: 8px 0; border-bottom: 1px solid #8883;
.prompt { background: Canvas; color: CanvasText; border-radius: 10px; padding: 16px 20px; width: 420px; box-shadow: 0 10px 40px #0005; }
.words { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 12px; }
.words span { font: 600 15px ui-monospace, monospace; padding: 4px 8px; border-radius: 6px; background: #8882; }
#modal { position: fixed; inset: 0; background: #0006; display: grid; place-content: center; z-index: 2; }
#modal[hidden] { display: none; }
#modal .prompt { display: grid; gap: 8px; max-height: 90vh; overflow: auto; }
aside { display: flex; flex-direction: column; height: 100vh; box-sizing: border-box; }
main > section { height: 100vh; overflow: auto; box-sizing: border-box; }
aside > ul { flex: 1; }
.sync { border-top: 1px solid #8884; padding: 8px 12px; display: grid; gap: 8px; }
.between { justify-content: space-between; }
.small { font-size: 12px; }
.big { font-size: 18px; }
.grid { display: grid; gap: 8px; }
.card.wide { width: 440px; }
.tabs { display: flex; gap: 4px; flex-wrap: wrap; }
.tab { border-color: transparent; background: none; }
.tab.sel { background: #8883; font-weight: 600; }
textarea { font: inherit; padding: 8px; border-radius: 6px; border: 1px solid #8886; resize: vertical; width: 100%; box-sizing: border-box; }
.qr { width: 240px; height: 240px; justify-self: center; image-rendering: pixelated; border-radius: 6px; }
.code { text-align: center; padding: 8px; border-radius: 8px; background: #8881; }
.digits { font: 700 32px ui-monospace, monospace; letter-spacing: 2px; text-align: center; }
.steps { margin: 0; padding-left: 20px; display: grid; gap: 4px; }
.notice { background: #2c6e8622; border-radius: 8px; padding: 8px 10px; }
.alarm { background: #d3333322; border: 1px solid #d33; border-radius: 8px; padding: 8px 10px; }
.alarm p { margin: 4px 0 0; }
.warn { color: #b26b00; }
.link { border: none; background: none; color: #2c6e86; padding: 0; text-decoration: underline; justify-self: start; }
.facts { display: grid; grid-template-columns: max-content 1fr; gap: 4px 12px; margin: 0 0 8px; }
.facts dd { margin: 0; overflow-wrap: anywhere; }
details { display: grid; gap: 6px; }
summary { cursor: pointer; }