| Sync password readable on the device (accepted, ADR-038) | High | The self-hosted password sits in localStorage in plain text. script-src 'self' and the strict CSP, password sent only to the announced CouchDB, never logged or synced, removed on Disconnect, stated in the login form. Residual: successful XSS or a shared browser profile exposes it. Use a dedicated low-privilege CouchDB user. |
| No per-device revocation in self-hosted mode (accepted) | Medium | A lost device means a password change, which locks out every device until updated. Workaround: one CouchDB user per device with a member list in _security (README). |
| Password guessing and lockout of all devices | Medium | Proxy rate limit and path allowlist, CouchDB lockout after 5 wrong passwords (Section 7.1). Behind a terminator that hides client addresses one wrong password, including the PWA’s connect check, can briefly lock out all devices; forward and trust the client address or accept the lockout. |
| CouchDB admin authenticates on the proxied path | High | nginx cannot tell users apart. COUCHDB_PASSWORD is long and random, never entered on devices, and CouchDB stays unpublished. |
| TLS edge misconfigured by the self-hoster | High | Basic credentials travel in every request. Serve only over HTTPS (tls profile or own terminator), keep the loopback bind, never publish CouchDB; the development override is never deployed. |
| External CouchDB without CORS or CSP entry | Low | Sync fails with network errors unless CORS allows the PWA origin and FORKED_CSP_CONNECT_SRC lists the CouchDB origin (Section 7.2). |
| Two sync modes double the sync test surface (accepted) | Medium | Both strategies share syncWithRemote and the status store, each with unit tests and a Playwright journey (Section 8.8). Cloud mode is tested against stubs only; a change to shared sync code must run both suites. |
| Operator gateway can drift from the PWA client (accepted) | Medium | The contract is the client plus Section 7.3; the gateway is tested in the operator’s repository. bun run check:arc42 verifies only that documented routes are still called; website pages (approval, /account) are covered by no check. Review the contract whenever a route, field or linked page changes. |
| Removed cloud device keeps access up to 15 minutes (accepted) | Medium | Removal stops new tokens; the current token expires within 15 minutes. |
| Email address as personal data at the operator (accepted) | Medium | Cloud accounts use magic-link sign-in. Self-hosting is accountless, the PWA stores no email and has no analytics; retention and deletion are documented by the operator. |
| Conflicts can hide a newer concurrent edit | Medium | Deterministic winner (ADR-025); losing revisions stay recoverable as conflict leaves, but no UI shows them. |
| Browser storage limits and eviction | High | Browsers may evict storage of rarely used sites (notably Safari), so local-only users can lose data. Backup and restore in Settings (ADR-032). |
| AI providers receive recipe content and hold user API keys | Medium | Opt-in per provider, keys in headers only and never synced, optional passkey lock, restrictive CSP (ADR-024). |
| CouchDB is a single server-side copy | High | Devices hold full local copies and can export a forked backup. Backing up the couchdb_data volume is the operator’s job; the README has no step-by-step restore procedure. |
| Safari and iOS behavior is unverified | Medium | @smoke journeys run on WebKit in CI. On Playwright WebKit the app hangs in bootstrap because PouchDB createIndex never resolves; the WebKit step is non-blocking until it is known whether Safari and iOS are affected. |
| Solo maintainer | High | Architecture clarity, automation and agent-readable docs. |