Skip to content

feat: Phase 9 — Tauri desktop client with FUSE mount#63

Merged
FSM1 merged 48 commits into
mainfrom
feat/phase-9-desktop-client
Feb 8, 2026
Merged

feat: Phase 9 — Tauri desktop client with FUSE mount#63
FSM1 merged 48 commits into
mainfrom
feat/phase-9-desktop-client

Conversation

@FSM1

@FSM1 FSM1 commented Feb 7, 2026

Copy link
Copy Markdown
Owner

Summary

  • Tauri-based macOS desktop app providing a native FUSE mount at ~/CipherVault
  • System browser redirect for Web3Auth login, deep link (cipherbox://) callback
  • Menu bar utility (no Dock icon) with background sync and offline write queue
  • Memory-only file content cache; folder metadata cached with 30s TTL
  • Temp-file commit write model: buffer locally, encrypt+upload on close/flush

Scope

  • In scope: macOS only, read-write FUSE, offline queueing, menu bar tray
  • Out of scope: Linux/Windows (v1.1), disk cache, pre-fetching, conflict detection, auto-update

Test plan

  • Tauri app builds and launches on macOS
  • Web3Auth login via system browser completes successfully
  • FUSE mount appears at ~/CipherVault after login
  • Read files from mounted vault matches web UI content
  • Write/create/delete files round-trips through IPFS
  • Offline writes queue and sync when connectivity returns
  • Menu bar icon reflects mount status (synced/syncing/error/offline)
  • App auto-unmounts on quit

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Desktop app (Tauri) with system tray, Keychain-backed token storage, Web3Auth, bundled frontend entry, desktop API client, and silent refresh
    • Mountable encrypted vault via FUSE with inode management, metadata/content caches, temp-file buffering, offline write queue, background sync, mount/unmount helpers
    • Desktop auth flows: desktop clients can send/receive refresh tokens in request/response bodies
  • Bug Fixes

    • More robust IPNS resolution with safer fallbacks and improved error handling
  • Tests

    • Extensive cross-language crypto, IPNS, FUSE, and queue unit/integration tests
  • Documentation

    • Added desktop design, FUSE, macOS integration, testing strategy, and implementation notes

Phase 09: Desktop Client
- Implementation decisions documented
- Phase boundary established

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 7, 2026

Copy link
Copy Markdown

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

Adds a Tauri desktop client (TypeScript + Rust) with desktop-specific auth branching (body refreshToken), macOS Keychain token storage, a Rust crypto library (AES/ECIES/Ed25519/IPNS), an inline IPNS parser, a FUSE-backed encrypted vault and background sync/tray, tokenPrefix DB optimization and migration, OpenAPI/model updates, and many tests/configs.

Changes

Cohort / File(s) Summary
API auth & IPNS
apps/api/src/auth/auth.controller.ts, apps/api/src/auth/auth.controller.spec.ts, apps/api/src/auth/dto/*, apps/api/src/ipns/ipns-record-parser.ts, apps/api/src/ipns/ipns.service.ts, apps/api/src/ipns/ipns.service.spec.ts, apps/api/test/__mocks__/ipns.ts, apps/api/jest.config.js, apps/api/package.json
Desktop-vs-web branching added for login/refresh/logout (desktop uses body tokens, web uses http-only cookie); new DesktopRefreshDto and optional refreshToken in response DTOs; replaced external ipns dependency with inline parser parseIpnsRecord, adjusted IpnsService fallback/error semantics and tests; removed ipns Jest mock.
Token persistence optimization & migration
apps/api/src/auth/entities/refresh-token.entity.ts, apps/api/src/auth/services/token.service.ts, apps/api/src/auth/auth.service.ts, apps/api/src/migrations/1738972800000-AddTokenPrefix.ts
Added tokenPrefix column, persisted prefix on token creation, and updated rotation lookup to filter by prefix for efficient refresh token rotation; includes DB migration to add column and index.
OpenAPI & web client models
packages/api-client/openapi.json, apps/web/src/api/models/*, apps/web/src/api/auth/auth.ts
OpenAPI and generated models include DesktopRefreshDto and optional refreshToken; web client refresh mutation updated to send desktop DTO in request body.
Desktop frontend bootstrap & config
apps/desktop/index.html, apps/desktop/package.json, apps/desktop/tsconfig.json, apps/desktop/vite.config.ts, apps/desktop/src/polyfills.ts
New webview entry, package manifest, TypeScript/Vite config, and Node polyfills to bootstrap Tauri frontend.
Tauri build & packaging
apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/build.rs, apps/desktop/src-tauri/.cargo/config.toml, apps/desktop/src-tauri/.gitignore, apps/desktop/src-tauri/pkg-config/fuse.pc, apps/desktop/src-tauri/tauri.conf.json
Added Cargo manifest, build script, linker/env flags, pkg-config shim, gitignore and Tauri configuration for desktop packaging.
Desktop API & client (Rust)
apps/desktop/src-tauri/src/api/*, apps/desktop/src-tauri/src/api/mod.rs, apps/desktop/src-tauri/src/api/types.rs
New ApiClient that injects X-Client-Type: desktop, Keychain-backed token helpers, and IPFS/IPNS wrapper functions and DTO types for desktop.
Rust crypto & tests
apps/desktop/src-tauri/src/crypto/*, apps/desktop/src-tauri/src/crypto/tests.rs, apps/desktop/src-tauri/src/crypto/mod.rs, apps/desktop/src-tauri/src/crypto/utils.rs
Added AES-GCM, ECIES-like key wrap/unwrap, Ed25519 ops, folder metadata encryption, IPNS record creation/marshalling, utilities, and extensive deterministic tests.
FUSE filesystem & support
apps/desktop/src-tauri/src/fuse/*
New FUSE subsystem: metadata/content caches, inode table, open-file buffering, FUSE operations, mount/unmount flow, background publish/prefetch/upload, metadata encryption/publish helpers, and public filesystem API.
Desktop runtime, sync & tray
apps/desktop/src-tauri/src/commands.rs, apps/desktop/src-tauri/src/state.rs, apps/desktop/src-tauri/src/sync/*, apps/desktop/src-tauri/src/tray/*, apps/desktop/src-tauri/src/main.rs
Tauri IPC command handlers, AppState with secure in-memory keys, SyncDaemon and offline write queue, tray/menu integration, and app bootstrap wiring.
Frontend auth & UI
apps/desktop/src/auth.ts, apps/desktop/src/main.ts
Web3Auth integration in the webview; login/logout flows that send idToken/privateKey to Rust IPC and manage webview lifecycle.
Queues, tests & tooling
apps/desktop/src-tauri/src/sync/queue.rs, apps/desktop/src-tauri/src/sync/tests.rs, apps/desktop/src-tauri/generate-test-vectors.mjs, apps/api/src/auth/auth.controller.spec.ts
Offline write queue and tests, deterministic crypto test-vector generator, and expanded API auth controller tests covering desktop scenarios and header/body token branching.
Docs & repo config
.coderabbit.yaml, .learnings/**, .planning/**
Added review path guidance, learnings (FUSE, macOS integration, Tauri lifecycles, IPNS parser rationale), planning and security review documents.

Sequence Diagram(s)

sequenceDiagram
    participant WebView as Desktop Webview
    participant Tauri as Tauri Runtime
    participant RustBackend as Rust IPC Handler
    participant Keychain as macOS Keychain
    participant API as CipherBox API
    participant IPFS as IPFS/IPNS

    WebView->>Tauri: handle_auth_complete(idToken, privateKey)
    Tauri->>RustBackend: invoke handle_auth_complete
    RustBackend->>API: POST /auth/login (idToken, publicKey) [X-Client-Type: desktop]
    API-->>RustBackend: accessToken + refreshToken (in body)
    RustBackend->>Keychain: store_refresh_token(user_id, refreshToken)
    RustBackend->>API: GET /vault (Authorization: Bearer accessToken)
    API-->>RustBackend: encrypted vault metadata (CID)
    RustBackend->>IPFS: GET /ipfs/{cid}
    IPFS-->>RustBackend: encrypted metadata bytes
    RustBackend->>RustBackend: decrypt metadata, populate inodes, mount FUSE
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes


Note

🎁 Summarized by CodeRabbit Free

Your organization is on the Free plan. CodeRabbit will generate a high-level summary and a walkthrough for each pull request. For a comprehensive line-by-line review, please upgrade your subscription to CodeRabbit Pro by visiting https://app.coderabbit.ai/login.

Comment @coderabbitai help to get the list of available commands and usage tips.

FSM1 and others added 19 commits February 7, 2026 22:15
Phase 9: Desktop Client
- Standard stack identified (Tauri v2, fuser, FUSE-T, keyring)
- Architecture patterns documented (Rust FUSE + crypto bridge)
- Pitfalls catalogued (FUSE-T limitations, cookie auth, deep link dev)
- Open questions flagged (fuser+FUSE-T compat, eciesjs format)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 09: Desktop Client
- 6 plans in 4 waves
- Wave 1: scaffold + crypto (09-01) || API auth changes (09-02) [parallel]
- Wave 2: desktop auth flow (09-03)
- Wave 3: FUSE read ops (09-04), FUSE write ops (09-05) [sequential]
- Wave 4: system tray + sync daemon (09-06) with human verification checkpoint
- Ready for execution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Split 09-01 into scaffold (01) + crypto (new 02), renumber 02-06 to 03-07
- Add IPNS record creation to crypto plan with cross-language test vectors
- Clarify Web3Auth runs in webview (not system browser) for secure key transfer
- Add explicit read-only note for open() in FUSE read plan
- Split FUSE write Task 2 into file mutations + directory mutations
- Add WriteQueue unit tests and offline verification to final plan
- Fix wave numbers: FUSE write=4, tray/sync=5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Address 4 remaining issues from plan verification:
- Add ipnsPrivateKeyEncrypted to FolderEntry and encryptionMode to FileEntry with camelCase Serde (plans 02, 05, 06)
- Add encryptedRootIpnsPrivateKey/rootIpnsPublicKey to VaultResponse and AppState (plan 04)
- Fix wave numbering cascade: 04→w3, 05→w4, 06→w5, 07→w6
- Update CONTEXT.md to reflect webview-based Web3Auth decision

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cross-language data model fidelity when porting TypeScript types to
Rust — encrypted key fields are the most dangerous to miss since reads
work fine but every write path breaks silently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Detect X-Client-Type: desktop header in login, refresh, logout
- Desktop login returns refreshToken in response body (no cookie)
- Desktop refresh reads refreshToken from body instead of cookie
- Desktop logout skips cookie clearing
- Add DesktopRefreshDto for body-based refresh token
- Add optional refreshToken to LoginResponseDto and TokenResponseDto
- Update existing tests for new method signatures
- Regenerate OpenAPI spec and API client

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Login with desktop header: verify refreshToken in body, no cookie
- Refresh with desktop header: verify body-based token, no cookie
- Refresh with desktop header + no token: verify UnauthorizedException
- Logout with desktop header: verify clearCookie not called
- Logout with desktop header: verify authService.logout still called
- All existing web flow tests continue to pass as regression coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create apps/desktop as pnpm workspace member (@cipherbox/desktop)
- Add Cargo.toml with all Rust dependencies (tauri, keyring, reqwest, aes-gcm, ecies, ed25519-dalek, tokio, etc.)
- Configure tauri.conf.json: no default window, deep-link scheme "cipherbox", ActivationPolicy::Accessory
- Register plugins: deep-link, autostart (LaunchAgent), shell, notification
- Make fuser optional (requires FUSE-T installed) -- enabled via "fuse" cargo feature in plan 09-03
- Add placeholder icons, build.rs, capabilities, minimal index.html + main.ts webview shell
- cargo check passes, package visible in pnpm workspace

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Add desktop client support to auth endpoints
- Update auth controller tests for desktop client flow

SUMMARY: .planning/phases/09-desktop-client/09-03-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 1/1
- Scaffold Tauri v2 app in monorepo

SUMMARY: .planning/phases/09-desktop-client/09-01-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…etadata

- AES-256-GCM encrypt/decrypt/seal/unseal matching TypeScript sealed format
- ECIES wrap/unwrap using ecies crate (cross-compatible with eciesjs)
- Ed25519 keygen/sign/verify with deterministic signatures via ed25519-dalek
- FolderMetadata/FolderEntry/FileEntry with serde camelCase renaming
- FolderEntry includes ipnsPrivateKeyEncrypted, FileEntry includes encryptionMode
- IPNS module stubbed for Task 2 implementation
- Utils: random generation, hex encoding, zeroize

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- IPNS record creation with CBOR data, V1+V2 Ed25519 signatures
- IPNS record marshaling to protobuf (IpnsEntry with all 9 fields)
- IPNS name derivation: CIDv1 base36 (k51...) from Ed25519 public key
- 51 tests passing including cross-language vectors from TypeScript:
  - AES-256-GCM: fixed key/IV produces byte-identical ciphertext
  - Ed25519: deterministic signature matches TypeScript output
  - ECIES: Rust unwraps TypeScript-wrapped data successfully
  - IPNS name: identical k51... string as TypeScript deriveIpnsName
- Test vector generation script (generate-test-vectors.mjs) committed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Task 1: AES-256-GCM, ECIES, Ed25519, folder metadata structs
- Task 2: IPNS record creation and 51 cross-language test vectors

SUMMARY: .planning/phases/09-desktop-client/09-02-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add ApiClient with X-Client-Type: desktop header on all requests
- Add Keychain storage for refresh tokens via keyring crate
- Add request/response types matching backend API (camelCase serde)
- Add AppState with memory-only sensitive key fields including root IPNS keypair
- Register AppState with Tauri in main.rs setup

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d Web3Auth

- Add commands.rs with handle_auth_complete, try_silent_refresh, logout IPC commands
- Register commands in main.rs invoke_handler
- Add fetch_and_decrypt_vault helper for ECIES vault key decryption including root IPNS keypair
- Add JWT sub claim extraction and secp256k1 public key derivation utilities
- Create auth.ts webview module with Web3Auth SDK initialization and login/logout
- Update main.ts entry point with silent refresh attempt and login UI flow
- Add @web3auth/modal dependency to desktop package.json

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- Task 1: Implement API client, Keychain auth, and app state
- Task 2: Implement Tauri commands for auth flow with webview-based Web3Auth

SUMMARY: .planning/phases/09-desktop-client/09-04-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…layer

- Add api/ipfs.rs: fetch_content and upload_content via backend API
- Add api/ipns.rs: resolve_ipns with IpnsResolveResponse struct
- Add fuse/inode.rs: InodeTable, InodeData, InodeKind (Root/Folder/File)
  - Root and Folder variants store decrypted ipns_private_key
  - File variant includes encryption_mode field
  - populate_folder decrypts both folder_key and ipns_private_key for subfolders
- Add fuse/cache.rs: MetadataCache (30s TTL) and ContentCache (256 MiB LRU)
- Add fuse/mod.rs: CipherVaultFS struct, mount_filesystem, unmount_filesystem
- Add multipart POST support to ApiClient for file uploads
- 64 tests passing (8 new cache tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Implement fuser::Filesystem trait for CipherVaultFS (feature-gated):
  - init: populate root folder from IPNS on mount
  - lookup: lazy folder loading when children not yet populated
  - getattr: return cached FileAttr from inode table
  - readdir: ALL entries in single pass (FUSE-T requirement)
  - open: read-only, returns EACCES for write flags (deferred to 09-06)
  - read: fetch encrypted from IPFS -> ECIES unwrap key -> AES decrypt -> cache
  - release: remove file handle (dirty handling deferred to 09-06)
  - statfs: report 500 MiB quota with used blocks from known file sizes
  - access: check inode existence
- Wire mount_filesystem after auth completion in handle_auth_complete
- Wire unmount_filesystem before key clearing in logout
- Async pattern: tokio runtime for IPFS/IPNS fetches, never block FUSE thread
- Background metadata refresh on stale readdir (fire-and-forget)
- File key zeroed from memory after decryption

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 2/2
- IPFS/IPNS API calls, inode table, and caching layer
- FUSE read operations (Filesystem trait) and mount/unmount wiring

SUMMARY: .planning/phases/09-desktop-client/09-05-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov

codecov Bot commented Feb 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.71429% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.68%. Comparing base (b4e6f3f) to head (e19e227).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
apps/api/src/auth/auth.controller.ts 86.66% 0 Missing and 2 partials ⚠️
apps/api/src/ipns/ipns.service.ts 92.30% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main      #63      +/-   ##
==========================================
+ Coverage   89.57%   89.68%   +0.10%     
==========================================
  Files          42       43       +1     
  Lines        1429     1483      +54     
  Branches      268      284      +16     
==========================================
+ Hits         1280     1330      +50     
- Misses         76       77       +1     
- Partials       73       76       +3     
Flag Coverage Δ
api 89.68% <95.71%> (+0.10%) ⬆️
crypto 89.68% <95.71%> (+0.10%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
apps/api/src/auth/auth.service.ts 93.33% <100.00%> (+0.09%) ⬆️
apps/api/src/auth/services/token.service.ts 97.43% <100.00%> (+0.13%) ⬆️
apps/api/src/ipns/ipns-record-parser.ts 100.00% <100.00%> (ø)
apps/api/src/ipns/ipns.service.ts 95.55% <92.30%> (-1.49%) ⬇️
apps/api/src/auth/auth.controller.ts 74.07% <86.66%> (+2.64%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

FSM1 and others added 8 commits February 8, 2026 02:36
- Add OpenFileHandle with temp file write buffering (file_handle.rs)
- Add IPFS upload_content and unpin_content API functions
- Add IPNS publish_ipns with IpnsPublishRequest matching backend DTO
- Add update_folder_metadata helper: rebuilds metadata, encrypts,
  uploads to IPFS, creates/signs IPNS record, publishes via API
- Add temp_dir, public_key, tee_public_key fields to CipherVaultFS
- Update mount_filesystem signature with new parameters
- Update commands.rs to pass public_key and TEE keys to FUSE mount
- Clean up temp directory on unmount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- create(): allocate inode, create temp file, add to parent children
- write(): buffer to temp file at offset, mark dirty, update inode size
- open(): handle O_WRONLY/O_RDWR with temp file pre-populated from IPFS
- release(): if dirty, encrypt content, wrap key (ECIES), upload to
  IPFS, update inode metadata, publish folder metadata via IPNS
- unlink(): remove inode, fire-and-forget unpin, update parent metadata
- setattr(): handle truncate via size parameter (FUSE-T compatible)
- flush(): no-op (upload happens on release, not flush)
- read(): handle both temp-file reads and IPFS-cached reads

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- mkdir(): generate Ed25519 IPNS keypair, derive IPNS name, create empty
  folder metadata, encrypt and upload to IPFS, create+sign+publish IPNS
  record, enroll TEE for republishing, store decrypted ipns_private_key
  in new folder's inode, update parent metadata
- rmdir(): verify ENOTEMPTY, remove inode, update parent metadata,
  fire-and-forget unpin of folder's IPNS CID
- rename(): handle same-folder rename and cross-folder move, replace
  existing destination if present, update both parents' metadata for
  cross-folder moves using per-folder IPNS private keys
- Make InodeTable.name_to_ino public for rename index manipulation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks completed: 3/3
- Temp-file write model and IPNS publish helpers
- FUSE file mutation operations (create, write, release, unlink, setattr, flush)
- FUSE directory mutation operations (mkdir, rmdir, rename)

SUMMARY: .planning/phases/09-desktop-client/09-06-SUMMARY.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ne write queue

- Add tray/status.rs: TrayStatus state machine (NotConnected, Mounting, Syncing, Synced, Offline, Error)
- Add tray/mod.rs: Menu bar icon with status, Open CipherVault, Sync Now, Login, Logout, Quit items
- Add sync/mod.rs: SyncDaemon with 30s IPNS polling, sequence number change detection, offline handling
- Add sync/queue.rs: WriteQueue with FIFO processing, retry logic, UploadHandler trait for testability
- Add sync/tests.rs: 7 unit tests covering enqueue, process, FIFO order, retry, max-retries drop
- Update main.rs: Add mod tray/sync, build tray in setup, register start_sync_daemon command
- Update commands.rs: Add start_sync_daemon IPC command, tray status updates in auth/mount lifecycle
- Update state.rs: Add sync_trigger channel for tray menu Sync Now button

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
beforeDevCommand was empty so Tauri waited for a frontend server that
never started. Added vite.config.ts, vite/typescript dev deps, and
wired up beforeDevCommand/beforeBuildCommand in tauri.conf.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The app starts headless (no windows) so clicking Login in the tray menu
needs to create a WebviewWindow, not just show a non-existent "main"
window. Creates a 480x600 centered window loading index.html.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Web3Auth SDK depends on Node.js globals (Buffer, process) that don't
exist in browser/webview environments. Added polyfills.ts (same as web
app), Vite resolve aliases, and buffer/process dependencies.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
FSM1 and others added 9 commits February 8, 2026 03:19
Web3Auth v10 renamed initModal() to init() and changed to static
imports instead of dynamic. Updated auth.ts to use proper v10 API
with typed Web3AuthOptions, WEB3AUTH_NETWORK enum, and
WALLET_CONNECTORS constants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tauri v2 blocks window.open() by default, which prevents Web3Auth
Google OAuth popups. Added on_new_window handler returning Allow to
permit authentication popup windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
NewWindowResponse::Allow doesn't maintain window.opener on macOS,
breaking Web3Auth's postMessage callback after Google OAuth completes.
Using NewWindowResponse::Create with window_features() passes the
opener's WKWebViewConfiguration, preserving the window.opener reference.

Also added Cross-Origin-Opener-Policy header to Vite dev server.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Web3Auth v10 removed authenticateUser() and replaced it with
getIdentityToken() which returns { idToken: string }. Also fixed
provider.request type parameters to match v10 generics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Web3Auth may auto-connect from cached session on init, but the Rust
side has no keys in memory on cold start. Now disconnects/clears cache
if already connected after init. Added console logging throughout the
login flow to help diagnose issues.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The backend login endpoint requires publicKey (hex-encoded secp256k1
uncompressed public key) and loginType ("social") fields. Derive
public key from private key before making the login call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Web3Auth JWT stores the compressed secp256k1 public key (33 bytes).
The backend does an exact string match, so we must send compressed
format, not uncompressed (65 bytes). Uncompressed key is still used
for ECIES operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add vault initialization flow for new users (POST /vault/init) with
  client-side key generation, ECIES wrapping, and IPNS name derivation
- Add window capability permissions (hide/show/focus/close) so the
  login window can hide after successful auth
- Intercept close_requested on main window to hide instead of quitting,
  keeping the app running as a menu-bar utility
- Update tray to Synced after auth when FUSE feature is disabled
- Rename mount point and FUSE struct from CipherVault to CipherBox

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eanup

16 fixes from UAT testing (14/15 pass, 1 known issue):

FUSE-T NFS:
- Force unmount fallback (diskutil unmount force)
- Inode reuse in populate_folder (prevents stale file handle)
- Deduplicate readdir refresh (only on offset=0)
- Parent mtime bump on children change
- Eager subfolder pre-population during mount
- Stale mount point cleanup on startup

Login/logout lifecycle:
- Webview reload on re-login (reset stale DOM)
- OAuth popup cleanup after auth completes
- Web3Auth clearCache instead of destructive logout
- Keychain delete-before-set for token persistence

Cleanup:
- Remove all eprintln diagnostic lines (errors → log::warn)
- FUSE-T userspace build config (.cargo/config.toml, pkg-config/fuse.pc)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 marked this pull request as ready for review February 8, 2026 19:21
FSM1 and others added 11 commits February 8, 2026 20:29
Four learning documents from desktop client development:
- FUSE-T NFS gotchas (single-thread, inode stability, READDIR caching)
- Tauri webview lifecycle (window reuse, Web3Auth clearCache)
- macOS system integration (keychain, force unmount, Spotlight)
- Desktop testing strategy (auth bypass proposal for automated testing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove path exclusions for .learnings/, .planning/, and *.lock
- Add path instructions to use markdown/planning/learnings for context
  but skip formatting and linting comments (handled by markdownlint)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Revert `new Function` dynamic import back to `await import('ipns')`
  which works with jest module mocking (the Function constructor bypass
  was only needed for the desktop Tauri build, not the API)
- Restore re-throw behavior in resolveRecord when DB cache is empty
  after a routing error (preserves existing test expectations)
- Remove unused generateEd25519Keypair import
- Fix prettier formatting for long hex strings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The ipns npm package is ESM-only, forcing a dynamic import() hack that
broke Jest mocking and masked a behavioral issue where resolveRecord
would re-throw BAD_GATEWAY when DB cache was empty — hanging the
desktop FUSE NFS thread.

Replace with ~65 lines of inline protobuf wire format parsing that
extracts the two fields we need (value, sequence). resolveRecord now
returns null instead of throwing when both routing and DB cache fail,
which the FUSE client handles gracefully.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Users see "EMPTY DIRECTORY" for 10-30s while IPNS resolves on login,
which is alarming. Track initial sync state and show a distinct loading
UI until the first resolve completes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement memory zeroization (H-1/H-2/H-3/H-6), FUSE permission
enforcement (H-4), secure temp file deletion (H-5), token prefix
indexing (M-1), restricted dir permissions (M-2/M-5), eliminate
intermediate key copies (M-3), and fix lock discipline (M-4).

Key changes:
- Wrap all key fields in Zeroizing<Vec<u8>> across CipherBoxFS, InodeKind
- Add Drop impl on CachedContent with zeroize, destroy() callback on unmount
- Zeroize JSON intermediates in encrypt/decrypt_folder_metadata
- Implement owner-only access() permission checking
- Zero-overwrite temp files before deletion, restrict to 0o600/0o700
- Add tokenPrefix column for O(1) refresh token lookup (migration included)
- Symlink check on mount point, consolidated clear_keys() lock discipline

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only build crypto, api, and web packages — the desktop Tauri/Rust
build is unnecessary for browser e2e tests and adds significant time.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The rotateRefreshToken query now filters by tokenPrefix for O(1)
lookup — update test assertion to expect the new field.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover all branches: valid records, large sequence numbers, missing
fields, unknown wire types (varint, length-delimited, fixed32, fixed64),
truncated varints, oversized varints, and buffer overflows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Tauri/Rust build requires system libraries (glib-2.0, etc.) not
available on the Ubuntu runner. Only build crypto, api, and web.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@FSM1 FSM1 merged commit 07884ee into main Feb 8, 2026
10 checks passed
@FSM1 FSM1 deleted the feat/phase-9-desktop-client branch February 8, 2026 23:03
This was referenced Mar 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant