From 1e3fa970f6b62100c350d5a6cb054de7dfc36b5d Mon Sep 17 00:00:00 2001 From: Daniel Gorgonha Date: Fri, 31 Jul 2026 14:05:46 -0300 Subject: [PATCH 1/6] feat(tauri): honest Tauri 2.0 scaffold + native shell plan (unvalidated) Lay down groundwork for a native Konclave shell without overclaiming. Nothing here is compiled or run: the dev machine's WSLg/GTK webview does not render (ADR-0004), and no macOS/iOS/Android build host was available. docs/TAURI-PLAN.md: what the browser path already validates (UI, konclave-wasm crypto, relay/helper protocol, custody invariant) vs. what Tauri adds (OS-keychain share persistence, native packaging, offline shell, optional bundled orchestrator). Includes the share-persistence abstraction design (a ShareStore over the existing IndexedDB storage.ts plus a Tauri keychain backend, encryption stays in the UI), a per-platform build matrix (Windows/macOS/Linux desktop + iOS/Android, each with toolchain and an honest "what blocks validation here" note), and security notes. All Mermaid diagrams are GitHub-renderable. src-tauri/: minimal, schema-valid Tauri 2.0 scaffold. Cargo.toml (lib crate-type for mobile), tauri.conf.json (frontendDist -> ../ui/dist), lib.rs (app builder + three keychain commands that move only already-encrypted share bytes, with one offline base64 round-trip test), main.rs, capabilities, .gitignore. Every source file is headed as an unvalidated scaffold. Does not touch ui/orchestrator/konclave-wasm/helper-server source or CI. The share-store.ts swap is documented but not added, so the ui build stays green until the Tauri deps are installed. --- docs/TAURI-PLAN.md | 224 ++++++++++++++++++++++++++++ src-tauri/.gitignore | 9 ++ src-tauri/Cargo.toml | 50 +++++++ src-tauri/README.md | 94 ++++++++++-- src-tauri/build.rs | 5 + src-tauri/capabilities/default.json | 7 + src-tauri/src/lib.rs | 137 +++++++++++++++++ src-tauri/src/main.rs | 10 ++ src-tauri/tauri.conf.json | 38 +++++ 9 files changed, 560 insertions(+), 14 deletions(-) create mode 100644 docs/TAURI-PLAN.md create mode 100644 src-tauri/.gitignore create mode 100644 src-tauri/Cargo.toml create mode 100644 src-tauri/build.rs create mode 100644 src-tauri/capabilities/default.json create mode 100644 src-tauri/src/lib.rs create mode 100644 src-tauri/src/main.rs create mode 100644 src-tauri/tauri.conf.json diff --git a/docs/TAURI-PLAN.md b/docs/TAURI-PLAN.md new file mode 100644 index 0000000..f1cf2bf --- /dev/null +++ b/docs/TAURI-PLAN.md @@ -0,0 +1,224 @@ +# TAURI-PLAN.md: a native shell for Konclave, honestly scoped + +**Status: PLAN + UNVALIDATED SCAFFOLD.** Nothing in `src-tauri/` has been compiled or run in +this repo's environment. The dev machine's WSLg/GTK webview does not paint a window +([ADR-0004](adr/0004-local-http-bridge.md)), and no macOS / iOS / Android build host was +available. This document is groundwork: it says what a native Konclave would be, what it +reuses, what it adds, and exactly what still needs real hardware to prove. Where a claim +cannot be checked without a build, it is labelled as such rather than asserted. + +This plan is consistent with the two prior delivery decisions and does not reopen them: + +- [ADR-0004](adr/0004-local-http-bridge.md): Tauri packaging is a roadmap item; the loopback + HTTP bridge is the working transport today. Tauri changes the delivery form, not the trust + model. +- [ADR-0005](adr/0005-web-first-delivery.md): web-first is primary; native shells are optional + wrappers. A Tauri shell is exactly such a wrapper. + +## 1. The key insight: Tauri reuses what the browser already proved + +Konclave's client is already a browser app. The `/net` flow runs a real cross-device DKG and a +FROST signing ceremony entirely in-browser via `konclave-wasm`, over the blind relay, with the +share held in encrypted IndexedDB (`ui/src/storage.ts`). ADR-0005 records this is proven +device-to-device on the hosted relay. + +A Tauri app is a native OS webview pointed at a web bundle. So a Tauri Konclave loads the same +`ui/dist` and runs the same code. The scaffold wires this with one line of config: +`tauri.conf.json` -> `build.frontendDist: "../ui/dist"`. + +### What is ALREADY validated by the browser path (carries over unchanged) + +These need no re-validation under Tauri, because Tauri runs the identical JS/WASM: + +- The React UI and all screens (`ui/`). +- `konclave-wasm` crypto: DKG (`DkgSession`), the confidential channel (`sealTo`/`open`), and + FROST signing (`Coordinator`, `participantRound1/2`). +- The relay transport (`ui/src/net.ts`) and the helper signing-request protocol + (`ui/src/net-sign.ts`, Architecture B). +- The custody invariant: the share stays on the device, only public / encrypted material + crosses the wire. + +### What Tauri ADDS (and therefore what is genuinely new and unproven here) + +- **Durable, OS-backed share persistence** in the OS keychain instead of a browser origin's + IndexedDB (Section 3). This is the one new native surface in the scaffold. +- **Native packaging / distribution** as an installable app per OS (Section 4). +- **Offline app shell** by default: a native binary ships its own UI assets, so it opens with + no network (the relay/helper are still needed to actually run a ceremony, exactly as on web). +- **Optionally, bundling the local `orchestrator`** so the native app can also be the helper + and the loopback bridge on the same machine (Section 5). Not in the scaffold. + +Everything in this second list is PLAN or SCAFFOLD, not validated. + +```mermaid +flowchart TD + subgraph shared["Shared, browser-proven (unchanged under Tauri)"] + ui["Vite/React UI, ui/dist"] + wasm["konclave-wasm: DKG, seal, FROST"] + net["relay + helper protocol"] + end + browser["Browser tab or PWA"] --> ui + tauri["Tauri native webview"] --> ui + ui --> wasm + ui --> net + browser -. share at rest .-> idb["Encrypted IndexedDB"] + tauri -. share at rest .-> kc["OS keychain, native command"] +``` + +## 2. Why bother, given web-first (ADR-0005) + +Web-first stays primary. A native shell earns its place only for what a browser tab cannot give: + +- **Durable custody.** A browser can evict IndexedDB (storage pressure, "clear site data", + private windows). A treasurer losing a share to a cleared cache is a real failure mode. The OS + keychain is backed up and survives browser state. +- **A real installed app.** An icon, no address bar, offline launch, OS integration. For a + non-technical treasurer this is the difference between "a website" and "my vault app". +- **A path to bundling the orchestrator/helper** locally, so a single desktop install is a + self-contained node (bridge + helper + UI), matching the original local-first vision. + +## 3. The share-persistence swap (design, not yet wired) + +The goal: the SAME UI code persists its share through either the browser IndexedDB path +(`ui/src/storage.ts`) or a Tauri keychain command, chosen at runtime, with no screen rewrite. + +### 3.1 The abstraction + +Introduce a narrow interface that both backends satisfy. `ui/src/storage.ts` already has the +right shape (`saveVault` / `loadVault` / `listVaults` / `deleteVault`); the abstraction is a +thin `ShareStore` over it plus a Tauri implementation. + +```ts +// SKETCH for ui/src/share-store.ts, NOT added to the repo yet. +// Reason it is not added: a live .ts that imports '@tauri-apps/api' would break `npm --prefix +// ui run build` and CI, because that dependency is not installed. It lands with the Tauri work. +export interface ShareStore { + save(id: string, data: VaultData, passphrase: string): Promise + load(id: string, passphrase: string): Promise + list(): Promise + remove(id: string): Promise +} + +// Runtime pick: Tauri injects a global, so the same bundle works in a browser and in the shell. +export function isTauri(): boolean { + return typeof (globalThis as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__ !== 'undefined' +} + +// Browser backend: the existing, tested module verbatim (ui/src/storage.ts, storage.test.ts). +export const indexedDbStore: ShareStore = { save: saveVault, load: loadVault, list: listVaults, remove: deleteVault } + +// Native backend: the UI still derives the key and seals the share (WebCrypto works in the +// webview), then hands only CIPHERTEXT to the keychain command. The keychain is durable at-rest +// storage, NOT the encryptor. This keeps one sealing path and one threat model across backends. +export const tauriKeychainStore: ShareStore = { + async save(id, data, passphrase) { + const cipher = await sealShare(data.sealedShare, passphrase) // same PBKDF2/AES-GCM as storage.ts + await invoke('keychain_set_share', { id, shareB64: b64(cipher) }) + await putPublicMeta(id, data) // public metadata can stay in IndexedDB + }, + async load(id, passphrase) { + const shareB64 = await invoke('keychain_get_share', { id }) + const plain = await openShare(unb64(shareB64), passphrase) + return { ...(await getPublicMeta(id)), sealedShare: plain } + }, + list: listPublicMeta, + async remove(id) { await invoke('keychain_delete_share', { id }); await delPublicMeta(id) }, +} + +export const shareStore: ShareStore = isTauri() ? tauriKeychainStore : indexedDbStore +``` + +`NetVault.tsx` would import `shareStore` instead of the four functions directly. That is a +small, mechanical, non-breaking change, deferred until the Tauri deps exist so CI stays green. + +### 3.2 Where encryption happens (deliberate) + +The UI stays the encryptor in BOTH backends (PBKDF2 -> AES-GCM, `ui/src/storage.ts`). The +keychain only holds the resulting ciphertext. Rationale: + +- One sealing path, one audited threat model, whether on web or native. +- The native command (`src-tauri/src/lib.rs`) never touches plaintext key material, so the + Rust/webview boundary carries only opaque bytes (mirrors the relay/helper discipline). +- The keychain adds a second at-rest factor (OS account protection) ON TOP of the passphrase, + rather than replacing it. + +An alternative (keychain-only, no app passphrase, relying on OS unlock) is viable and simpler +for the user, but it drops the passphrase factor and diverges from the web path. Kept as a +documented option, not the default. + +## 4. Per-platform build matrix + +Legend: **scaffolded** = config/code present for it here; **plan-only** = documented, no files +specific to it; **needs-hardware** = cannot even be attempted in this environment. + +| Target | Toolchain needed | Bundle | Status here | What blocks validation | +|---|---|---|---|---| +| Windows desktop | Rust + MSVC build tools, WebView2 (bundled on Win 11), Tauri CLI 2 | `.msi`, `.exe` | scaffolded, plan-only | No Windows build host in this environment. | +| macOS desktop | Rust, Xcode CLT, Tauri CLI 2; Apple Developer ID to sign/notarize | `.app`, `.dmg` | scaffolded, plan-only | Needs a Mac; signing needs an Apple ID. | +| Linux desktop | Rust, `libwebkit2gtk-4.1-dev`, Tauri CLI 2 | `.deb`, `.rpm`, AppImage | scaffolded, needs-hardware to VALIDATE | Compiles in principle, but the WSLg webview does not render here (ADR-0004), so it cannot be seen to work on this box. | +| iOS | macOS + Xcode, Apple Developer account, Rust ios targets | `.ipa` via `tauri ios` | plan-only | Impossible off a Mac; signing/provisioning required. | +| Android | Android SDK + NDK, `ANDROID_HOME`/`NDK_HOME`, Rust android targets, Tauri CLI 2 | `.apk`/`.aab` via `tauri android` | plan-only | No Android SDK/NDK installed here. | + +The crate is already shaped for mobile: `src-tauri/Cargo.toml` sets +`crate-type = ["staticlib", "cdylib", "rlib"]` and `src/lib.rs` uses +`#[cfg_attr(mobile, tauri::mobile_entry_point)]`, so `tauri android init` / `tauri ios init` +have what they need. That shape is asserted from the Tauri 2 mobile docs, not from a run here. + +Exact commands per target are in [`../src-tauri/README.md`](../src-tauri/README.md). + +## 5. Optionally bundling the orchestrator (beyond the scaffold) + +ADR-0004's loopback bridge (`konclave serve`) and Architecture B's helper are Rust in the +`orchestrator` crate. A desktop Tauri build could spawn that as a sidecar (Tauri's +`externalBin`) or link it, so one install is UI + helper + bridge on the same machine, fully +local-first. This is NOT in the scaffold (it needs the orchestrator to build for each target, +including its SQLCipher/native deps, which is its own validation effort). It is the natural next +milestone after a desktop build renders. + +Mobile is different: on iOS/Android the phone is a signing device only. It holds its share and +signs in-webview; build/prove/broadcast stay off-device via a remote helper (Architecture B), +exactly as on web. No orchestrator on the phone. + +```mermaid +flowchart LR + phone["Phone: Tauri webview + share in keychain"] -->|"public and encrypted only"| relay["Blind relay, never sees a share"] + desktop["Desktop: Tauri + bundled orchestrator/helper"] -->|"public and encrypted only"| relay + desktop -->|"build, prove, broadcast PCZT"| chain["Zcash network"] +``` + +## 6. Security notes + +- **The share never leaves the device.** In both backends the plaintext share stays local. Over + the wire, only public or encrypted material moves (unchanged from the browser path, §6.3/§6.4 + of CLAUDE.md). The native keychain command receives only ciphertext. +- **Keychain vs IndexedDB tradeoffs.** + - IndexedDB is origin-scoped and app-clearable; convenient, but evictable and tied to browser + state. It is protected only by the app passphrase. + - The OS keychain (Windows Credential Manager / macOS + iOS Keychain / Linux Secret Service) + is durable, OS-account protected, and survives browser resets. Cost: it is a platform + surface with per-OS behavior (Linux Secret Service needs a running keyring daemon; headless + Linux has no backend), so the app must degrade gracefully to the IndexedDB path when no + keychain is present. The design keeps the passphrase factor either way. +- **The relay and helper stay blind.** Tauri changes nothing about coordination: the relay + forwards opaque bytes, and the helper (Architecture B) builds/proves/broadcasts without ever + holding a share. A quorum is still required to move funds; a native shell does not weaken that. +- **CSP must be tightened before release.** The scaffold ships `csp: null` so the shell loads at + all. A real build must set a strict CSP: allow `wasm-unsafe-eval` for `konclave-wasm`, and + restrict `connect-src` to the configured relay and helper origins only. Tracked as a build-time + task, not a scaffold default. +- **No new trust in the packager.** Distribution (code signing, notarization, store review) adds + supply-chain responsibilities but does not change the on-device custody guarantee. + +## 7. Honest open items before any "native app" claim + +- Compile the shell on each desktop host (none available here). +- Render a real window (blocked on this machine by WSLg/GTK, ADR-0004). +- Wire `ui/src/share-store.ts` and switch `NetVault.tsx` to `shareStore` (deferred so CI stays + green until the Tauri deps are installed). +- Prove the keychain round-trip on each OS (the base64 helper has an offline test; the keychain + itself is untested). +- Mobile: init + build + sign on real Mac/Android toolchains. +- Optional: build the orchestrator per target for the bundled-helper desktop variant. + +Until those are done, Konclave's honest delivery statement stays: **web-first, proven in the +browser; native shell scaffolded and planned, not built.** diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore new file mode 100644 index 0000000..d88a890 --- /dev/null +++ b/src-tauri/.gitignore @@ -0,0 +1,9 @@ +# Rust build output +/target + +# Tauri-generated code (schemas, mobile projects) is produced by `tauri android/ios init` +# and the build script; regenerate it per host rather than committing it. +/gen + +# Generated app icons (produce with `npx tauri icon`); binary, host-generated, not source. +/icons diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml new file mode 100644 index 0000000..3e5978d --- /dev/null +++ b/src-tauri/Cargo.toml @@ -0,0 +1,50 @@ +# Konclave Tauri shell (Layer 3 delivery wrapper). +# +# UNVALIDATED SCAFFOLD. This crate has NOT been compiled or run in this environment +# (the dev machine's WSLg/GTK webview does not render, see docs/adr/0004-local-http-bridge.md). +# It is laid down as honest groundwork per docs/TAURI-PLAN.md. Versions are chosen to be +# coherent with Tauri 2.x and the rest of the repo, not verified by a successful build here. + +[package] +name = "konclave-tauri" +version = "0.1.0" +edition = "2021" +description = "Konclave native shell (Tauri 2.0): wraps the Vite/React UI in an OS webview and adds OS-keychain share persistence" +license = "MIT OR Apache-2.0" +# Keep the shell out of any future cargo workspace by default; it builds standalone. +publish = false + +# staticlib + cdylib are required by Tauri 2's mobile targets (iOS/Android link the app +# as a library); rlib keeps the crate usable from a normal `cargo build` on desktop. +[lib] +name = "konclave_tauri_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +# The webview shell. `features` are intentionally empty here; a real build enables what a +# given target needs (for example a tray or a custom protocol) once validated on hardware. +tauri = { version = "2", features = [] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +# OS keychain for durable, on-device share persistence, replacing the browser's IndexedDB +# when running inside the native shell. Same crate the orchestrator already uses for the +# sealing key (audit C2): Windows Credential Manager / macOS + iOS Keychain / Linux Secret +# Service. Pinned with NO backend feature so this scaffold resolves anywhere; a real build +# turns on the platform-native backend per target (see the commented block below). +keyring = "3" + +# Per-target keychain backends (uncomment on the matching build host, once validated): +# [target.'cfg(target_os = "windows")'.dependencies] +# keyring = { version = "3", features = ["windows-native"] } +# [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] +# keyring = { version = "3", features = ["apple-native"] } +# [target.'cfg(target_os = "linux")'.dependencies] +# keyring = { version = "3", features = ["sync-secret-service"] } + +[features] +# `custom-protocol` is the Tauri convention for a production (non-dev-server) build. +default = ["custom-protocol"] +custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/README.md b/src-tauri/README.md index 9a04443..cccdd0e 100644 --- a/src-tauri/README.md +++ b/src-tauri/README.md @@ -1,19 +1,85 @@ -# src-tauri/ +# src-tauri/: Konclave native shell (Tauri 2.0) -**Placeholder: Tauri packaging is on the roadmap, not built yet.** +**Status: UNVALIDATED SCAFFOLD. Not built, not run, not validated in this repo's environment.** -The plan was a Tauri desktop shell hosting the Rust backend. During integration the -WSLg/GTK window would not render on the dev machine, so we pivoted to a **loopback HTTP -bridge**: `konclave serve` binds `127.0.0.1` and serves the `ui` bundle plus a JSON API -wired to the tested core. Same local-first guarantee (keys never leave the device), only -the transport differs. See [docs/adr/0004-local-http-bridge.md](../docs/adr/0004-local-http-bridge.md). +This directory is honest groundwork for packaging Konclave as a native app. It has never +compiled or launched here: the dev machine's WSLg/GTK webview does not paint a window +(see [../docs/adr/0004-local-http-bridge.md](../docs/adr/0004-local-http-bridge.md)), and no +macOS / iOS / Android build host was available. Treat every command below as "what a +developer on the right hardware would run", not as something proven. -Where the code actually lives today: +The full rationale, the browser-vs-Tauri boundary, the share-persistence design, and the +per-platform matrix live in [../docs/TAURI-PLAN.md](../docs/TAURI-PLAN.md). Read that first. -- **Backend / orchestrator (Layer 2):** `orchestrator/`, holding the proposal state machine, - validation, wallet, ceremony, store, sealing, and the loopback bridge. -- **FROST↔PCZT bridge:** `konclave-signer/`. -- **Frontend (Layer 3, the UI):** `ui/`. +## What this shell is -Packaging the app as a single Tauri desktop binary is a post-submission roadmap item; it -changes only the delivery, not the architecture. +A thin Tauri 2.0 wrapper that loads the **already-built** Vite/React UI (`../ui/dist`) in a +native OS webview. The entire browser-proven app carries over unchanged: the `/net` DKG + +FROST flow, the relay/helper protocol, and the `konclave-wasm` crypto all run inside the +webview exactly as they do in a browser tab. The shell adds only one native capability today: +three `invoke` commands (`keychain_set_share` / `keychain_get_share` / `keychain_delete_share` +in `src/lib.rs`) that let the UI persist its already-encrypted per-device share in the **OS +keychain** instead of browser IndexedDB. Plaintext key material never crosses that boundary. + +## Files + +| File | Purpose | +|---|---| +| `Cargo.toml` | Crate + Tauri 2.x deps; `lib` crate-type set for mobile targets. | +| `build.rs` | Standard `tauri_build::build()`. | +| `src/lib.rs` | App builder + the three keychain commands. Has one offline unit test (base64 round-trip). | +| `src/main.rs` | Desktop entry point that calls `run()`. | +| `tauri.conf.json` | `frontendDist` -> `../ui/dist`; window; CSP is `null` in the scaffold (must be tightened). | +| `capabilities/default.json` | Core default permissions only. | + +## Prerequisites (all hosts) + +- Rust (repo uses 1.95) and the Tauri CLI: `cargo install tauri-cli --version "^2"` (or `npm i -g @tauri-apps/cli@2`). +- Build the UI at least once so `../ui/dist` exists: `npm --prefix ../ui ci && npm --prefix ../ui run build`. +- Generate app icons once (bundling requires them): `npx tauri icon path/to/icon-1024.png`. + +## Per-target commands and honest status + +Run from `src-tauri/` (or repo root with `--config src-tauri/tauri.conf.json`). + +### Desktop + +```sh +# Dev (hot-reload against the Vite dev server): +cargo tauri dev + +# Release bundle for the current OS: +cargo tauri build +``` + +- **Windows** (`.msi` / `.exe` via WiX/NSIS), PLAN-ONLY. Needs a Windows host with MSVC + build tools + WebView2. Not run here. +- **macOS** (`.app` / `.dmg`), NEEDS HARDWARE. Needs a Mac with Xcode command-line tools; + signing/notarization need an Apple Developer ID. Not run here. +- **Linux** (`.deb` / `.rpm` / AppImage), SCAFFOLDED but BLOCKED on THIS machine. Needs + WebKitGTK (`libwebkit2gtk-4.1-dev`); the WSLg webview does not render here (ADR-0004), so + even a successful compile could not be validated visually on this box. + +### Mobile + +```sh +# Android (needs the crate's lib target, already set): +cargo tauri android init +cargo tauri android dev # or: cargo tauri android build + +# iOS: +cargo tauri ios init +cargo tauri ios dev # or: cargo tauri ios build +``` + +- **Android**, NEEDS HARDWARE/SDK. Needs the Android SDK + NDK, `ANDROID_HOME`/`NDK_HOME`, + and Rust android targets (`aarch64-linux-android`, ...). Not installed here. +- **iOS**, NEEDS HARDWARE. Needs macOS + Xcode + an Apple Developer account for signing, and + Rust ios targets (`aarch64-apple-ios`, ...). Impossible to validate off a Mac. + +## The one thing that IS checkable offline + +`src/lib.rs` has a single pure unit test (`base64_round_trips`) guarding the byte-wrapping at +the keychain boundary. It exercises no Tauri and no keychain (neither is available here). Even +that test has not been compiled in this environment, because pulling the Tauri dependency tree +was out of scope for laying down the scaffold. It is written to pass; it is not proven to. diff --git a/src-tauri/build.rs b/src-tauri/build.rs new file mode 100644 index 0000000..b326d5b --- /dev/null +++ b/src-tauri/build.rs @@ -0,0 +1,5 @@ +// UNVALIDATED SCAFFOLD (see src/lib.rs). Standard Tauri 2.0 build script: it wires the +// generated context, capabilities, and platform metadata at compile time. +fn main() { + tauri_build::build(); +} diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json new file mode 100644 index 0000000..23a888f --- /dev/null +++ b/src-tauri/capabilities/default.json @@ -0,0 +1,7 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "SCAFFOLD capability set: core defaults only. The three keychain commands (keychain_set_share / keychain_get_share / keychain_delete_share) are custom app commands wired via invoke_handler, so they need no plugin permission here. Widen this only when a plugin (for example a stronghold or a real keychain plugin) is added, and only for the windows/webviews that need it.", + "windows": ["main"], + "permissions": ["core:default"] +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs new file mode 100644 index 0000000..591f2e7 --- /dev/null +++ b/src-tauri/src/lib.rs @@ -0,0 +1,137 @@ +// UNVALIDATED SCAFFOLD. +// +// This file has NOT been compiled or run anywhere in this project. The dev machine's +// WSLg/GTK webview does not paint a window (docs/adr/0004-local-http-bridge.md), and no +// macOS/iOS/Android build host was available, so nothing here is validated by a real build. +// It is deliberately minimal and correct-in-principle groundwork for the plan in +// docs/TAURI-PLAN.md. Do not read a green CI signal into it: there is none yet. +// +// What it does when it DOES build on proper hardware: +// - opens a native webview whose content is the already-built Vite/React UI +// (tauri.conf.json -> build.frontendDist -> ../ui/dist), so the entire browser-proven +// app (the /net DKG + FROST flow, the relay/helper protocol, the konclave-wasm crypto) +// runs unchanged inside the OS webview; +// - exposes three `invoke` commands that let the SAME UI persist its per-device FROST +// share in the OS keychain instead of browser IndexedDB (ui/src/storage.ts). Only the +// ENCRYPTED share bytes (already sealed by the UI) and public metadata are stored; the +// plaintext share never crosses this boundary and never leaves the device. +// +// The share still never leaves the device (principle §6.3); the keychain is simply a more +// durable, OS-backed at-rest store than a browser origin's IndexedDB. + +use keyring::Entry; + +// A stable service name namespaces every keychain entry Konclave owns on this OS account. +const KEYCHAIN_SERVICE: &str = "app.konclave.share"; + +// The share bytes handed across the boundary are ALREADY ciphertext produced by the UI's +// sealing step; this layer treats them as opaque and only base64-wraps them so the keychain +// (which stores UTF-8 secrets) can hold arbitrary bytes. No plaintext key material here. +fn b64(bytes: &[u8]) -> String { + // Dependency-free base64 (standard alphabet, padded). Kept tiny on purpose: this scaffold + // should not drag in a crate just to move already-encrypted bytes into the keychain. + const A: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4); + for chunk in bytes.chunks(3) { + let b = [ + chunk[0], + *chunk.get(1).unwrap_or(&0), + *chunk.get(2).unwrap_or(&0), + ]; + let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32); + out.push(A[((n >> 18) & 63) as usize] as char); + out.push(A[((n >> 12) & 63) as usize] as char); + out.push(if chunk.len() > 1 { A[((n >> 6) & 63) as usize] as char } else { '=' }); + out.push(if chunk.len() > 2 { A[(n & 63) as usize] as char } else { '=' }); + } + out +} + +fn unb64(s: &str) -> Result, String> { + fn val(c: u8) -> Option { + match c { + b'A'..=b'Z' => Some((c - b'A') as u32), + b'a'..=b'z' => Some((c - b'a' + 26) as u32), + b'0'..=b'9' => Some((c - b'0' + 52) as u32), + b'+' => Some(62), + b'/' => Some(63), + _ => None, + } + } + let clean: Vec = s.bytes().filter(|&c| c != b'=' && !c.is_ascii_whitespace()).collect(); + let mut out = Vec::with_capacity(clean.len() / 4 * 3); + for chunk in clean.chunks(4) { + let mut n = 0u32; + let mut bits = 0; + for &c in chunk { + n = (n << 6) | val(c).ok_or("invalid base64 in stored share")?; + bits += 6; + } + // Emit only the fully-decoded bytes for this chunk. + n <<= 24 - bits; + for i in 0..(bits / 8) { + out.push(((n >> (16 - i * 8)) & 0xff) as u8); + } + } + Ok(out) +} + +/// Store the (already-encrypted) share bytes for `id` in the OS keychain. +/// `share_b64` is base64 of ciphertext produced by the UI; this command does not encrypt +/// or decrypt, and never sees plaintext key material. +#[tauri::command] +fn keychain_set_share(id: String, share_b64: String) -> Result<(), String> { + // Validate the payload is well-formed base64 before writing (boundary check, §6.8). + let _ = unb64(&share_b64)?; + let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; + entry.set_password(&share_b64).map_err(|e| e.to_string()) +} + +/// Return the stored share bytes (base64) for `id`, or an explicit "not found" error. +#[tauri::command] +fn keychain_get_share(id: String) -> Result { + let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; + match entry.get_password() { + Ok(s) => Ok(s), + Err(keyring::Error::NoEntry) => Err(format!("no share stored on this device for id {id}")), + Err(e) => Err(e.to_string()), + } +} + +/// Remove the stored share for `id` from this device's keychain. +#[tauri::command] +fn keychain_delete_share(id: String) -> Result<(), String> { + let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; + match entry.delete_credential() { + Ok(()) | Err(keyring::Error::NoEntry) => Ok(()), + Err(e) => Err(e.to_string()), + } +} + +// Mobile (iOS/Android) enters through this function; desktop uses the same path via main.rs. +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .invoke_handler(tauri::generate_handler![ + keychain_set_share, + keychain_get_share, + keychain_delete_share, + ]) + .run(tauri::generate_context!()) + .expect("error while running the Konclave Tauri application"); +} + +#[cfg(test)] +mod tests { + use super::{b64, unb64}; + + // A pure round-trip check of the base64 helper. This does NOT exercise Tauri or the + // keychain (neither is available here); it only guards the byte-wrapping used at the + // boundary. It is the only thing in this scaffold that can be honestly tested offline. + #[test] + fn base64_round_trips() { + for case in [b"".as_slice(), b"a", b"ab", b"abc", b"abcd", &[0u8, 255, 1, 254, 127]] { + assert_eq!(unb64(&b64(case)).unwrap(), case, "round-trip mismatch"); + } + } +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs new file mode 100644 index 0000000..a1174b2 --- /dev/null +++ b/src-tauri/src/main.rs @@ -0,0 +1,10 @@ +// UNVALIDATED SCAFFOLD. Not compiled or run in this environment (WSLg/GTK does not render a +// webview here, see docs/adr/0004-local-http-bridge.md). Groundwork only; see docs/TAURI-PLAN.md. +// +// Desktop entry point. On Windows release builds this attribute suppresses the console window; +// it is a no-op elsewhere. All the real setup lives in lib.rs so iOS/Android can share it. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + konclave_tauri_lib::run(); +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json new file mode 100644 index 0000000..6956573 --- /dev/null +++ b/src-tauri/tauri.conf.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "Konclave", + "version": "0.1.0", + "identifier": "app.konclave.desktop", + "build": { + "frontendDist": "../ui/dist", + "devUrl": "http://localhost:5173", + "beforeDevCommand": "npm --prefix ../ui run dev", + "beforeBuildCommand": "npm --prefix ../ui run build" + }, + "app": { + "windows": [ + { + "title": "Konclave", + "width": 1100, + "height": 820, + "minWidth": 900, + "minHeight": 600, + "resizable": true + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} From c1d8206b3f612194b77cc27d359bf569aa829c0a Mon Sep 17 00:00:00 2001 From: Daniel Gorgonha Date: Sat, 1 Aug 2026 23:03:09 -0300 Subject: [PATCH 2/6] feat(tauri): OS-keychain share store + JS bridge contract + platform choice Advance the native shell's core value: durable, OS-backed custody for the per-device FROST share, so a browser eviction (iOS Safari clears IndexedDB after ~7 days idle) no longer loses a vault. share_store.rs (no tauri dep, logic-tested headlessly, 12 green tests against keyring's in-memory mock, clippy/-D-warnings + rustfmt clean): - ShareStore trait + OS-keychain KeychainShareStore, mirroring the orchestrator's KeyStore/KeychainStore pattern and its keyring="3" pin (no backend feature). - StoreError with explicit Backend / NotFound / InvalidId variants (no silent loss). - list() served from a public-id index entry (keyring has no portable enumeration); the index holds only public vault ids, so it does not weaken the threat model. - id guard rejects empty / reserved-index ids before touching the backend. - The bytes stored are ALREADY ciphertext sealed by the UI (PBKDF2->AES-GCM); this layer is durable at-rest storage, not the encryptor. Plaintext never crosses. lib.rs: four thin #[tauri::command]s (secure_store / secure_load / secure_delete / secure_list) over the store, base64 at the JS boundary. Scaffold wiring only: the tauri crate needs a system webview (webkit2gtk/glib) absent on this machine (ADR-0004), so the enclosing crate is not compiled here. docs/NATIVE-STORAGE-BRIDGE.md: the JS<->native abstraction. A StorageBackend interface with the existing IndexedDB module (web) and an invoke-based native backend, selected at runtime via isTauri(); exact invoke arg shapes matching the Rust commands; the sealing path stays in the UI; the one-line NetVault.tsx swap. Deliberately not wired into ui/ so npm build / CI stay green until the Tauri deps are installed. docs/TAURI-PLAN.md: rewrote the share-persistence section to what is now implemented; added a Tauri 2.0 vs React Native vs Flutter comparison (recommendation: Tauri, the only option reusing both the React UI and the Rust crypto with ~zero rewrite; honest tradeoff is its younger mobile story); refreshed the status ladder (logic-tested vs unbuilt-here vs design-only vs needs-hardware). Honesty: the keychain LOGIC is proven by a real test run; the GUI does not build on this machine and mobile needs a Mac/Android host. Nothing here claims a working native build. --- docs/NATIVE-STORAGE-BRIDGE.md | 129 +++++++++ docs/TAURI-PLAN.md | 149 +++++----- src-tauri/README.md | 11 +- src-tauri/capabilities/default.json | 2 +- src-tauri/src/lib.rs | 150 +++------- src-tauri/src/share_store.rs | 413 ++++++++++++++++++++++++++++ 6 files changed, 679 insertions(+), 175 deletions(-) create mode 100644 docs/NATIVE-STORAGE-BRIDGE.md create mode 100644 src-tauri/src/share_store.rs diff --git a/docs/NATIVE-STORAGE-BRIDGE.md b/docs/NATIVE-STORAGE-BRIDGE.md new file mode 100644 index 0000000..3306706 --- /dev/null +++ b/docs/NATIVE-STORAGE-BRIDGE.md @@ -0,0 +1,129 @@ +# NATIVE-STORAGE-BRIDGE.md: one share-store, two backends + +**Status: DESIGN + WIRED RUST COMMANDS (logic-tested), JS SWAP NOT YET APPLIED.** The Rust side +(`src-tauri/src/share_store.rs` + the four `#[tauri::command]`s in `src-tauri/src/lib.rs`) is +written and its keychain logic passes a real headless test run against `keyring`'s in-memory mock. +The JS `share-store.ts` below is specified but deliberately NOT added to `ui/`, because a live +`.ts` that imports `@tauri-apps/api` would break `npm --prefix ui run build` and CI until the Tauri +dependency is installed. This doc is the exact contract so the swap is mechanical when the Tauri +work lands. It does not change the web path, which stays the tested default (ADR-0005). + +## 1. The problem it solves + +`ui/src/storage.ts` persists a `/net` vault's per-device FROST share in encrypted IndexedDB +(PBKDF2 -> AES-GCM). IndexedDB is origin-scoped and evictable: storage pressure, "clear site +data", private windows, and iOS Safari's ~7-day inactivity eviction can all drop it. A treasurer +losing a share to a cleared cache is a real failure mode. The OS keychain is durable, OS-account +protected, and survives browser resets. The native shell should let the SAME UI persist its share +in the keychain instead, with no screen rewrite. + +## 2. The abstraction + +A narrow `StorageBackend` interface both backends satisfy. `ui/src/storage.ts` already has the +right shape (`saveVault` / `loadVault` / `listVaults` / `deleteVault`), so the web backend is that +module verbatim; the native backend forwards ciphertext to the keychain via `invoke`. + +```ts +// SKETCH for ui/src/share-store.ts -- NOT added to the repo yet (see status note above). +import { invoke } from '@tauri-apps/api/core' +import { + saveVault, loadVault, listVaults, deleteVault, + type VaultData, type VaultLoaded, type VaultPublic, +} from './storage' + +export interface StorageBackend { + save(id: string, data: VaultData, passphrase: string): Promise + load(id: string, passphrase: string): Promise + list(): Promise + remove(id: string): Promise +} + +/** Tauri injects this global into the webview; the same bundle detects it at runtime. */ +export function isTauri(): boolean { + return typeof (globalThis as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__ !== 'undefined' +} + +/** Web backend: the existing, tested module unchanged (storage.ts + storage.test.ts). */ +export const webBackend: StorageBackend = { + save: saveVault, load: loadVault, list: listVaults, remove: deleteVault, +} + +/** Runtime selection. The web path is the default and stays fully tested. */ +export const shareStore: StorageBackend = isTauri() ? nativeBackend : webBackend +``` + +## 3. The native backend and its `invoke` shapes + +The UI stays the encryptor in BOTH backends. The native backend derives the key and seals the +share with the SAME WebCrypto PBKDF2 -> AES-GCM as `storage.ts` (WebCrypto is available in every +Tauri webview), then hands only CIPHERTEXT (as base64) to the keychain. The keychain is durable +at-rest storage, not the encryptor. Public metadata (`groupKey`, `address`, `roster`, `createdAt`) +carries no secret and stays in IndexedDB so vaults can be listed for unlock exactly as today. + +The Rust commands are in `src-tauri/src/lib.rs`; their argument names below are exactly what +`invoke` must send (Tauri maps a Rust `snake_case` parameter to the same key in the JS args +object). + +| JS call | Rust command | Args | Returns | +|---|---|---|---| +| `invoke('secure_store', { id, shareB64 })` | `secure_store(id: String, share_b64: String)` | `id`, `share_b64` (base64 ciphertext) | `void` (rejects on invalid base64 / backend error) | +| `invoke('secure_load', { id })` | `secure_load(id: String) -> String` | `id` | base64 ciphertext, or rejects `"no share stored on this device for id ..."` | +| `invoke('secure_delete', { id })` | `secure_delete(id: String)` | `id` | `void` (idempotent: deleting an absent id succeeds) | +| `invoke('secure_list')` | `secure_list() -> Vec` | none | array of vault ids that have a share on this device | + +> Tauri's JS `invoke` camelCases nothing automatically for custom keys: a Rust parameter named +> `share_b64` is sent as `share_b64`. To use `shareB64` in JS, either rename the Rust parameter or +> pass the key as `share_b64`. The table above assumes the Rust names; the sketch below shows the +> exact keys. + +```ts +const nativeBackend: StorageBackend = { + async save(id, data, passphrase) { + // Same sealing path as storage.ts: derive key from passphrase, AES-GCM encrypt sealedShare. + const cipher = await sealShare(data.sealedShare, passphrase) // Uint8Array of ciphertext + await invoke('secure_store', { id, share_b64: toBase64(cipher) }) + await putPublicMeta(id, data) // public metadata stays in IndexedDB + }, + async load(id, passphrase) { + const shareB64 = await invoke('secure_load', { id }) + const sealedShare = await openShare(fromBase64(shareB64), passphrase) // AES-GCM decrypt + return { ...(await getPublicMeta(id)), sealedShare } + }, + list: listVaults, // public metadata still lives in IndexedDB + async remove(id) { + await invoke('secure_delete', { id }) + await deleteVault(id) // drop the public metadata too + }, +} +``` + +`sealShare` / `openShare` / `toBase64` / `fromBase64` are the existing helpers factored out of +`storage.ts` (the AES-GCM + PBKDF2 body is already there; only the persistence target changes). + +## 4. The one-line consumer change + +`ui/src/screens/NetVault.tsx` imports the four storage functions directly today. The swap is to +import `shareStore` and call `shareStore.save/load/list/remove`. Mechanical, non-breaking, and +deferred until the Tauri deps exist so `npm run build` and CI stay green. + +## 5. Why base64 (not raw bytes) at the boundary + +`invoke` marshals JSON; strings cross cleanly, byte arrays would arrive as `number[]` and cost a +conversion on both ends. The Rust command decodes the base64 to raw bytes and stores those in the +keychain (via `keyring`'s `set_secret`), so the keychain holds real bytes, not doubly-encoded +text. `share_store.rs` carries the tiny dependency-free base64 codec and a round-trip test. + +## 6. `secure_list` and the id index (honest note) + +`keyring` has no portable "list all credentials for a service" API. `secure_list` is therefore +served from an INDEX entry the Rust store maintains under a reserved keychain account, holding the +newline-joined set of vault ids. Vault ids are PUBLIC identifiers (never secret material), so +keeping that index in the clear does not weaken the threat model. `store` adds an id to the index, +`delete` removes it, and the reserved index account can never be used as a vault id (guarded). + +## 7. Graceful degradation + +On a host with no keychain backend (headless Linux with no Secret Service daemon), the native +command errors clearly rather than losing data silently. The shell must fall back to `webBackend` +(IndexedDB) in that case: `isTauri()` gates the choice, and a failed `secure_*` call is a +recoverable, human-readable error (§6.11), never a silent share loss (§6.8). diff --git a/docs/TAURI-PLAN.md b/docs/TAURI-PLAN.md index f1cf2bf..f43a995 100644 --- a/docs/TAURI-PLAN.md +++ b/docs/TAURI-PLAN.md @@ -77,59 +77,37 @@ Web-first stays primary. A native shell earns its place only for what a browser - **A path to bundling the orchestrator/helper** locally, so a single desktop install is a self-contained node (bridge + helper + UI), matching the original local-first vision. -## 3. The share-persistence swap (design, not yet wired) +## 3. The share-persistence swap (Rust wired + logic-tested; JS swap specified) The goal: the SAME UI code persists its share through either the browser IndexedDB path (`ui/src/storage.ts`) or a Tauri keychain command, chosen at runtime, with no screen rewrite. -### 3.1 The abstraction - -Introduce a narrow interface that both backends satisfy. `ui/src/storage.ts` already has the -right shape (`saveVault` / `loadVault` / `listVaults` / `deleteVault`); the abstraction is a -thin `ShareStore` over it plus a Tauri implementation. - -```ts -// SKETCH for ui/src/share-store.ts, NOT added to the repo yet. -// Reason it is not added: a live .ts that imports '@tauri-apps/api' would break `npm --prefix -// ui run build` and CI, because that dependency is not installed. It lands with the Tauri work. -export interface ShareStore { - save(id: string, data: VaultData, passphrase: string): Promise - load(id: string, passphrase: string): Promise - list(): Promise - remove(id: string): Promise -} - -// Runtime pick: Tauri injects a global, so the same bundle works in a browser and in the shell. -export function isTauri(): boolean { - return typeof (globalThis as { __TAURI_INTERNALS__?: unknown }).__TAURI_INTERNALS__ !== 'undefined' -} - -// Browser backend: the existing, tested module verbatim (ui/src/storage.ts, storage.test.ts). -export const indexedDbStore: ShareStore = { save: saveVault, load: loadVault, list: listVaults, remove: deleteVault } - -// Native backend: the UI still derives the key and seals the share (WebCrypto works in the -// webview), then hands only CIPHERTEXT to the keychain command. The keychain is durable at-rest -// storage, NOT the encryptor. This keeps one sealing path and one threat model across backends. -export const tauriKeychainStore: ShareStore = { - async save(id, data, passphrase) { - const cipher = await sealShare(data.sealedShare, passphrase) // same PBKDF2/AES-GCM as storage.ts - await invoke('keychain_set_share', { id, shareB64: b64(cipher) }) - await putPublicMeta(id, data) // public metadata can stay in IndexedDB - }, - async load(id, passphrase) { - const shareB64 = await invoke('keychain_get_share', { id }) - const plain = await openShare(unb64(shareB64), passphrase) - return { ...(await getPublicMeta(id)), sealedShare: plain } - }, - list: listPublicMeta, - async remove(id) { await invoke('keychain_delete_share', { id }); await delPublicMeta(id) }, -} - -export const shareStore: ShareStore = isTauri() ? tauriKeychainStore : indexedDbStore -``` - -`NetVault.tsx` would import `shareStore` instead of the four functions directly. That is a -small, mechanical, non-breaking change, deferred until the Tauri deps exist so CI stays green. +### 3.1 What is now implemented (the native side) + +- **`src-tauri/src/share_store.rs`** -- a `ShareStore` trait and an OS-keychain-backed + `KeychainShareStore`, plus a `StoreError` with explicit variants (`Backend` / `NotFound` / + `InvalidId`). It has NO `tauri` dependency, so its logic is covered by a REAL headless test run + (12 tests, green) against `keyring`'s in-memory mock -- the same technique + `orchestrator::secrets::KeychainStore` uses. Because `keyring` has no portable enumeration API, + `list` is served from a public-id index entry the store maintains (see `NATIVE-STORAGE-BRIDGE.md` + §6). The `keyring` pin (`3`, no backend feature) mirrors the orchestrator exactly. +- **`src-tauri/src/lib.rs`** -- four thin `#[tauri::command]`s over that store: + `secure_store` / `secure_load` / `secure_delete` / `secure_list`. These marshal base64 at the JS + boundary and never see plaintext key material. This wiring is SCAFFOLD (the `tauri` crate needs a + system webview absent here, so the crate is not compiled), but the store it delegates to is + proven. + +### 3.2 The JS side (specified, not yet added) + +A narrow `StorageBackend` interface both backends satisfy: `ui/src/storage.ts` verbatim for web, +and an `invoke`-based native backend. Runtime selection via `isTauri()`. The exact interface, the +`invoke` argument shapes (matching the Rust command signatures above), the sealing path, and the +one-line `NetVault.tsx` consumer change are specified in +[`NATIVE-STORAGE-BRIDGE.md`](NATIVE-STORAGE-BRIDGE.md). + +It is deliberately NOT added to `ui/` yet: a live `.ts` importing `@tauri-apps/api` would break +`npm --prefix ui run build` and CI until the Tauri dependency is installed. It lands with the Tauri +work; the change is mechanical and non-breaking, and the web path stays the tested default. ### 3.2 Where encryption happens (deliberate) @@ -186,7 +164,32 @@ flowchart LR desktop -->|"build, prove, broadcast PCZT"| chain["Zcash network"] ``` -## 6. Security notes +## 6. Platform choice: Tauri 2.0 vs React Native vs Flutter + +The native shell must reuse Konclave's two biggest assets -- the existing React UI (`ui/`) and the +Rust crypto core (`konclave-wasm` for in-browser FROST/DKG, `orchestrator` for the helper/bridge) +-- while giving OS-keychain custody and staying blind and local-first (no server holds a share). +Measured against exactly those needs: + +| Need | Tauri 2.0 | React Native | Flutter | +|---|---|---|---| +| Reuse the React UI (`ui/`) | **As-is.** The webview loads `ui/dist` unchanged; every screen and the whole `/net` flow run verbatim. | Rewrite. RN is not the DOM; React components using web APIs/CSS do not port. | Rewrite in Dart/Flutter widgets. | +| Reuse the Rust crypto (`konclave-wasm`, `orchestrator`) | **Directly.** WASM runs in the webview as today; native Rust can be linked/sidecar'd (desktop) or invoked via commands. Rust is Tauri's native language. | Re-bridge. Rust reachable only via a custom native module (JSI/turbo-module) or re-run the WASM in a JS engine; extra FFI surface. | Re-bridge via Dart FFI (`dart:ffi`) or platform channels; the WASM path is awkward. | +| OS keychain (desktop + mobile) | Native Rust command over `keyring` (this scaffold) or a plugin; the same crate the orchestrator already uses. | `react-native-keychain` (mature) -- but a different code path per platform, not the Rust one. | `flutter_secure_storage` -- again a separate, non-Rust path. | +| Blind, local-first, no-server | Natural: a native binary with a bundled UI + optional local orchestrator/helper; nothing phones home. | Achievable, but the ecosystem assumes a JS backend; more to strip out. | Achievable; same caveat. | +| Single codebase desktop + mobile | Yes (desktop mature; **mobile is younger**, 2.0-era, smaller track record). | Mobile-first and very mature; desktop support is secondary/less polished. | Both mature; strong mobile, decent desktop. | +| Bundle size / footprint | Small (system webview, no bundled Chromium). | Medium. | Medium/large (bundled engine). | + +**Recommendation: Tauri 2.0.** It is the only option that reuses BOTH the existing React UI and the +Rust crypto core with essentially zero rewrite -- for a solo project that is the difference between +a shippable shell and a second full front end. The honest tradeoff is that Tauri's **mobile** story +(iOS/Android under 2.0) is younger and less battle-tested than React Native's or Flutter's, and the +Linux desktop webview does not render on this dev machine (ADR-0004), so validation waits on real +hardware. React Native or Flutter would buy a more mature mobile runtime at the cost of rewriting +the UI in a non-DOM framework AND re-bridging the Rust crypto over FFI -- re-implementing, and +re-auditing, the exact parts that are already proven. That cost is not justified for this app. + +## 7. Security notes - **The share never leaves the device.** In both backends the plaintext share stays local. Over the wire, only public or encrypted material moves (unchanged from the browser path, §6.3/§6.4 @@ -209,16 +212,36 @@ flowchart LR - **No new trust in the packager.** Distribution (code signing, notarization, store review) adds supply-chain responsibilities but does not change the on-device custody guarantee. -## 7. Honest open items before any "native app" claim - -- Compile the shell on each desktop host (none available here). -- Render a real window (blocked on this machine by WSLg/GTK, ADR-0004). -- Wire `ui/src/share-store.ts` and switch `NetVault.tsx` to `shareStore` (deferred so CI stays - green until the Tauri deps are installed). -- Prove the keychain round-trip on each OS (the base64 helper has an offline test; the keychain - itself is untested). -- Mobile: init + build + sign on real Mac/Android toolchains. -- Optional: build the orchestrator per target for the bundled-helper desktop variant. - -Until those are done, Konclave's honest delivery statement stays: **web-first, proven in the -browser; native shell scaffolded and planned, not built.** +## 8. Honest status ladder + +**Code-complete + logic-tested here (headless, no Tauri build needed):** +- `src-tauri/src/share_store.rs`: the `ShareStore` trait, `KeychainShareStore`, the public-id + index behind `list`, the id guard, error mapping, and the base64 boundary codec. **12 unit tests + pass** against `keyring`'s in-memory mock (run in an isolated crate, since the enclosing + `src-tauri` crate cannot compile here). Clippy `-D warnings` clean, rustfmt conformant. + +**Code-complete but UNBUILT here (needs a desktop/mobile host to compile and run):** +- `src-tauri/src/lib.rs`: the four `#[tauri::command]`s (`secure_store/load/delete/list`) and the + Tauri app builder. Correct-in-principle wiring, but the `tauri` crate needs a system webview + (webkit2gtk/glib) absent on this machine, so it is not compiled. No green signal on the wiring. +- `tauri.conf.json`, `Cargo.toml`, `build.rs`, `capabilities/default.json`, `main.rs`: schema-valid + Tauri 2.0 scaffold, mobile-shaped crate types, not compiled here. + +**Design-only (specified, no code committed):** +- `ui/src/share-store.ts` + the `NetVault.tsx` swap: fully specified in `NATIVE-STORAGE-BRIDGE.md` + with exact `invoke` shapes, deliberately not added so `npm run build`/CI stay green until the + Tauri deps are installed. +- Bundling the `orchestrator`/helper as a desktop sidecar (§5). + +**Needs real hardware to VALIDATE (cannot be attempted here):** +- Compile the shell on each desktop host; render a real window (blocked by WSLg/GTK, ADR-0004). +- Prove the keychain round-trip on each OS (the mock is per-`Entry`; true cross-call persistence + only exercises on a real backend -- Windows Credential Manager / macOS + iOS Keychain / Linux + Secret Service). +- Mobile: `tauri ios/android init` + build + sign on real Mac / Android SDK toolchains. +- Tighten CSP (the scaffold ships `csp: null`); code-sign / notarize per store. + +Until the desktop/mobile builds and the keychain round-trip are proven on hardware, Konclave's +honest delivery statement stays: **web-first, proven in the browser; native shell scaffolded and +planned, with the keychain share-store logic implemented and unit-tested but not yet built into a +running app.** diff --git a/src-tauri/README.md b/src-tauri/README.md index cccdd0e..fe45e9d 100644 --- a/src-tauri/README.md +++ b/src-tauri/README.md @@ -17,9 +17,11 @@ A thin Tauri 2.0 wrapper that loads the **already-built** Vite/React UI (`../ui/ native OS webview. The entire browser-proven app carries over unchanged: the `/net` DKG + FROST flow, the relay/helper protocol, and the `konclave-wasm` crypto all run inside the webview exactly as they do in a browser tab. The shell adds only one native capability today: -three `invoke` commands (`keychain_set_share` / `keychain_get_share` / `keychain_delete_share` -in `src/lib.rs`) that let the UI persist its already-encrypted per-device share in the **OS -keychain** instead of browser IndexedDB. Plaintext key material never crosses that boundary. +four `invoke` commands (`secure_store` / `secure_load` / `secure_delete` / `secure_list` in +`src/lib.rs`, over the `share_store` module) that let the UI persist its already-encrypted +per-device share in the **OS keychain** instead of browser IndexedDB. Plaintext key material +never crosses that boundary. The exact JS contract is in +[`../docs/NATIVE-STORAGE-BRIDGE.md`](../docs/NATIVE-STORAGE-BRIDGE.md). ## Files @@ -27,7 +29,8 @@ keychain** instead of browser IndexedDB. Plaintext key material never crosses th |---|---| | `Cargo.toml` | Crate + Tauri 2.x deps; `lib` crate-type set for mobile targets. | | `build.rs` | Standard `tauri_build::build()`. | -| `src/lib.rs` | App builder + the three keychain commands. Has one offline unit test (base64 round-trip). | +| `src/share_store.rs` | `ShareStore` trait + OS-keychain `KeychainShareStore` (no `tauri` dep). 12 unit tests pass headlessly against `keyring`'s mock. | +| `src/lib.rs` | App builder + the four `secure_*` commands delegating to `share_store`. Scaffold wiring (not compiled here). | | `src/main.rs` | Desktop entry point that calls `run()`. | | `tauri.conf.json` | `frontendDist` -> `../ui/dist`; window; CSP is `null` in the scaffold (must be tightened). | | `capabilities/default.json` | Core default permissions only. | diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 23a888f..1e4619b 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -1,7 +1,7 @@ { "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", - "description": "SCAFFOLD capability set: core defaults only. The three keychain commands (keychain_set_share / keychain_get_share / keychain_delete_share) are custom app commands wired via invoke_handler, so they need no plugin permission here. Widen this only when a plugin (for example a stronghold or a real keychain plugin) is added, and only for the windows/webviews that need it.", + "description": "SCAFFOLD capability set: core defaults only. The four share-store commands (secure_store / secure_load / secure_delete / secure_list) are custom app commands wired via invoke_handler, so they need no plugin permission here. Widen this only when a plugin (for example a stronghold or a real keychain plugin) is added, and only for the windows/webviews that need it.", "windows": ["main"], "permissions": ["core:default"] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 591f2e7..d376ac8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,111 +1,61 @@ -// UNVALIDATED SCAFFOLD. +// UNVALIDATED SCAFFOLD (the command wiring), LOGIC-TESTED CORE (share_store.rs). // -// This file has NOT been compiled or run anywhere in this project. The dev machine's -// WSLg/GTK webview does not paint a window (docs/adr/0004-local-http-bridge.md), and no -// macOS/iOS/Android build host was available, so nothing here is validated by a real build. -// It is deliberately minimal and correct-in-principle groundwork for the plan in -// docs/TAURI-PLAN.md. Do not read a green CI signal into it: there is none yet. +// This file has NOT been compiled or run anywhere in this project: the `tauri` crate needs a +// system webview (webkit2gtk/glib), which is absent on this machine (docs/adr/0004-local-http-bridge.md), +// and no macOS/iOS/Android build host was available. Do not read a green CI signal into the Tauri +// wiring: there is none yet. What IS proven is the keychain logic it delegates to -- `share_store` +// has no `tauri` dependency and its tests run headlessly against `keyring`'s in-memory mock (the +// same technique `orchestrator::secrets::KeychainStore` uses). See docs/TAURI-PLAN.md. // // What it does when it DOES build on proper hardware: // - opens a native webview whose content is the already-built Vite/React UI -// (tauri.conf.json -> build.frontendDist -> ../ui/dist), so the entire browser-proven -// app (the /net DKG + FROST flow, the relay/helper protocol, the konclave-wasm crypto) -// runs unchanged inside the OS webview; -// - exposes three `invoke` commands that let the SAME UI persist its per-device FROST -// share in the OS keychain instead of browser IndexedDB (ui/src/storage.ts). Only the -// ENCRYPTED share bytes (already sealed by the UI) and public metadata are stored; the -// plaintext share never crosses this boundary and never leaves the device. +// (tauri.conf.json -> build.frontendDist -> ../ui/dist), so the entire browser-proven app +// (the /net DKG + FROST flow, the relay/helper protocol, the konclave-wasm crypto) runs +// unchanged inside the OS webview; +// - exposes four `invoke` commands (secure_store / secure_load / secure_delete / secure_list) +// that let the SAME UI persist its per-device FROST share in the OS keychain instead of +// browser IndexedDB (ui/src/storage.ts). Only ENCRYPTED share bytes (already sealed by the +// UI) and public vault ids cross this boundary; plaintext key material never does. // -// The share still never leaves the device (principle §6.3); the keychain is simply a more -// durable, OS-backed at-rest store than a browser origin's IndexedDB. +// The share still never leaves the device (principle §6.3); the keychain is simply a more durable, +// OS-backed at-rest store than a browser origin's IndexedDB. The JS side of this contract is +// specified in docs/NATIVE-STORAGE-BRIDGE.md (exact invoke shapes). -use keyring::Entry; +mod share_store; -// A stable service name namespaces every keychain entry Konclave owns on this OS account. -const KEYCHAIN_SERVICE: &str = "app.konclave.share"; +use share_store::{b64, unb64, KeychainShareStore, ShareStore, StoreError}; -// The share bytes handed across the boundary are ALREADY ciphertext produced by the UI's -// sealing step; this layer treats them as opaque and only base64-wraps them so the keychain -// (which stores UTF-8 secrets) can hold arbitrary bytes. No plaintext key material here. -fn b64(bytes: &[u8]) -> String { - // Dependency-free base64 (standard alphabet, padded). Kept tiny on purpose: this scaffold - // should not drag in a crate just to move already-encrypted bytes into the keychain. - const A: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4); - for chunk in bytes.chunks(3) { - let b = [ - chunk[0], - *chunk.get(1).unwrap_or(&0), - *chunk.get(2).unwrap_or(&0), - ]; - let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32); - out.push(A[((n >> 18) & 63) as usize] as char); - out.push(A[((n >> 12) & 63) as usize] as char); - out.push(if chunk.len() > 1 { A[((n >> 6) & 63) as usize] as char } else { '=' }); - out.push(if chunk.len() > 2 { A[(n & 63) as usize] as char } else { '=' }); - } - out +/// Map a storage failure to a UI-ready string. The UI turns these into human messages (§6.11). +fn msg(e: StoreError) -> String { + e.to_string() } -fn unb64(s: &str) -> Result, String> { - fn val(c: u8) -> Option { - match c { - b'A'..=b'Z' => Some((c - b'A') as u32), - b'a'..=b'z' => Some((c - b'a' + 26) as u32), - b'0'..=b'9' => Some((c - b'0' + 52) as u32), - b'+' => Some(62), - b'/' => Some(63), - _ => None, - } - } - let clean: Vec = s.bytes().filter(|&c| c != b'=' && !c.is_ascii_whitespace()).collect(); - let mut out = Vec::with_capacity(clean.len() / 4 * 3); - for chunk in clean.chunks(4) { - let mut n = 0u32; - let mut bits = 0; - for &c in chunk { - n = (n << 6) | val(c).ok_or("invalid base64 in stored share")?; - bits += 6; - } - // Emit only the fully-decoded bytes for this chunk. - n <<= 24 - bits; - for i in 0..(bits / 8) { - out.push(((n >> (16 - i * 8)) & 0xff) as u8); - } - } - Ok(out) +/// Persist the (already-encrypted) share bytes for `id` in the OS keychain. +/// `share_b64` is base64 of ciphertext produced by the UI; this command does not encrypt or +/// decrypt, and never sees plaintext key material (boundary validation of the base64, §6.8). +#[tauri::command] +fn secure_store(id: String, share_b64: String) -> Result<(), String> { + let bytes = unb64(&share_b64)?; + KeychainShareStore::default().store(&id, &bytes).map_err(msg) } -/// Store the (already-encrypted) share bytes for `id` in the OS keychain. -/// `share_b64` is base64 of ciphertext produced by the UI; this command does not encrypt -/// or decrypt, and never sees plaintext key material. +/// Return the stored share bytes (base64) for `id`, or an explicit "not found" error. #[tauri::command] -fn keychain_set_share(id: String, share_b64: String) -> Result<(), String> { - // Validate the payload is well-formed base64 before writing (boundary check, §6.8). - let _ = unb64(&share_b64)?; - let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; - entry.set_password(&share_b64).map_err(|e| e.to_string()) +fn secure_load(id: String) -> Result { + let bytes = KeychainShareStore::default().load(&id).map_err(msg)?; + Ok(b64(&bytes)) } -/// Return the stored share bytes (base64) for `id`, or an explicit "not found" error. +/// Remove the stored share for `id` from this device's keychain (idempotent). #[tauri::command] -fn keychain_get_share(id: String) -> Result { - let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; - match entry.get_password() { - Ok(s) => Ok(s), - Err(keyring::Error::NoEntry) => Err(format!("no share stored on this device for id {id}")), - Err(e) => Err(e.to_string()), - } +fn secure_delete(id: String) -> Result<(), String> { + KeychainShareStore::default().delete(&id).map_err(msg) } -/// Remove the stored share for `id` from this device's keychain. +/// List the vault ids that have a share stored on this device (public ids only). #[tauri::command] -fn keychain_delete_share(id: String) -> Result<(), String> { - let entry = Entry::new(KEYCHAIN_SERVICE, &id).map_err(|e| e.to_string())?; - match entry.delete_credential() { - Ok(()) | Err(keyring::Error::NoEntry) => Ok(()), - Err(e) => Err(e.to_string()), - } +fn secure_list() -> Result, String> { + KeychainShareStore::default().list().map_err(msg) } // Mobile (iOS/Android) enters through this function; desktop uses the same path via main.rs. @@ -113,25 +63,11 @@ fn keychain_delete_share(id: String) -> Result<(), String> { pub fn run() { tauri::Builder::default() .invoke_handler(tauri::generate_handler![ - keychain_set_share, - keychain_get_share, - keychain_delete_share, + secure_store, + secure_load, + secure_delete, + secure_list, ]) .run(tauri::generate_context!()) .expect("error while running the Konclave Tauri application"); } - -#[cfg(test)] -mod tests { - use super::{b64, unb64}; - - // A pure round-trip check of the base64 helper. This does NOT exercise Tauri or the - // keychain (neither is available here); it only guards the byte-wrapping used at the - // boundary. It is the only thing in this scaffold that can be honestly tested offline. - #[test] - fn base64_round_trips() { - for case in [b"".as_slice(), b"a", b"ab", b"abc", b"abcd", &[0u8, 255, 1, 254, 127]] { - assert_eq!(unb64(&b64(case)).unwrap(), case, "round-trip mismatch"); - } - } -} diff --git a/src-tauri/src/share_store.rs b/src-tauri/src/share_store.rs new file mode 100644 index 0000000..8d601cd --- /dev/null +++ b/src-tauri/src/share_store.rs @@ -0,0 +1,413 @@ +// On-device share persistence backed by the OS keychain. +// +// UNVALIDATED ON HARDWARE, BUT LOGIC-TESTED. This module has NO `tauri` dependency: it needs +// only `keyring`, so its unit tests run headlessly against `keyring`'s in-memory mock (exactly +// as `orchestrator::secrets::KeychainStore` is tested). The surrounding crate that wraps these +// in `#[tauri::command]`s (lib.rs) still cannot be COMPILED here, because `tauri` needs a system +// webview (webkit2gtk/glib), absent on this machine (ADR-0004). So: the keychain logic below is +// proven by a real green test run in isolation; the Tauri command wiring is scaffold only. +// +// THREAT MODEL (mirrors ui/src/storage.ts). The bytes stored here are ALREADY ciphertext, sealed +// by the UI under a passphrase (PBKDF2 -> AES-GCM). This layer is durable at-rest storage, not +// the encryptor: the plaintext share never crosses this boundary and never leaves the device. +// The keychain adds a second, OS-account at-rest factor ON TOP of the passphrase; it does not +// replace it. Compared with the browser's IndexedDB (origin-scoped, app-clearable, evictable), +// the keychain (Windows Credential Manager / macOS + iOS Keychain / Linux Secret Service) is +// durable and survives browser resets -- the whole reason to go native (docs/TAURI-PLAN.md §2). + +use std::collections::BTreeSet; + +/// Default keychain service namespace for Konclave's per-device sealed shares. Every entry this +/// module owns on the OS account lives under this service; the account is the caller's vault id. +pub const DEFAULT_SERVICE: &str = "app.konclave.share"; + +/// Reserved account name under which the list index lives (see [`KeychainShareStore::list`]). +/// A vault id may not collide with it. The `\u{0}`-fenced name cannot be a real vault id. +const INDEX_ACCOUNT: &str = "\u{0}konclave.index\u{0}"; + +/// Explicit, human-mappable failures at this boundary (§6.8/§6.11). Never a silent loss. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum StoreError { + /// The underlying keychain backend failed (no Secret Service, locked keychain, IO, ...). + Backend(String), + /// No share is stored on this device for the given id. + NotFound(String), + /// The id is empty or collides with the reserved index account. + InvalidId(String), +} + +impl std::fmt::Display for StoreError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + StoreError::Backend(e) => write!(f, "keychain unavailable: {e}"), + StoreError::NotFound(id) => write!(f, "no share stored on this device for id {id}"), + StoreError::InvalidId(id) => write!(f, "invalid vault id {id:?}"), + } + } +} +impl std::error::Error for StoreError {} + +/// A durable at-rest store for opaque, already-encrypted share bytes, keyed by vault id. +/// +/// The domain depends only on this trait; the real implementation is OS-keychain-backed and the +/// tests drive an in-memory one. This mirrors `orchestrator::secrets::KeyStore`. +pub trait ShareStore { + /// Persist `ciphertext` for `id`, overwriting any previous value. + fn store(&self, id: &str, ciphertext: &[u8]) -> Result<(), StoreError>; + /// Return the stored ciphertext for `id`, or [`StoreError::NotFound`]. + fn load(&self, id: &str) -> Result, StoreError>; + /// Remove the stored share for `id`. Idempotent: deleting an absent id is `Ok`. + fn delete(&self, id: &str) -> Result<(), StoreError>; + /// List the ids that currently have a stored share on this device. + fn list(&self) -> Result, StoreError>; +} + +/// OS-keychain-backed [`ShareStore`]. +/// +/// `keyring` exposes no enumeration API (there is no portable "list all credentials for a +/// service" across Windows/macOS/Linux/iOS), so [`list`](Self::list) is served from an INDEX +/// entry this store maintains under a reserved account. The index holds only vault ids, which +/// are public identifiers (the group verifying key's namespace) -- never secret material -- so +/// keeping them in the clear does not weaken the threat model. +/// +/// The native backend is selected by the app enabling `keyring`'s platform feature per target; +/// with no backend (headless CI) construction still succeeds and calls error clearly, and the +/// unit tests drive it through `keyring`'s in-memory mock. +pub struct KeychainShareStore { + service: String, +} + +impl Default for KeychainShareStore { + fn default() -> Self { + Self { + service: DEFAULT_SERVICE.to_string(), + } + } +} + +impl KeychainShareStore { + /// Construct a store under a specific keychain service namespace (tests isolate with this). + pub fn new(service: impl Into) -> Self { + Self { + service: service.into(), + } + } + + fn entry(&self, account: &str) -> Result { + keyring::Entry::new(&self.service, account).map_err(|e| StoreError::Backend(e.to_string())) + } + + fn check_id(id: &str) -> Result<(), StoreError> { + if id.is_empty() || id == INDEX_ACCOUNT { + return Err(StoreError::InvalidId(id.to_string())); + } + Ok(()) + } + + /// Read the id index (a `\n`-joined set of public ids), tolerating a missing index as empty. + fn read_index(&self) -> Result, StoreError> { + let entry = self.entry(INDEX_ACCOUNT)?; + match entry.get_secret() { + Ok(bytes) => Ok(String::from_utf8_lossy(&bytes) + .lines() + .filter(|l| !l.is_empty()) + .map(str::to_string) + .collect()), + Err(keyring::Error::NoEntry) => Ok(BTreeSet::new()), + Err(e) => Err(StoreError::Backend(e.to_string())), + } + } + + fn write_index(&self, ids: &BTreeSet) -> Result<(), StoreError> { + let entry = self.entry(INDEX_ACCOUNT)?; + let joined = ids.iter().cloned().collect::>().join("\n"); + entry + .set_secret(joined.as_bytes()) + .map_err(|e| StoreError::Backend(e.to_string())) + } +} + +impl ShareStore for KeychainShareStore { + fn store(&self, id: &str, ciphertext: &[u8]) -> Result<(), StoreError> { + Self::check_id(id)?; + self.entry(id)? + .set_secret(ciphertext) + .map_err(|e| StoreError::Backend(e.to_string()))?; + let mut ids = self.read_index()?; + if ids.insert(id.to_string()) { + self.write_index(&ids)?; + } + Ok(()) + } + + fn load(&self, id: &str) -> Result, StoreError> { + Self::check_id(id)?; + match self.entry(id)?.get_secret() { + Ok(bytes) => Ok(bytes), + Err(keyring::Error::NoEntry) => Err(StoreError::NotFound(id.to_string())), + Err(e) => Err(StoreError::Backend(e.to_string())), + } + } + + fn delete(&self, id: &str) -> Result<(), StoreError> { + Self::check_id(id)?; + match self.entry(id)?.delete_credential() { + Ok(()) | Err(keyring::Error::NoEntry) => {} + Err(e) => return Err(StoreError::Backend(e.to_string())), + } + let mut ids = self.read_index()?; + if ids.remove(id) { + self.write_index(&ids)?; + } + Ok(()) + } + + fn list(&self) -> Result, StoreError> { + Ok(self.read_index()?.into_iter().collect()) + } +} + +// ---- base64 at the JS boundary ---- +// +// Tauri `invoke` marshals JS strings cleanly, so the command layer (lib.rs) hands share bytes +// across as standard padded base64 and this store keeps raw bytes in the keychain. These tiny, +// dependency-free helpers live here so they are covered by the same headless test run. + +const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/// Encode bytes as standard padded base64. +pub fn b64(bytes: &[u8]) -> String { + let mut out = String::with_capacity(bytes.len().div_ceil(3) * 4); + for chunk in bytes.chunks(3) { + let b = [ + chunk[0], + *chunk.get(1).unwrap_or(&0), + *chunk.get(2).unwrap_or(&0), + ]; + let n = ((b[0] as u32) << 16) | ((b[1] as u32) << 8) | (b[2] as u32); + out.push(B64[((n >> 18) & 63) as usize] as char); + out.push(B64[((n >> 12) & 63) as usize] as char); + out.push(if chunk.len() > 1 { + B64[((n >> 6) & 63) as usize] as char + } else { + '=' + }); + out.push(if chunk.len() > 2 { + B64[(n & 63) as usize] as char + } else { + '=' + }); + } + out +} + +/// Decode standard base64 (padding and whitespace tolerated), erroring on any stray symbol. +pub fn unb64(s: &str) -> Result, String> { + fn val(c: u8) -> Option { + match c { + b'A'..=b'Z' => Some((c - b'A') as u32), + b'a'..=b'z' => Some((c - b'a' + 26) as u32), + b'0'..=b'9' => Some((c - b'0' + 52) as u32), + b'+' => Some(62), + b'/' => Some(63), + _ => None, + } + } + let clean: Vec = s + .bytes() + .filter(|&c| c != b'=' && !c.is_ascii_whitespace()) + .collect(); + let mut out = Vec::with_capacity(clean.len() / 4 * 3); + for chunk in clean.chunks(4) { + let mut n = 0u32; + let mut bits = 0; + for &c in chunk { + n = (n << 6) | val(c).ok_or("invalid base64 in stored share")?; + bits += 6; + } + n <<= 24 - bits; + for i in 0..(bits / 8) { + out.push(((n >> (16 - i * 8)) & 0xff) as u8); + } + } + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::cell::RefCell; + use std::collections::BTreeMap; + + // ---- trait SEMANTICS, proven against an in-memory double ---- + // + // `keyring`'s in-memory mock keeps state INSIDE each `Entry` object, not in a shared backend + // (that is why `orchestrator::secrets` never round-trips a value across two `Entry::new` + // calls under the mock). So a true store-here / load-there round trip is only exercisable on + // a real OS backend, not headlessly. We therefore prove the store SEMANTICS against a + // `MemoryShareStore` (mirroring `orchestrator::secrets`'s `MemoryKeyStore`), and prove the + // `KeychainShareStore` error MAPPING + id guard against the mock below. + + #[derive(Default)] + struct MemoryShareStore { + map: RefCell>>, + } + impl ShareStore for MemoryShareStore { + fn store(&self, id: &str, ciphertext: &[u8]) -> Result<(), StoreError> { + KeychainShareStore::check_id(id)?; + self.map + .borrow_mut() + .insert(id.to_string(), ciphertext.to_vec()); + Ok(()) + } + fn load(&self, id: &str) -> Result, StoreError> { + KeychainShareStore::check_id(id)?; + self.map + .borrow() + .get(id) + .cloned() + .ok_or_else(|| StoreError::NotFound(id.to_string())) + } + fn delete(&self, id: &str) -> Result<(), StoreError> { + KeychainShareStore::check_id(id)?; + self.map.borrow_mut().remove(id); + Ok(()) + } + fn list(&self) -> Result, StoreError> { + Ok(self.map.borrow().keys().cloned().collect()) + } + } + + #[test] + fn store_then_load_returns_exact_bytes() { + let s = MemoryShareStore::default(); + let cipher = &[0u8, 255, 1, 254, 127, 42]; + s.store("vault-a", cipher).unwrap(); + assert_eq!(s.load("vault-a").unwrap(), cipher); + } + + #[test] + fn load_missing_is_an_explicit_not_found() { + let s = MemoryShareStore::default(); + assert_eq!(s.load("nope"), Err(StoreError::NotFound("nope".into()))); + } + + #[test] + fn store_overwrites_in_place() { + let s = MemoryShareStore::default(); + s.store("v", b"first").unwrap(); + s.store("v", b"second").unwrap(); + assert_eq!(s.load("v").unwrap(), b"second"); + assert_eq!(s.list().unwrap(), vec!["v".to_string()]); + } + + #[test] + fn delete_removes_and_is_idempotent() { + let s = MemoryShareStore::default(); + s.store("v", b"x").unwrap(); + s.delete("v").unwrap(); + assert_eq!(s.load("v"), Err(StoreError::NotFound("v".into()))); + // Deleting an absent id is not an error (§: explicit, non-surprising boundary). + s.delete("v").unwrap(); + assert!(s.list().unwrap().is_empty()); + } + + #[test] + fn list_reflects_the_stored_set() { + let s = MemoryShareStore::default(); + s.store("alice", b"a").unwrap(); + s.store("bob", b"b").unwrap(); + s.store("carol", b"c").unwrap(); + s.delete("bob").unwrap(); + let mut got = s.list().unwrap(); + got.sort(); + assert_eq!(got, vec!["alice".to_string(), "carol".to_string()]); + } + + #[test] + fn empty_and_reserved_ids_are_rejected() { + let s = MemoryShareStore::default(); + assert!(matches!(s.store("", b"x"), Err(StoreError::InvalidId(_)))); + assert!(matches!( + s.store(INDEX_ACCOUNT, b"x"), + Err(StoreError::InvalidId(_)) + )); + assert!(matches!(s.load(""), Err(StoreError::InvalidId(_)))); + } + + // ---- KeychainShareStore error MAPPING + guard, against the keyring mock ---- + + /// Route `keyring` through its in-memory mock (set once per process), exactly as + /// `orchestrator::secrets` does, so the keychain path is reachable with no OS Secret Service. + fn use_mock_keychain() { + use std::sync::Once; + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + keyring::set_default_credential_builder(keyring::mock::default_credential_builder()); + }); + } + + #[test] + fn keychain_load_absent_maps_no_entry_to_not_found() { + use_mock_keychain(); + // A fresh keychain entry reports NoEntry; the store must surface it as a clean NotFound, + // never a panic or an opaque backend string. + let s = KeychainShareStore::new("test.konclave.share.absent"); + assert_eq!(s.load("ghost"), Err(StoreError::NotFound("ghost".into()))); + } + + #[test] + fn keychain_list_is_empty_without_an_index() { + use_mock_keychain(); + // No index entry yet => an empty list, not a backend error (read_index tolerates NoEntry). + let s = KeychainShareStore::new("test.konclave.share.emptylist"); + assert!(s.list().unwrap().is_empty()); + } + + #[test] + fn keychain_rejects_invalid_ids_before_touching_the_backend() { + use_mock_keychain(); + let s = KeychainShareStore::new("test.konclave.share.guard"); + assert!(matches!(s.store("", b"x"), Err(StoreError::InvalidId(_)))); + assert!(matches!( + s.store(INDEX_ACCOUNT, b"x"), + Err(StoreError::InvalidId(_)) + )); + assert!(matches!(s.delete(""), Err(StoreError::InvalidId(_)))); + } + + #[test] + fn keychain_backend_contract_secret_roundtrips_within_one_entry() { + use_mock_keychain(); + // Documents the backend contract KeychainShareStore relies on: a single Entry stores and + // returns exactly the bytes set, and signals NoEntry when absent. (Cross-`Entry::new` + // persistence needs a REAL OS backend; the mock is per-Entry, hence the MemoryShareStore + // tests above for full round-trip semantics.) + let entry = keyring::Entry::new("test.konclave.share.contract", "acct").unwrap(); + assert!(matches!(entry.get_secret(), Err(keyring::Error::NoEntry))); + let cipher = &[9u8, 8, 7, 6, 0, 255]; + entry.set_secret(cipher).unwrap(); + assert_eq!(entry.get_secret().unwrap().as_slice(), cipher); + } + + // ---- base64 boundary helpers ---- + + #[test] + fn base64_round_trips() { + for case in [ + b"".as_slice(), + b"a", + b"ab", + b"abc", + b"abcd", + &[0u8, 255, 1, 254, 127], + ] { + assert_eq!(unb64(&b64(case)).unwrap(), case, "round-trip mismatch"); + } + } + + #[test] + fn base64_rejects_stray_symbols() { + assert!(unb64("not*base64").is_err()); + } +} From 12ef4383d427638f77dbcbcc7deadb989b827761 Mon Sep 17 00:00:00 2001 From: Daniel Gorgonha Date: Sun, 2 Aug 2026 20:27:58 -0300 Subject: [PATCH 3/6] fix(tauri): beforeDev/BuildCommand run from project root, so 'ui' not '../ui' Tauri runs beforeDevCommand/beforeBuildCommand from the app root (konclave, the parent of src-tauri), so '../ui' resolved one level too high (to the user's home) and npm could not find ui/package.json on a fresh clone. Point them at 'ui' (relative to the project root); frontendDist stays '../ui/dist' (relative to tauri.conf.json in src-tauri). Also merges main so the desktop shell wraps the CURRENT app (the whole signing convergence), not the Jul-27 snapshot. --- src-tauri/tauri.conf.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6956573..7b1b949 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -6,8 +6,8 @@ "build": { "frontendDist": "../ui/dist", "devUrl": "http://localhost:5173", - "beforeDevCommand": "npm --prefix ../ui run dev", - "beforeBuildCommand": "npm --prefix ../ui run build" + "beforeDevCommand": "npm --prefix ui run dev", + "beforeBuildCommand": "npm --prefix ui run build" }, "app": { "windows": [ From 44475fcc660719e5d2c8cf45f98752cf07fe6705 Mon Sep 17 00:00:00 2001 From: Daniel Gorgonha Date: Sun, 2 Aug 2026 20:48:36 -0300 Subject: [PATCH 4/6] feat(tauri): version the app icons (stop gitignoring src-tauri/icons) The scaffold gitignored /icons as host-generated, but tauri-build requires icon.ico to exist to build on Windows, and a fresh clone had none. Standard Tauri templates commit the icon set; version the 5 small derived icons (from ui/public/icon-512.png) so any clone builds with no extra step. Mobile /gen stays ignored (truly host-generated). --- src-tauri/.gitignore | 3 --- src-tauri/icons/128x128.png | Bin 0 -> 4282 bytes src-tauri/icons/128x128@2x.png | Bin 0 -> 9834 bytes src-tauri/icons/32x32.png | Bin 0 -> 704 bytes src-tauri/icons/icon.icns | Bin 0 -> 117931 bytes src-tauri/icons/icon.ico | Bin 0 -> 21245 bytes src-tauri/icons/icon.png | Bin 0 -> 13663 bytes 7 files changed, 3 deletions(-) create mode 100644 src-tauri/icons/128x128.png create mode 100644 src-tauri/icons/128x128@2x.png create mode 100644 src-tauri/icons/32x32.png create mode 100644 src-tauri/icons/icon.icns create mode 100644 src-tauri/icons/icon.ico create mode 100644 src-tauri/icons/icon.png diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore index d88a890..b4cd035 100644 --- a/src-tauri/.gitignore +++ b/src-tauri/.gitignore @@ -4,6 +4,3 @@ # Tauri-generated code (schemas, mobile projects) is produced by `tauri android/ios init` # and the build script; regenerate it per host rather than committing it. /gen - -# Generated app icons (produce with `npx tauri icon`); binary, host-generated, not source. -/icons diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..78b7591c29c595d3b8c502fcb00baef4aa66a8e3 GIT binary patch literal 4282 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 Wy%E@ADhuhK1i;wP+yJfT9QS|y7A$}O literal 0 HcmV?d00001 diff --git a/src-tauri/icons/128x128@2x.png b/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0024f57cb27996e777a7f56bc3ed2b1d59c4dcde GIT binary patch literal 9834 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..b4e3595e87f8b8996318d35edf02a3ecc1115d58 GIT binary patch literal 704 zcmV;x0zdtUP)1uS*E^&4Qe>3k=u#T;2U2H@em1FSmSoYQO`A3?3MmqT2!%2TS_DxM(WYI? z2rTn27ZEM;2eL`UO$&7#oW!5GgX6i^(;^iYrDF|>Fz@>1JI~?!-tQc-H_NNoM!B~0 z@DxA_VCMrUrBLesQvA;VN+}G(#4yZN2*+`@4nRtYKTwW8P>z%mAq3vsysZVWZ5!LR ziN!1qm6TCYS&c2_rs||6YP6-q<#K@#0311b9Dm?23W!=!3W|zpf7gmy_NM82asV91 zAvZUVimDnq+TZds5~1OGBN-V6pFj2haJsG@+qQ{BrpWQ;QeAV34_)nykB^~g+Pd{c zB9D;}f|4>nKHotM(`3nUc>e4$`-@HjFg5s|%U2rEG>w>LF)}>FK!0yS3E2=pDTS1h z#dw^o>}+l~w{WNFChqJ)j@@bl;M4tDWH?B3%LDG+ZDJ}M#_jQ->pEh&Wq;GUEm(x5bs-WSLLmgv z`FRY}#4vXe4UZCh)d)Z|Jc?oN#xPBy(Rub{W+uF#)&&qk5Q|y#bax`9BrQ#2G8E#> z+4H=7@dU$L48THPD;F+aVlotBcqquX!LR7Lo=^)mHG~C)#k9V8&DCo+h=eBy8KnT^ z#QNEn?d9pCht$=d<9l$BfxaXS;o2B>b+j=tK87bfofDPS_Fy*RU%>0l zqpG?#xiN&_%SE_cXqvX1j73&1CUZ mt#6w4XKp0bhj{cckbc$ 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

-iB=616EJ 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

w^7vF*RX9W<`O##Jky*L#Qf# z{`?NGaZZ&dV}dI{qxG`VhjmwZ*8}tkEOLM zD02AY&%m0ias}Fph{u1tN*I-6=i(WH3KW4p1VPIQ3_S0a=D@lhw4#5;6}%0Gn{bi> z3qxA(4lV|t^fV2P*UIvc$>Y6SP>vPo3w-$8)7x;&YNF_ssKZ*HEdpLlPR0VE4aFC< zRpB~6lw^tL8ZzN`>ApS7fM1gZAi~KDTgyw_KQiG0ZotF5&6ICIOmcBA18Nd)O%P|; z2s692CGzlJ5d1I_VTi}nv-%zg3-Aj#Tk$O|Df7e>lg@~rOVLk0tt>NKoHFxsx2v}` zhQ2Q)IkmsjG;?lnsk`F+`~1+p`|{^kSOcW%OM`7A9HA*nfX+)Ksj{&SX@R%rjwbCk;=t1a z!;YxY5DIX#nv4A-YNr1|Tf1)b^-2KGLBJ6GD~_m$%t!(vUiP`O&SZw(lIYwM0s@(LS6A{@^Z+Sg@UkWdhnjaa6FB$)c zDFXXu4iH?=tAI}FT|Iub!1UOO%>70Xg~gLAY(NI>Ll&xMP1>6bgxqJM27nkIE&vSX zfE+Q$`J3xWlnXG%$9K%wvPYzhMrJaR6eQ}bWfr0$1`REpi5i>(Vz?upjBkMetUh<; zl=Brp-42qW6@%?dpPesg_4qojS+(>jPMa_Po{ℜ0PYTuPiED=7Hy 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 HXI=acaCEty literal 0 HcmV?d00001 diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..89184337c076316cbe889700038ae96f032b0c29 GIT binary patch literal 21245 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 literal 0 HcmV?d00001 diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d06534984841a6bd695ee54d1f37e831003aa0bc GIT binary patch literal 13663 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

C0pfOf68?k?y=e)>#55=2CTT94H zE^KH}n3}|B8g8OlIUyU_8TTgJjf{B`u~}yCIu)BwXo;_gr4FA$p%`JBoGb%TZOXu({Cy z{Gq(~87i)$t;GSHYDS6>K*e!XXELdhj#;3 Date: Sun, 2 Aug 2026 21:09:54 -0300 Subject: [PATCH 5/6] chore(tauri): silence dead-code warning on KeychainShareStore::new (test/future use) --- src-tauri/src/share_store.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src-tauri/src/share_store.rs b/src-tauri/src/share_store.rs index 8d601cd..dd521ef 100644 --- a/src-tauri/src/share_store.rs +++ b/src-tauri/src/share_store.rs @@ -87,6 +87,7 @@ impl Default for KeychainShareStore { impl KeychainShareStore { /// Construct a store under a specific keychain service namespace (tests isolate with this). + #[allow(dead_code)] // used by tests + the future keychain wiring; not on the default build path yet pub fn new(service: impl Into) -> Self { Self { service: service.into(), From 2530c0b1b4145216504fc5032c295c8b4df4516b Mon Sep 17 00:00:00 2001 From: Daniel Gorgonha Date: Sun, 2 Aug 2026 21:15:38 -0300 Subject: [PATCH 6/6] feat(tauri): desktop opens on the vaults, not the marketing landing A desktop app already has its user (they installed it), so opening on the marketing Intro is wrong. Detect the Tauri shell (isDesktop) and redirect '/' -> '/vaults'; the web keeps the landing at '/'. The vault list is the right home: enter a vault or create one. --- ui/src/App.tsx | 7 +++++-- ui/src/platform.ts | 6 ++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 ui/src/platform.ts diff --git a/ui/src/App.tsx b/ui/src/App.tsx index b58712d..42a8494 100644 --- a/ui/src/App.tsx +++ b/ui/src/App.tsx @@ -1,4 +1,5 @@ -import { Routes, Route } from 'react-router-dom' +import { Routes, Route, Navigate } from 'react-router-dom' +import { isDesktop } from './platform' import { useReveal } from './reveal' import { DemoBanner } from './components' import Layout from './Layout' @@ -34,7 +35,9 @@ export default function App() { {/* Onboarding — standalone, no rail */} - } /> + {/* The desktop app opens straight on the product (the vaults), not the marketing landing; + the web keeps the landing at `/`. */} + : } /> } /> } /> } /> diff --git a/ui/src/platform.ts b/ui/src/platform.ts new file mode 100644 index 0000000..173ac40 --- /dev/null +++ b/ui/src/platform.ts @@ -0,0 +1,6 @@ +// Detect the Tauri desktop shell. Tauri v2 always injects `__TAURI_INTERNALS__` into the webview +// (and `__TAURI__` when `withGlobalTauri` is on); on the plain web both are absent. Used to open +// the desktop app straight on the product (the vaults), not the marketing landing. +export const isDesktop = + typeof window !== 'undefined' && + ('__TAURI_INTERNALS__' in window || '__TAURI__' in window)