As decided on 2026-10-03:
- Encryption: with Encrypt on, the built-in engines seal what they store -
segments, page deltas, bases, blobs and state - with AES-256-GCM from
Node's crypto. The key is a file the administrator holds outside the
target and everything the stream reads, never in the database: 64 hex
characters, or a passphrase stretched with scrypt and the stream's salt.
stream.json keeps the salt and an HMAC check, so a wrong key is refused
on save and before a restore; blob names are keyed HMACs; a generation
records whether it is encrypted and is refused without its key.
- Cloud upload: the target is mirrored to S3/MinIO, Azure or GCS with the
Attachments storage panes' adapters and credentials. Sealed files go up
once, and an index or generation file again after the files it names, so
the remote copy never lists a segment it lacks; retention removals follow,
a failed upload is retried, and Fetch from cloud fills a lost target.
- SQLite restore on restart: a rebuilt, checked wekan.sqlite is staged in
<db>/continuous-restore with RESTORE_REQUESTED "continuous". The Docker
entrypoint, start-wekan.sh and the Snap keep the live database and its WAL
in continuous-restore/replaced first, then copy the staged one in, before
FerretDB opens its files. A pending request is never overwritten.
Tests: tests/continuousBackupEncryption.test.cjs, tests/continuousBackupCloud.test.cjs,
tests/continuousBackupRestartRestore.test.cjs (each script's real restore
block in a sandbox), encrypted integration tests on a real replica set and
WAL database, the server methods, and the pane in Chromium and WebKit.
Thanks to xet7 !
Admin Panel / Attachments / Continuous backup, beside Backup, streams the
database, filesystem attachments, avatars and logs to a target directory
within seconds, so less work is lost between scheduled archives and a
restore can return to a chosen moment. Design: docs/Backup/Continuous-Backup.md.
The built-in engines use only copyfree code - WeKan's own (MIT), Node.js
and its node:sqlite (SQLite, public domain) - and start no other program:
- oplog: tails local.oplog.rs like Meteor's oplog driver, on a MongoDB
replica set or FerretDB with its oplog; a base archive (the existing
wekan-instance-backup format), then whole documents and deletions,
continued from the saved position after a restart, a new generation
when the oplog has lost it.
- sqlite: SQLite's online backup API on FerretDB's WAL database at an
interval, storing only the pages that changed.
- files: attachments, avatars and logs by watching and by rescans, each
content once under its SHA-256, links never followed.
Litestream (Apache-2.0) is an optional engine for SQLite: WeKan writes its
configuration and supervises a binary the administrator installed.
Segments are written whole, flushed and listed in append-only indexes.
A restore checks every index line, segment and blob first and writes
nothing when one is missing or changed; a database restore replays up to
the chosen moment, a file restore puts files back (add-missing or
replace-all), and a SQLite restore builds a checked file beside the stream,
never over the live one. Site administrators only; failures of the
unattended stream show in Admin Panel / Problems.
Tests: tests/continuousBackup.test.cjs; tests/integration/continuousBackup.test.cjs
against a real replica set, a real WAL database, real files and a stand-in
Litestream; server/lib/tests/continuousBackup.tests.js through the methods;
tests/playwright/specs/admin-continuous-backup.e2e.js on Chromium and WebKit.
Thanks to xet7 !