Skip to main content

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.