Disk file home
The disk file is the purest form of the promise: the durable home is a file on the user’s own machine, and nothing else is involved. No account, no cloud, no consent screen, no backend of any size.
One call
const outcome = await store.connectFile(); // opens the browser's save dialog
switch (outcome) {
case 'started': break; // new file: this device's data now lives in it
case 'merged': break; // existing backup: folded with this device
case 'manual': break; // no File System Access here (see below)
case 'cancelled': break; // the user closed the picker
}
connectFile() needs a user gesture (a click), because the File System Access
API does. From then on, every debounced save also writes that file, encrypted
if protect() is on, and the store re-acquires the handle
on the next visit without asking again. Pass a password for an already
encrypted file: store.connectFile({ password }).
Browser support, honestly
File System Access ships in Chromium browsers (Chrome, Edge, Brave, Arc…).
Firefox and Safari do not expose it, so there connectFile() returns
'manual': fall back to a downloaded file the user saves and re-imports.
if ((await store.connectFile()) === 'manual') {
await store.downloadBackup(); // they save it themselves
// ...later: await store.importBackup(pickedFile);
}
Same format, same encryption; the difference is one gesture per backup instead of zero.
Inside a desktop shell
A native webview usually has no File System Access at all - macOS and Linux
shells embed WebKit - so packaging your app would lose this home in the one
place writing a real file is easiest. Hand the shell its own filesystem and
dialog calls once and connectFile() comes back, on a path rather than a
handle:
import { useDesktopFiles } from 'selfstore';
useDesktopFiles({ readFile, writeFile, stat, exists, save, open });
The path outlives the session, so the app reopens on its file with nothing asked of the user. The whole integration is on desktop shell.
Why this home matters
Every other storage story ultimately asks the user to trust someone’s server. A disk file asks them to trust their own disk. For a local-first app it is the reference implementation of “your data is yours”: auditable with an archive tool, movable on a USB stick, restorable anywhere the format is implemented.
On the advanced store
If you own the data model and drive createLocalStore
directly, the same home is fileTarget from the advanced subpath:
import { fileTarget } from 'selfstore/advanced';
const target = await fileTarget.connect({ kv: cache.kv, fileName: 'my-app.zip' });
await store.attachTarget(target, { password });
// restoreTarget: (kind) => kind === 'file' ? fileTarget.fromSession({ kv: cache.kv }) : null