clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

main
lofi / docs / troubleshooting.md
troubleshooting.md 9.7K rendered
1# Troubleshooting23Start with:45```sh6deno task doctor7```89The report names the blocked capability or configuration and gives a remediation without printing10environment values.1112## Doctor or build reports a PWA source blocker1314Start with the first named file and action. The checks validate `public/manifest.webmanifest` as15JSON; require stable identity, names, same-origin launch scope, display mode, colors, and complete16icon roles; and inspect the dimensions and MIME types of local icons, shortcuts, and optional17screenshots. Asset filenames and branding are replaceable, and unknown optional manifest members18remain allowed.1920If source checks pass but build reports `dist/`, do not hand-edit the output. Fix the author-owned21manifest, shell link, route, or asset, then rerun `deno task build`. The production check22deliberately treats HTML manifest links, the worker revision and scope, `lofi-build.json`, and23`lofi-precache.json` as one artifact so a partial or stale build cannot pass.2425## The app says configuration is incomplete2627Managed sync requires both public values:2829```text30JAZZ_APP_ID31JAZZ_SERVER_URL32```3334Set both, or remove both to run local-only. Do not move `JAZZ_ADMIN_SECRET` or `BACKEND_SECRET` into35client-visible variables.3637If provisioning should replace an existing `.env`, review and back up the intended configuration,38then use the command's explicit `--force` option:3940```sh41deno task jazz:provision --force42```4344## Durable storage is blocked4546lofi requires a secure context, OPFS, SharedWorker, Web Locks, and MessageChannel. It stops instead47of silently switching to memory-only data.4849- Use a supported browser: Android Chrome 148+ or iOS Safari 16.4+.50- Use `localhost` during development or HTTPS on another device.51- Avoid embedded/private browser surfaces that disable required storage APIs.52- Treat clearing site data as destructive unless the account has synced and the recovery phrase is53 safe.5455## Another tab is running an incompatible app version5657Lofi stops persistent runtime startup when another tab for the same app is using an incompatible58browser broker configuration. This is reported as `broker-incompatible` in runtime diagnostics in59both local and managed mode.60611. Close every other tab or installed-app window for this app.622. Return to the tab showing the recovery notice.633. Select **Reload app** once.6465The package does not take over the existing broker, fall back to memory, or automatically enter a66reload loop. Do not clear site data for this condition: closing the incompatible tabs and explicitly67reloading creates the clean document boundary the persistent driver requires.6869## A task disappeared after changing app configuration7071Check whether `databaseName` or the managed `JAZZ_APP_ID` changed. Both participate in the durable72storage namespace. Restoring the old values reopens the old namespace; copying rows between73namespaces is not automatic.7475## Preview says the build is missing or stale7677Run:7879```sh80deno task build81deno task preview82```8384Do not hand-create `dist/lofi-build.json`; it must describe the output produced by the build.8586## The production build reports a secret leak8788Remove the named server-only value from source, public files, logs, fixtures, and generated client89configuration. Rotate a real credential if it entered source control or an artifact, then rebuild.9091## Browser tests skip or cannot launch9293Set the base URL when running the opt-in example:9495```sh96LOFI_E2E_BASE_URL=http://localhost:4321/ \97 deno test -A tests/convergence_e2e_test.ts98```99100The base URL must say `localhost`, not `127.0.0.1`: the convergence gate enrolls a passkey to elect101sync, and an IP address is not a valid WebAuthn RP ID, so the ceremony never starts.102103Install the pinned Chromium runtime if needed:104105```sh106deno run -A npm:playwright@1.61.1 install chromium107```108109## Local writes work but another device does not update110111Check these states separately:1121131. The deployment has a complete public Jazz pair.1142. The user explicitly elected to sync in `AccountGate`.1153. The latest write reached global durability rather than only local durability.1164. Both devices restored the same account identity.1175. The deployed schema and permissions allow the operation.118119“Sync configured” is not proof that a specific user opted in or that a specific write reached the120server.121122## A device with an enrolled ticket came back local-only123124The declared sync location persists as a sealed record whose key lives in the browser's IndexedDB.125Clearing site data partially — IndexedDB without localStorage, or a browser "free up space" eviction126— leaves the record in place but unopenable, and the device deliberately falls back to local-only127rather than guessing. Local data is untouched. Re-enroll the ticket (paste it again) to declare the128sink and resume syncing under the same account.129130## Sync is connected but writes to a self-hosted store never complete131132Against a store with no deployed schema, writes hang rather than fail. When the active sink is a133ticket-gated node URL, the runtime checks the store at boot and records the result as `storeStatus`134in runtime diagnostics; the development inspector's **Store** row and the `DeviceStatus` panel135display it. `no_schema` means the store has never been provisioned for this app — follow136[Store provisioning](store-provisioning.md), then reload. The runtime only reports store state; it137never provisions or repairs a store on its own, and for non-ticket sinks the row stays138`not checked`.139140Ticket enrollment runs the same check up front and refuses to keep an enrollment whose store answers141`no_schema` or rejects the ticket (`SyncEnrollmentError`), so this boot-time symptom is reached142through a store that regressed after enrollment, not through a fresh enrollment.143144## Enrolling a ticket fails with "no schema deployed"145146The node accepted the ticket, but its store has never been provisioned for this app, so nothing147could sync; enrollment was rolled back and the device is unchanged. Provision the store — see148[Store provisioning](store-provisioning.md) — then enroll again. A sync-only ticket cannot149provision; the store's owner deploys the schema from a provision-scoped capability first.150151## Sync says it belongs to another account152153Sync on a device is pinned to the account that elected it. When the account in hand differs — a154restored identity over a device that was already syncing, or a reset browser store that minted a155fresh local account — the runtime boots local-only with transport suppressed, the session reports156`syncOwnerMismatch`, and the `DeviceStatus` panel names the owning account when it is known. Nothing157is merged and nothing is deleted. Either stop syncing (which releases the pin, so the current158account may elect afterwards) or restore the owning account, then enable sync again.159160## The app stopped reloading automatically161162Framework-driven reloads (sync election, account replacement) are budgeted per tab: a sequence of163reloads that never reaches a working runtime stops instead of cycling, and the failure surfaces as a164`reload-loop` runtime startup failure in the recovery UI and the device report. A reload cycle means165persisted state contradicts itself, often after a partially cleared browser store. Check the device166report's account and sync state, resolve what it names, then reload manually.167168## The installed app opened empty after Add to Home Screen169170WebKit gives an installed web app its own storage container: cookies are copied at install time, but171OPFS, IndexedDB, and localStorage are not. Data created in a Safari tab before installing stays in172Safari's container — it is not lost, and the installed app cannot see it. When the runtime detects173this fork it renders a notice (`.lofi-storage-fork-banner`), and the `DeviceStatus` panel's174**Storage container** row reads `fresh install — previous data is in the browser`.175176To move the data:1771781. Open the site in Safari (the browser, not the installed icon). The original data is there.1792. In the account panel, turn on sync, or back up the account (reveal the recovery phrase or create180 a passkey backup) where a sync location exists.1813. Return to the installed app and restore the account there; enrolled sync replays the data.1824. Once the data appears, dismiss the notice.183184Dismissing without restoring starts the installed app fresh; the Safari copy remains until Safari's185site data for the origin is cleared. If no notice appeared (the flag cookie expires after seven days186without a browser visit), the same steps apply. Background on the container fork and its limits is187in [Deployment](deployment.md#installed-apps-get-their-own-storage-container).188189## Recovery does not restore a recent item190191The phrase restores account authority, not unsynced device storage. Data returns only if it reached192managed sync before the original device was lost or cleared.193194## Passkey restore was cancelled or cannot find an account195196- **Cancelled:** retry the user-verification prompt, or continue with the recovery phrase.197- **No recoverable passkey:** confirm the passkey exists in the active provider. A legacy198 phrase-reveal guard is not an account backup.199- **Different app hostname / RP-ID:** open the canonical production hostname used during backup.200- **Verification failed:** unlock the platform/password-manager authenticator and retry.201- **Unsupported browser/provider:** use the recovery phrase. Provider availability is not portable202 across every iOS, Android, browser, and password-manager combination.203204The package maps these states without printing credential IDs, account secrets, or phrases.205206## Sharing says managed sync is required207208Private resources work local-only. Direct shares and groups require both a configured Jazz server209and the current account's explicit sync election. Back up and enable sync in `AccountGate`, then210retry. A configured deployment alone does not opt the current account into sync.