Runtime
import * as mod from "jsr:@nzip/lofi";
Build local-first browser applications with durable storage, reactive tables, optional Jazz sync, recoverable accounts, and an installable PWA lifecycle.
Application code composes these APIs; storage, identity, sync, PWA, and lifecycle implementations remain versioned package code.
Start with defineLofiApp and bootLofi. Use
LofiRuntime.store to bind declared Jazz tables to application UI, and
add account backup only after managed sync is configured.
AccountReplacementError
class
class AccountReplacementError extends Error {
constructor();
readonly name: string;
readonly code: string;
}
Raised when account recovery needs explicit acknowledgement of local replacement.
acquireLiveQuery
function
function acquireLiveQuery<T extends TableRow>(query: QueryBuilder<T>): LiveQueryLease<T>
Acquires the package-wide shared store for an arbitrary typed query.
acquireTableMutations
function
function acquireTableMutations<T extends TableRow, Init>(table: TableProxy<T, Init>): TableMutationLease<T, Init>
Acquires the package-wide typed mutation surface for one table.
applyPwaUpdate
function
function applyPwaUpdate(): boolean
Activates a waiting service worker; returns false when no update is ready.
assertDurableBrowser
function
function assertDurableBrowser(): void
Throws unless the current browser can open lofi's persistent local driver.
authenticateDeviceCredential
function
async function authenticateDeviceCredential(options: AuthenticateOptions): Promise<DeviceCredential>
Authenticates with a device passkey (user-verifying). Pass credentialId to
pin the ceremony to one enrolled credential; without it the request is
discoverable and any resident credential for the RP ID can assert.
AuthError
class
class AuthError extends Error {
constructor(code: AuthError["code"], message?: string);
readonly name: string;
readonly code: "cancelled" | "origin-rejected" | "unsupported" | "prf-unavailable" | "credential-missing" | "credential-mismatch" | "unknown";
}
A precise, non-leaking failure reason for a credential operation.
bootLofi
function
async function bootLofi(): Promise<void>
Starts lofi's browser lifecycle once for the current document.
Import the module that calls defineLofiApp first so the runtime can resolve
the app definition. Safe to call more than once; later calls are no-ops.
Returns: A promise that resolves once boot has been scheduled for this document.
Example
import { bootLofi } from "@nzip/lofi";
// Importing app.ts registers defineLofiApp before the runtime boots.
import { app } from "../app.ts";
void app;
void bootLofi();
checkPwaUpdate
function
function checkPwaUpdate(): Promise<boolean>
Runs the shared controller's bounded update check when registration is ready.
Resolves true when a check ran or was already in flight; inspect PwaState.update for outcome.
classifyCredentialOrigin
function
function classifyCredentialOrigin(url: URL, trustedOrigins: readonly string[]): CredentialOriginReport
Classifies whether url is a safe origin to enroll a device credential on,
against the author's committed-stable hostnames (app.ts credentialOrigins,
exact hostnames or *. suffix patterns). Guidance is platform-agnostic — the
requirement is a stable HTTPS origin you control, however you serve it.
confirmPhraseAccess
function
async function confirmPhraseAccess(): Promise<void>
Runs the user-verifying passkey ceremony that must precede revealing the phrase
on a guarded device. The ceremony is pinned to the enrolled credential, so no
other passkey for the same RP ID can satisfy it (guards enrolled before pinning
remain discoverable until re-enrolled). A no-op when the device is not guarded.
Throws cancelled if the user dismisses the prompt — the phrase is then not
revealed.
createBackupPasskey
function
async function createBackupPasskey(label?: string): Promise<boolean>
Enrolls a passkey to guard the recovery phrase on this device. Enrollment is a
user-verifying ceremony, so it doubles as the confirmation for the reveal that
immediately follows. Returns true when a guard was set; false (without
throwing) when this browser or origin cannot enroll a passkey, so the caller
can still show the phrase while telling the user it is unguarded. Re-throws a
cancelled ceremony so the caller can abort.
createPwaController
function
function createPwaController(dependencies: PwaControllerDependencies): PwaController
Creates an isolated PWA controller, primarily for custom integration and tests.
createRecoverablePasskeyBackup
function
async function createRecoverablePasskeyBackup(displayName?: string): Promise<PasskeyBackupReceipt>
Stores the current 32-byte local-first secret inside Jazz's resident,
user-verifying passkey backup. Unlike createBackupPasskey, this credential
can restore the account and is not merely a local phrase-reveal guard.
decryptAtRest
function
async function decryptAtRest(key: CryptoKey, blob: { iv: Uint8Array; ciphertext: Uint8Array }): Promise<Uint8Array>
Decrypts a blob produced by encryptAtRest.
defineLofiApp
function
function defineLofiApp<Schema>(config: LofiAppConfig<Schema>): LofiAppConfig<Schema>
Defines the application-facing values the versioned runtime needs.
The returned value retains the exact schema type so author code can select tables without importing or editing framework internals.
| Parameter | Description |
| --- | --- |
| config | The author-owned app configuration, including the raw Jazz schema. |
Returns: The same configuration, typed to the exact schema for table selection.
Example
import { defineLofiApp } from "@nzip/lofi";
import { app as schema } from "./schema.ts";
export const app = defineLofiApp({
name: "my-app",
databaseName: "my-app",
schema,
storage: "durable",
sync: { adapter: "jazz" },
});
deriveAtRestKey
function
async function deriveAtRestKey(prfSecret: Uint8Array, info: string, salt: Uint8Array): Promise<CryptoKey>
Turns a PRF secret into an AES-GCM key for at-rest encryption via HKDF-SHA-256.
Bind unrelated data to different keys by varying info.
derivePrfSecret
function
async function derivePrfSecret(salt: BufferSource, dependencies: AuthDependencies): Promise<Uint8Array>
Derives a credential-bound secret from the passkey via the WebAuthn PRF
extension in one user-verifying get(). The same salt on the same
authenticator yields the same 32-byte secret; it never leaves the device.
Throws prf-unavailable if the client or authenticator does not return a
PRF result — the secret is never faked.
durableCapabilityReport
function
function durableCapabilityReport(): DurableCapabilityReport
Reads synchronous browser capabilities needed by the durable Jazz driver.
DurableStorageUnsupportedError
class
class DurableStorageUnsupportedError extends Error {
constructor(report: DurableCapabilityReport);
name: string;
readonly report: DurableCapabilityReport;
}
Raised when the browser cannot provide the durable local-storage contract.
enableSyncBackup
function
async function enableSyncBackup(): Promise<Session>
Elects to back up and sync this account: replication to the managed Jazz app
turns on and the existing local data pushes up under the same identity. Pair
it with revealRecoveryPhrase so the user has a way back in. No-op when
no Jazz app is configured.
encryptAtRest
function
async function encryptAtRest(key: CryptoKey, plaintext: Uint8Array): Promise<{ iv: Uint8Array; ciphertext: Uint8Array }>
Encrypts plaintext with an at-rest key, returning a self-contained blob.
enrollDeviceCredential
function
async function enrollDeviceCredential(options: EnrollOptions): Promise<DeviceCredential>
Enrolls a resident, user-verifying device passkey with the PRF extension
enabled. Refuses unless the current origin is stable (pass rpId to test).
The returned portable flag says whether the credential roams across devices;
label names it in the user's password manager. Returns the opaque id.
getAuthCapability
function
async function getAuthCapability(dependencies: AuthDependencies): Promise<AuthCapability>
Reports what the current device/browser/origin can do for credential auth.
getPwaState
function
function getPwaState(): PwaState
Returns the shared controller's current state snapshot.
getRuntime
function
function getRuntime(): Promise<LofiRuntime>
Opens or reuses the one package runtime for the current browser document.
getRuntimeDiagnostics
function
function getRuntimeDiagnostics(): RuntimeDiagnostics
Returns a value-only snapshot of runtime resource and durability counters.
getRuntimePrincipal
function
function getRuntimePrincipal(): string | null
The stable Jazz principal currently opened by the package runtime.
isAccountReplacementError
function
function isAccountReplacementError(error: unknown): error is AccountReplacementError
True when recovery requires explicit confirmation before replacing a local account.
isAuthError
function
function isAuthError(error: unknown): error is AuthError
True when an error came from a passkey ceremony (enroll / confirm).
isRecoverablePasskeyError
function
function isRecoverablePasskeyError(error: unknown): error is RecoverablePasskeyError
True when an error came from a recoverable passkey backup or restore ceremony.
isRecoveryError
function
function isRecoveryError(error: unknown): error is RecoveryError
True when an error is a recovery-phrase problem the user can fix and retry.
LiveQueryStore
class
class LiveQueryStore<T extends TableRow> {
constructor(query: QueryBuilder<T>, environment: LiveQueryEnvironment, onIdle: () => void);
getSnapshot: () => LiveQuerySnapshot<T>;
subscribe: (listener: Listener) => () => void;
get consumerCount(): number;
dispose(): void;
}
A shared framework-neutral reactive store for one typed Jazz query.
LofiConfigurationError
class
class LofiConfigurationError extends Error {
readonly name: string;
readonly code: string;
}
Raised when package runtime startup cannot resolve valid author-owned app configuration.
pwaController
const
const pwaController: PwaController;
Shared controller used by the root runtime and optional Preact bindings.
pwaFailureMessage
function
function pwaFailureMessage(code: PwaFailureCode): string
Returns actionable, non-technical recovery guidance for a PWA failure.
readAccountSession
function
async function readAccountSession(): Promise<Session>
Resolves the runtime before returning a session with a stable user_id.
readDeviceCapabilityReport
function
async function readDeviceCapabilityReport(): Promise<DeviceCapabilityReport>
Reads the complete capability report without requesting new browser permission.
readSession
function
function readSession(): Session
Reads the current session. Synchronous — it never prompts or touches the network.
RecoverablePasskeyError
class
class RecoverablePasskeyError extends Error {
constructor(code: RecoverablePasskeyErrorCode, options?: ErrorOptions);
readonly name: string;
}
A non-secret, actionable recoverable-passkey failure.
RecoveryError
class
class RecoveryError extends Error {
constructor(code: RecoveryError["code"], message?: string);
readonly name: string;
readonly code: "invalid-length" | "invalid-word" | "invalid-checksum" | "invalid-secret";
}
A precise, non-leaking failure reason for a recovery-phrase operation.
recreateRuntime
function
function recreateRuntime(): Promise<LofiRuntime>
Replaces the active Jazz client while preserving the configured account secret.
reloadAfterRuntimeStartupFailure
function
function reloadAfterRuntimeStartupFailure(reload: () => void): void
Performs the explicit navigation required after closing incompatible app tabs.
reloadBrowserRuntime
function
async function reloadBrowserRuntime(): Promise<never>
Fully tears down a browser persistent worker and reloads the document. Jazz alpha.53 cannot attach a second OPFS worker reliably in the same document after shutdown; navigation is the supported clean-runtime boundary.
requestPersistentStorage
function
async function requestPersistentStorage(): Promise<DeviceCapabilityReport>
Requests eviction protection, then returns the browser's authoritative capability report.
requestPwaInstall
function
function requestPwaInstall(): Promise<PwaInstallState>
Requests the deferred browser installation prompt when one is available.
restoreFromPasskey
function
async function restoreFromPasskey(options: AccountReplacementOptions): Promise<Session>
Restores a passkey-backed secret and recreates Jazz on that stable principal.
restoreFromRecoveryPhrase
function
async function restoreFromRecoveryPhrase(phrase: string, options: AccountReplacementOptions): Promise<Session>
Reconstructs an account from its recovery phrase, elects sync, and replaces
the active runtime. Throws RecoveryError for malformed phrases and
AccountReplacementError when confirmation is required.
revealRecoveryPhrase
function
async function revealRecoveryPhrase(): Promise<string>
Reveals the current account's recovery phrase — the same 32-byte secret encoded as words. Show it for the user to write down; never persist it. Works whether or not sync is on, but only matters once the account syncs, since the phrase recovers what has been backed up.
runtimeRecreatedEvent
const
const runtimeRecreatedEvent: "lofi:runtime-recreated";
Event dispatched after account, sync, or runtime replacement changes active state.
RuntimeStartupError
class
class RuntimeStartupError extends Error {
constructor(failure: RuntimeStartupFailure, cause?: unknown);
readonly name: string;
readonly code: RuntimeStartupFailureCode;
readonly failure: RuntimeStartupFailure;
}
Lofi-owned error boundary for a rejected persistent runtime startup.
settleUiMutation
function
async function settleUiMutation(mutation: Promise<unknown>): Promise<void>
Lets event handlers await a UI mutation without leaking an unhandled rejection.
shutdownRuntime
function
function shutdownRuntime(): Promise<void>
Releases stores, subscriptions, the Jazz client, and persistent worker resources.
stopSyncBackup
function
async function stopSyncBackup(): Promise<Session>
Stops replicating this account to the server and returns to local-only. The local data and the account are untouched — this only detaches the network, so electing to sync again resumes against the same account.
subscribePwaState
function
function subscribePwaState(subscriber: (state: PwaState) => void): () => void
Subscribes to shared PWA state and returns an idempotent unsubscribe function.
subscribeRuntimeDiagnostics
function
function subscribeRuntimeDiagnostics(listener: () => void): () => void
Subscribes to diagnostics changes and returns an idempotent unsubscribe function.
TableMutationStore
class
class TableMutationStore<T extends TableRow, Init> {
constructor(table: TableProxy<T, Init>, environment: TableMutationEnvironment, onIdle: () => void);
getSnapshot: () => TableMutationSnapshot;
subscribe: (listener: Listener) => () => void;
get consumerCount(): number;
insert(values: Init): Promise<T>;
update(id: string, patch: Partial<Init>): Promise<void>;
remove(id: string): Promise<void>;
dispose(): void;
}
Framework-neutral typed mutations and observable durability for one table.
TableStore
class
class TableStore<T extends TableRow, Init> {
constructor(db: Db, table: TableHandle<T, Init>, diagnostics: RuntimeDiagnostics, options: TableStoreOptions);
getSnapshot: () => TableSnapshot<T>;
subscribe: (listener: Listener) => () => void;
insert(values: Init): Promise<void>;
update(id: string, patch: Partial<Init>): Promise<void>;
delete(id: string): Promise<void>;
reportMutationError(event: MutationErrorEvent): void;
close(): void;
}
A generic, reactive store over a single declared table. It has no knowledge of
the application schema beyond TableRow: callers bind it to one of their
own schema.<name> tables and read/write typed rows through it.
AccountReplacementOptions
type
type AccountReplacementOptions = {
confirmLocalReplacement?: boolean;
};
Confirmation required before recovery may replace a different local-only account.
AuthCapability
type
type AuthCapability = {
webAuthn: boolean;
prf: PrfSupport;
origin: CredentialOriginReport;
};
What the current device/browser/origin can do for credential auth.
AuthDependencies
type
type AuthDependencies = {
credentials?: CredentialsContainer;
rpId?: string;
trustedOrigins?: readonly string[];
};
Injected browser surfaces, so the flows are unit-testable without a device.
CredentialOriginReport
type
type CredentialOriginReport = {
status: "stable" | "local-only" | "unverified" | "blocked";
rpId: string;
action: string;
};
How stable the current origin is for enrolling a passkey. A passkey is bound
to the origin hostname (its RP ID); enrolling on an origin that later changes
silently breaks the credential, so enrollment is gated on stable.
DeviceCapabilityReport
type
type DeviceCapabilityReport = {
secureContext: boolean;
opfs: boolean;
sharedWorker: boolean;
webLocks: boolean;
messageChannel: boolean;
durableDriverSupported: boolean;
webAuthn: boolean;
prf: "available" | "not-reported" | "unknown" | "unavailable";
persistentPermission: "granted" | "not-granted" | "unavailable" | "error";
displayMode: "standalone" | "browser";
};
Browser capabilities that determine whether lofi can provide its runtime guarantees.
DeviceCredential
type
type DeviceCredential = {
id: string;
rpId: string;
portable: boolean;
};
An enrolled or authenticated device credential.
DurableCapabilityReport
type
type DurableCapabilityReport = Omit<DeviceCapabilityReport, "persistentPermission">;
Capability report that excludes the separately requested persistence permission.
EnrollOptions
type
type EnrollOptions = AuthDependencies & { label?: string };
Options for enrollDeviceCredential.
InstallEnvironment
type
type InstallEnvironment = {
displayModeStandalone: boolean;
navigatorStandalone: boolean;
platform: string;
maxTouchPoints: number;
secureContext: boolean;
serviceWorkerSupported: boolean;
};
Browser signals used to classify installed and manual-install experiences.
InstallPromptEvent
type
type InstallPromptEvent = Event & { userChoice: Promise<{ outcome: "accepted" | "dismissed" }>; prompt(): Promise<void> };
Chromium install event retained until application UI requests the prompt.
LiveQueryLease
type
type LiveQueryLease<T extends TableRow> = {
store: LiveQueryStore<T>;
release(): void;
};
A retained reference to a shared live-query store. Release it after its consumer unsubscribes.
LiveQuerySnapshot
type
type LiveQuerySnapshot<T extends TableRow> = {
status: "loading" | "ready" | "error";
rows: T[];
error: string | null;
};
Honest read state for an arbitrary typed Jazz query.
LofiAppConfig
type
type LofiAppConfig<Schema = unknown> = {
name: string;
databaseName: string;
schema: Schema;
storage: "durable";
credentialOrigins?: readonly string[];
passkey?: { rpId?: string };
sync: { adapter: "jazz" };
repositoryUrl?: string;
};
Author-owned configuration consumed by the package runtime.
Example
const config: LofiAppConfig<typeof schema> = {
name: "my-app",
databaseName: "my-app",
schema,
storage: "durable",
sync: { adapter: "jazz" },
};
LofiRuntime
type
type LofiRuntime = {
db: Db;
diagnostics: RuntimeDiagnostics;
store<T extends TableRow, Init>(table: TableHandle<T, Init>): TableStore<T, Init>;
shutdown(): Promise<void>;
};
The shared, lazily opened Jazz client and its application-facing adapters.
PasskeyBackupReceipt
type
type PasskeyBackupReceipt = {
user_id: string;
rpId: string;
};
Non-secret confirmation that a recoverable passkey was created for an account and RP-ID.
PrfSupport
type
type PrfSupport =
| "available"
| "not-reported"
| "unknown"
| "unavailable";
Whether the WebAuthn PRF extension can be used on this client.
PwaController
type
type PwaController = {
getState(): PwaState;
subscribe(subscriber: (state: PwaState) => void): () => void;
requestInstall(): Promise<PwaInstallState>;
checkForUpdate(): Promise<boolean>;
applyUpdate(): boolean;
initialize(): void;
};
Stateful controller for browser installation and service-worker updates.
PwaControllerDependencies
type
type PwaControllerDependencies = {
readonly eventTarget?: () => EventTarget;
readonly visibilityTarget?: () => EventTarget;
readonly serviceWorker?: () => ServiceWorkerContainer | undefined;
readonly installEnvironment?: () => InstallEnvironment;
readonly isVisible?: () => boolean;
readonly now?: () => number;
readonly setTimeout?: (callback: () => void, milliseconds: number) => unknown;
readonly clearTimeout?: (handle: unknown) => void;
readonly updateCheckIntervalMs?: number;
readonly updateCheckTimeoutMs?: number;
readonly production?: () => boolean;
readonly deploymentBaseUrl?: () => string;
readonly reload?: () => void;
readonly exposeState?: (state: PwaState) => void;
};
Injectable browser surfaces used to test the PWA lifecycle deterministically.
PwaFailureCode
type
type PwaFailureCode =
| "registration"
| "installation"
| "install-prompt"
| "update-check"
| "precache"
| "runtime-cache";
Stable categories for recoverable offline/PWA failures.
PwaInstallState
type
type PwaInstallState =
| "installed"
| "available"
| "prompting"
| "accepted"
| "dismissed"
| "manual-ios"
| "manual-browser"
| "unsupported";
Browser installation states exposed to application UI.
PwaState
type
type PwaState = {
worker: PwaWorkerState;
install: PwaInstallState;
update: PwaUpdateState;
failure?: { code: PwaFailureCode; message: string };
};
Current install, service-worker, and offline-cache state.
PwaUpdateState
type
type PwaUpdateState =
| "idle"
| "checking"
| "installing"
| "ready"
| "applying"
| "failed";
Foreground update-check and waiting-worker states exposed to application UI.
PwaWorkerState
type
type PwaWorkerState =
| "development-disabled"
| "unsupported"
| "registering"
| "ready"
| "failed";
Service-worker lifecycle states exposed to application UI.
RecoverablePasskeyErrorCode
type
type RecoverablePasskeyErrorCode =
| "cancelled"
| "unsupported"
| "credential-missing"
| "rp-id-mismatch"
| "verification-failed"
| "invalid-credential"
| "backup-failed"
| "restore-failed";
Stable categories for recoverable passkey backup and restore failures.
RowOf
type
type RowOf<Table> = Table extends { readonly _rowType: infer Row } ? Row : never;
The row type of one declared schema table — RowOf<typeof app.schema.tasks>.
Lets author code derive row types from its schema without importing the
vendor module, keeping UI islands on public package seams.
RuntimeDiagnostics
type
type RuntimeDiagnostics = {
storageState: "persistent-requested" | "persistent-driver-open" | "failed";
startupFailure: RuntimeStartupFailure | null;
clientsCreated: number;
activeClients: number;
activeConsumers: number;
activeVendorSubscriptions: number;
totalVendorSubscriptions: number;
activeMutationListeners: number;
totalMutationListeners: number;
unsubscribeCalls: number;
localWaitCalls: number;
pendingLocalWrites: number;
pendingGlobalWrites: number;
lastWriteDurability: "none" | "local" | "global" | "failed";
mutationErrors: number;
};
// Package-owned runtime diagnostics. Runtime-owned observability counters. These describe the framework's storage, subscription, and write machinery and never reference any application schema.
RuntimeStartupFailure
type
type RuntimeStartupFailure = {
code: RuntimeStartupFailureCode;
runtimeMode: "local" | "managed";
message: string;
};
Non-sensitive runtime context retained for diagnostics and recovery UI.
RuntimeStartupFailureCode
type
type RuntimeStartupFailureCode =
| "broker-incompatible"
| "configuration-error"
| "storage-startup-failed"
| "unsupported-capabilities";
Stable categories for failures that prevent Lofi's persistent runtime from opening.
Session
type
type Session = {
user_id: string | null;
syncAvailable: boolean;
backedUp: boolean;
syncing: boolean;
phraseGuarded: boolean;
passkeyRecoverable: boolean;
};
A snapshot of the account: what is possible and what the user has chosen.
TableHandle
type
type TableHandle<T extends TableRow, Init> = TableProxy<T, Init> & QueryBuilder<T>;
A declared schema table (schema.<name>). It is both an insert/update/delete
target and a query source, so the store accepts the intersection Jazz expects.
T is the row type and Init the insert type (row minus server-owned fields).
TableMutationLease
type
type TableMutationLease<T extends TableRow, Init> = {
store: TableMutationStore<T, Init>;
release(): void;
};
Retained ownership of one shared table-mutation store.
TableMutationSnapshot
type
type TableMutationSnapshot = {
pending: number;
durability: "none" | "local" | "global" | "failed";
error: string | null;
};
Observable state shared by every mutation consumer for one table.
TableRow
type
type TableRow = {
id: string;
};
The minimum shape every persisted row exposes to the framework.
TableSnapshot
type
type TableSnapshot<T extends TableRow> = {
status: "loading" | "ready" | "error";
rows: T[];
durability: "none" | "local" | "global" | "failed";
error: string | null;
};
Reactive table state including rows and the last observed durability tier.
TableStoreOptions
type
type TableStoreOptions = {
syncConfigured?: () => boolean;
onDiagnosticsChange?: () => void;
};
Runtime options controlling durability waits and diagnostics notifications.