WebDAV home
WebDAV is the self-hoster’s durable home: a Nextcloud or ownCloud instance, a
NAS, any server speaking the protocol. The user brings a URL and credentials;
the store re-writes one backup file there, encrypted when you give it a
backup password and a readable ZIP otherwise (requireEncryption defaults to
false). For an audience that
already refuses Big-Tech clouds, this is the home that makes your app feel
built for them.
One call
import { selfstore } from 'selfstore';
// Never write a readable backup, whatever a later code path forgets.
const store = await selfstore('my-app', { requireEncryption: true });
const outcome = await store.connectWebdav(
{
url: 'https://cloud.example.com/remote.php/dav/files/ada/backups/my-app.zip',
username: 'ada',
password: appPassword, // the server credential
},
{ password: backupPassword }, // encrypts a new backup from its first write
);
// 'started' | 'merged' | 'manual' | 'cancelled'
Two distinct passwords can be in play, worth naming clearly in your UI: the
server credential in the config (how the store reaches WebDAV), and the
backup password in the second argument, which encrypts the file itself. It
encrypts a new file from its first write and opens one that is already
encrypted. store.protect(...) is not a substitute before connecting: it needs
a connected destination and throws NOT_CONNECTED without one, and called
afterwards it rewrites the backup encrypted, but the first one has already been
written on the server in clear. With requireEncryption: true, a connect
without a backup password throws ENCRYPTION_REQUIRED instead of writing
anything. Without that flag and without a backup password, the server holds a
readable ZIP.
Security posture
- https is enforced. Basic-auth credentials over plain
httpare refused outright (loopback aside), so a misconfigured URL fails fast instead of leaking a credential. - Credentials are sealed at rest. The server config persists locally under a non-extractable device key, which defeats casual inspection of IndexedDB (not code running in your origin). The threat model states both halves.
- Prefer app passwords. Nextcloud and ownCloud issue per-application passwords (Settings, Security); recommend those over the account password.
Sharing: a read-only link as a sync input
A WebDAV share link can also act as a peer: another person publishes their
copy read-only, you attach it, and the merge folds their changes in. That is
how a small group shares a store read-write without anyone granting write
access, and without Google. The peers guide has the model; the
helper is webdavTarget.peer({ url }) from selfstore/advanced.
On the advanced store
Driving createLocalStore yourself:
import { webdavTarget } from 'selfstore/advanced';
const target = await webdavTarget.connect({
kv: cache.kv,
config: { url, username, password },
});
await store.attachTarget(target, { password: backupPassword });