An easy-to-host PDS on the ATProtocol, MacOS. Grandma-approved.

docs: update identity-wallet CLAUDE.md for MM-144 onboarding implementation

Reflects new contracts added during the mobile onboarding flow:
create_account IPC command, keychain/http modules, onboarding
components, and cross-crate crypto dependency.

authored by malpercio.dev and committed by

Tangled 352e2b4e a47d75e8

+34 -14
+34 -14
apps/identity-wallet/CLAUDE.md
··· 12 12 13 13 **Exposes:** 14 14 - `src/lib/ipc.ts` — typed wrappers for all Tauri IPC commands; import these instead of calling `invoke()` directly 15 - - `src/routes/+page.svelte` — root page (Greet IPC demo) 15 + - `src/lib/components/onboarding/` — five onboarding screen components (WelcomeScreen, ClaimCodeScreen, EmailScreen, HandleScreen, LoadingScreen) 16 + - `src/routes/+page.svelte` — root page: five-screen onboarding state machine (welcome -> claim_code -> email -> handle -> loading -> did_ceremony) 16 17 17 18 **Guarantees:** 18 19 - SSR is disabled globally (`ssr = false` in `src/routes/+layout.ts`); the frontend is a fully static SPA loaded from disk by WKWebView 19 20 - Build output lands in `dist/` (configured via `pages: 'dist'` in `svelte.config.js`) 20 21 - Frontend calls Tauri commands only through `src/lib/ipc.ts` — no raw `invoke()` calls in page components 22 + - Relay error codes from `create_account` are mapped back to the originating screen (e.g. EXPIRED_CODE -> claim_code step, EMAIL_TAKEN -> email step) 21 23 22 24 **Expects:** 23 25 - `pnpm install` has been run in `apps/identity-wallet/` ··· 26 28 ### Rust Backend (src-tauri/) 27 29 28 30 **Exposes:** 29 - - `src/lib.rs::greet(name: String) -> String` — registered Tauri IPC command callable from the frontend via `invoke('greet', { name })` 31 + - `src/lib.rs::greet(name: String) -> String` — Tauri IPC command (demo, still registered) 32 + - `src/lib.rs::create_account(claim_code: String, email: String, handle: String) -> Result<CreateAccountResult, CreateAccountError>` — Tauri IPC command: generates P-256 keypair, stores private key in Keychain, POSTs to relay `/v1/accounts/mobile`, stores tokens in Keychain on success 33 + - `src/keychain.rs` — iOS Keychain abstraction (`store_item`, `get_item`) under service `"ezpds-identity-wallet"` 34 + - `src/http.rs` — `RelayClient` with compile-time base URL (localhost:8080 debug, relay.ezpds.com release) 30 35 31 36 **Guarantees:** 32 37 - `crate-type = ["staticlib", "cdylib", "rlib"]` supports iOS (staticlib), Android (cdylib), and normal cargo builds (rlib) 33 38 - `src/main.rs` is the desktop entry point; `src/lib.rs::run()` is the iOS/Android entry point (via `#[cfg_attr(mobile, tauri::mobile_entry_point)]`) 34 39 - `tauri.conf.json` configures the bundle identifier, dev URL (`http://localhost:5173`), and frontend dist path (`../dist`) 40 + - `create_account` maps relay HTTP error codes to typed `CreateAccountError` variants (EXPIRED_CODE, REDEEMED_CODE, EMAIL_TAKEN, HANDLE_TAKEN, NETWORK_ERROR, UNKNOWN) serialized as `{ code: "SCREAMING_SNAKE" }` for the frontend 41 + - Private key is stored in Keychain before any network call (fail-safe ordering) 35 42 36 43 **Expects:** 37 44 - `tauri.conf.json` exists in `src-tauri/` before `cargo build` runs — the config is read at compile time by `generate_context!()` 38 45 - `cargo-tauri` is in PATH (provided by the Nix dev shell) 39 46 - Xcode and iOS Simulator are installed on the developer's macOS machine 47 + - Relay must be running at the compile-time URL for `create_account` to succeed at runtime 40 48 41 49 ## Dependencies 42 50 43 - - Frontend → Rust backend (via Tauri IPC — `@tauri-apps/api/core` `invoke()`) 44 - - Rust backend → Cargo workspace (inherits `version`, `edition`, `publish` from root `Cargo.toml`) 45 - - `src-tauri/gen/` → NOT tracked in git; generated per-developer by `cargo tauri ios init` (gitignored) 51 + - Frontend -> Rust backend (via Tauri IPC -- `@tauri-apps/api/core` `invoke()`) 52 + - Rust backend -> Cargo workspace (inherits `version`, `edition`, `publish` from root `Cargo.toml`) 53 + - Rust backend -> `crates/crypto` (workspace dep: P-256 key generation for `create_account`) 54 + - Rust backend -> relay `/v1/accounts/mobile` endpoint (via `reqwest` HTTP at runtime) 55 + - Rust backend -> iOS Keychain (via `security-framework` crate) 56 + - `src-tauri/gen/` -> NOT tracked in git; generated per-developer by `cargo tauri ios init` (gitignored) 46 57 47 58 ## Prerequisites (macOS/iOS Development) 48 59 ··· 129 140 - **`src-tauri/gen/` is gitignored**: The Xcode project generated by `cargo tauri ios init` is machine-specific. Committing it causes merge conflicts and bloats the repo. 130 141 - **`tauri` and `tauri-build` declared locally**: These crates are not in `[workspace.dependencies]` because no other workspace crate uses them. `serde` and `serde_json` use `{ workspace = true }` per the standard workspace pattern. 131 142 - **`src-tauri/.cargo/config.toml` committed**: Overrides `CC`, `AR`, and `linker` for iOS and macOS-host targets to use Xcode's unwrapped clang instead of the Nix cc-wrapper. Without this, Nix's clang wrapper injects macOS-specific flags (`-mmacos-version-min`, macOS sysroot) that are incompatible with iOS cross-compilation. See the Troubleshooting section for the full explanation. 143 + - **Compile-time relay URL**: `http.rs` uses `#[cfg(debug_assertions)]` to switch between localhost:8080 (debug) and relay.ezpds.com (release). No runtime configuration needed for the base URL. 144 + - **Keychain-before-network ordering**: `create_account` stores the private key in Keychain before POSTing to the relay -- if the network call fails, the key is already persisted and can be reused on retry. 145 + - **reqwest with rustls-tls**: Uses `default-features = false` + `rustls-tls` to avoid linking OpenSSL. On iOS, rustls handles TLS natively without additional system deps. 132 146 133 147 ## Invariants 134 148 135 149 - `src/lib/ipc.ts` is the only file that calls `invoke()` directly; page components import from `ipc.ts` 136 150 - `tauri.conf.json` bundle identifier `dev.malpercio.identitywallet` must match the iOS provisioning profile for physical device builds 137 - - `src-tauri/gen/` is never committed — regenerate with `cargo tauri ios init` 151 + - `src-tauri/gen/` is never committed -- regenerate with `cargo tauri ios init` 138 152 - `pnpm-lock.yaml` is committed and kept in sync with `package.json` 153 + - Keychain service name is always `"ezpds-identity-wallet"` (constant `keychain::SERVICE`); changing it orphans previously stored credentials 154 + - `CreateAccountError` variant names serialize as SCREAMING_SNAKE_CASE to the frontend -- the TypeScript `CreateAccountError.code` union must match exactly 139 155 140 156 ## Key Files 141 157 142 - - `src-tauri/tauri.conf.json` — Tauri config: bundle ID, devUrl, frontendDist, window settings 143 - - `src-tauri/src/lib.rs` — Tauri IPC commands and `run()` (mobile entry point) 144 - - `src-tauri/src/main.rs` — Desktop entry point (calls `lib::run()`) 145 - - `src-tauri/.cargo/config.toml` — Cargo toolchain overrides for iOS cross-compilation (CC, AR, linker per target) 146 - - `src/lib/ipc.ts` — Typed TypeScript wrappers for all Tauri IPC commands 147 - - `src/routes/+layout.ts` — `ssr = false; prerender = false` (global SPA config) 148 - - `svelte.config.js` — adapter-static with `pages: 'dist'` (SPA mode, matches tauri.conf.json) 149 - - `vite.config.ts` — Tauri-compatible Vite server (clearScreen, HMR via TAURI_DEV_HOST, envPrefix) 158 + - `src-tauri/tauri.conf.json` -- Tauri config: bundle ID, devUrl, frontendDist, window settings 159 + - `src-tauri/src/lib.rs` -- Tauri IPC commands (`greet`, `create_account`) and `run()` (mobile entry point) 160 + - `src-tauri/src/main.rs` -- Desktop entry point (calls `lib::run()`) 161 + - `src-tauri/src/keychain.rs` -- iOS Keychain abstraction (store_item, get_item) 162 + - `src-tauri/src/http.rs` -- RelayClient with compile-time base URL 163 + - `src-tauri/.cargo/config.toml` -- Cargo toolchain overrides for iOS cross-compilation (CC, AR, linker per target) 164 + - `src/lib/ipc.ts` -- Typed TypeScript wrappers for all Tauri IPC commands (greet, createAccount) 165 + - `src/lib/components/onboarding/` -- Five onboarding screen components 166 + - `src/routes/+page.svelte` -- Onboarding state machine (welcome -> claim_code -> email -> handle -> loading -> did_ceremony) 167 + - `src/routes/+layout.ts` -- `ssr = false; prerender = false` (global SPA config) 168 + - `svelte.config.js` -- adapter-static with `pages: 'dist'` (SPA mode, matches tauri.conf.json) 169 + - `vite.config.ts` -- Tauri-compatible Vite server (clearScreen, HMR via TAURI_DEV_HOST, envPrefix) 150 170 151 171 ## Troubleshooting 152 172