forked / Architecture

11. Risks and Technical Debt

11.1 Risks

RiskImpactMitigation in place
Sync password readable on the device (accepted, ADR-038)HighThe 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)MediumA 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 devicesMediumProxy 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 pathHighnginx cannot tell users apart. COUCHDB_PASSWORD is long and random, never entered on devices, and CouchDB stays unpublished.
TLS edge misconfigured by the self-hosterHighBasic 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 entryLowSync 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)MediumBoth 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)MediumThe 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)MediumRemoval stops new tokens; the current token expires within 15 minutes.
Email address as personal data at the operator (accepted)MediumCloud 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 editMediumDeterministic winner (ADR-025); losing revisions stay recoverable as conflict leaves, but no UI shows them.
Browser storage limits and evictionHighBrowsers 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 keysMediumOpt-in per provider, keys in headers only and never synced, optional passkey lock, restrictive CSP (ADR-024).
CouchDB is a single server-side copyHighDevices 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 unverifiedMedium@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 maintainerHighArchitecture clarity, automation and agent-readable docs.

11.2 Technical Debt

DebtSeverityDescription
Feature modules import each otherLowrecipes uses the shopping ingredient search hook and shopping uses the settings duplicate-amount modal. The architecture test does not restrict cross-feature imports (ADR-031).
No conflict UIMediumConflict leaves accumulate storage and are never surfaced.
Unverified quality scenariosMediumNo automated test for the self-host proxy or compose stack (manual curl and run), no search benchmark or reference device for the Performance scenario, no zero-egress test with sync enabled.
Limited import formatsMediumImport covers recipe JSON files and photos; Schema.org recipe.json and zip files exported by forked cannot be re-imported.
One shared password for self-hostingMediumNo per-device credentials without extra CouchDB users; cloud mode already uses short-lived tokens.
Basic searchMediumLocal search only; may not scale beyond the target collection size.

View source on GitHub