docs: update CLAUDE.md with biometric unlock + reasoning persistence

This commit is contained in:
Bruno Deanoz 2026-06-21 23:58:05 +02:00
parent 88559bf49e
commit edbdbd8697

View file

@ -35,7 +35,7 @@ Make the **native Android app** (`kaizen-app`) a valid daily-driver that talks t
### Package structure (`app/src/main/java/dev/kaizen/app/`) ### Package structure (`app/src/main/java/dev/kaizen/app/`)
``` ```
auth/ LoginScreen auth/ LoginScreen, BiometricUnlock (KeyStore + CryptoObject)
chat/ ChatScreen, ChatViewModel, Sidebar, ChatComponents, ChatModels, chat/ ChatScreen, ChatViewModel, Sidebar, ChatComponents, ChatModels,
ModelSheet, Markdown, Background ModelSheet, Markdown, Background
db/ Room offline cache: KaizenDatabase, Entities, DAOs, db/ Room offline cache: KaizenDatabase, Entities, DAOs,
@ -174,7 +174,7 @@ Send message → in-memory during streaming → Room flush after stream ends
- `db/KaizenDatabase.kt` — singleton via `companion object` (double-checked locking), thread-safe - `db/KaizenDatabase.kt` — singleton via `companion object` (double-checked locking), thread-safe
- `db/ConversationRepository.kt``observeAll()` (Flow of ConversationSummary), `refresh()` (server → Room), `allUnlockedIds()` - `db/ConversationRepository.kt``observeAll()` (Flow of ConversationSummary), `refresh()` (server → Room), `allUnlockedIds()`
- `db/MessageRepository.kt``observeMessages()`, `getCached()`, `fetchAndCache()`, `cacheMessages()`, `prefetchMissing()` (background prefetch for offline) - `db/MessageRepository.kt``observeMessages()`, `getCached()`, `fetchAndCache()`, `cacheMessages()`, `prefetchMissing()` (background prefetch for offline)
- `db/Converters.kt``List<Attachment>` ↔ JSON string via kotlinx.serialization - `db/Converters.kt``List<Attachment>` + `List<SearchSource>` ↔ JSON string via kotlinx.serialization
- `db/Mappers.kt``ConversationSummary``ConversationEntity`, `StoredMessage``MessageEntity` - `db/Mappers.kt``ConversationSummary``ConversationEntity`, `StoredMessage``MessageEntity`
**AGP 9.x compat:** `android.disallowKotlinSourceSets=false` in `gradle.properties` (KSP issue [#2729](https://github.com/google/ksp/issues/2729)). **AGP 9.x compat:** `android.disallowKotlinSourceSets=false` in `gradle.properties` (KSP issue [#2729](https://github.com/google/ksp/issues/2729)).
@ -191,6 +191,8 @@ All backend work for the app is done. `pnpm test:run` green (774+), `tsc` + `lin
- **`GET /api/v1/me`** — identity + `defaultModel` for native clients. - **`GET /api/v1/me`** — identity + `defaultModel` for native clients.
- **`GET /api/v1/models`** — Bearer-authed, returns filtered model catalog. - **`GET /api/v1/models`** — Bearer-authed, returns filtered model catalog.
- **`/api/v1/conversations`** — thin re-exports of canonical handlers, Bearer-authed. - **`/api/v1/conversations`** — thin re-exports of canonical handlers, Bearer-authed.
- **`POST /api/v1/auth/unlock`** — Bearer-authed, returns unlock token in response body (not cookie) for native clients. Rate-limited 10/5min per user.
- **`GET /api/v1/conversations/[id]`** — accepts `X-Unlock-Token` header alongside the existing `kaizen_unlock` cookie for locked conversations.
- **`GET /api/v1/meta`** — capability handshake. - **`GET /api/v1/meta`** — capability handshake.
- **App-devices card** (`components/settings/app-tokens-card.tsx`) — see/revoke signed-in devices on `/settings/security`. - **App-devices card** (`components/settings/app-tokens-card.tsx`) — see/revoke signed-in devices on `/settings/security`.
- **API versioning**`/api/v1/` for outward contract, additive-only within v1. Web-internal endpoints stay unversioned. - **API versioning**`/api/v1/` for outward contract, additive-only within v1. Web-internal endpoints stay unversioned.
@ -254,13 +256,13 @@ These are what separate "prototype" from "daily-driver":
- [ ] **Conversation rename/delete** from the sidebar (currently read-only) - [ ] **Conversation rename/delete** from the sidebar (currently read-only)
- [ ] **Conversation pin/unpin** from the sidebar - [ ] **Conversation pin/unpin** from the sidebar
- [ ] **In-app unlock for locked conversations** (currently skipped with "Gesperrter Chat" label) - [x] **In-app unlock for locked conversations** — BiometricPrompt with CryptoObject (AES key in TEE/StrongBox), hardware-enforced biometric auth → server unlock token via `POST /api/v1/auth/unlock` → fetch messages with `X-Unlock-Token` header
### 3. Security hardening ### 3. Security hardening
- [ ] **Auto-logout on 401** — if the token is rejected, route to login screen instead of showing error banner - [ ] **Auto-logout on 401** — if the token is rejected, route to login screen instead of showing error banner
- [ ] **Server version check** — call `/api/v1/meta` after login to detect server skew and warn cleanly - [ ] **Server version check** — call `/api/v1/meta` after login to detect server skew and warn cleanly
- [ ] **Biometric lock** (`BiometricPrompt` + KeyStore binding) — see `LATER.md` §1C - [x] **Biometric lock** (`BiometricPrompt` + KeyStore/CryptoObject) — implemented for conversation unlock; `setUserAuthenticationRequired(true)` + `setInvalidatedByBiometricEnrollment(true)` ensures hardware-enforced biometric, not bypassable even on rooted devices
- [ ] **`FLAG_SECURE`** on sensitive screens — see `LATER.md` §1B - [ ] **`FLAG_SECURE`** on sensitive screens — see `LATER.md` §1B
### 4. Features for parity with the web ### 4. Features for parity with the web