quest'n'click 4.5.0 · r1

Built for quest'n'click 4.5.0, revision r1. Whether it is published, and which one is current, is recorded at publication.json.

Backup and restore

Français · Type: how-to · Audience: admin · Context: Reality Bridge · Status: ready · Applies from: 4.2.5

The Reality Bridge, local profile (SQLite) · contract: stable · verified by CI (support matrix of 4.4.0, 2026-10-23): Node 22.13+ with node:sqlite, on Ubuntu; Chromium and WebKit end to end · automated checks: jobs reality, reality-xcheck; CI plan reality; 2 test files · people's passes: none · known limits: bridge-backup-digest-not-signature, bridge-backup-bounds, bridge-replace-store-two-transactions, bridge-leases-older-bridges

The Reality Bridge, distributed profile (Postgres) · contract: experimental · verified by CI (support matrix of 4.4.0, 2026-10-23): Postgres 16 in CI; The qualification profile: HTTPS, three instances (ops/qualify/) · automated checks: jobs bridge-postgres, deployment-smoke; CI plan reality; 1 test file · people's passes: bridge-postgres-https not run (#77) · known limits: bridge-backup-digest-not-signature, bridge-backup-bounds, bridge-replace-store-two-transactions, bridge-leases-older-bridges

How to back up a Reality Bridge's store to one file, restore it all or nothing, and get a Bridge back after an instance or its database is lost. This is the Bridge's backup (npm run bridge -- backup), not the hosted Studio's (studio-server backup, Backups on Clever Cloud).

What you get

A backup file of the whole store taken in one snapshot, with a digest that catches a file changed or cut by accident; and, when you need it, a store restored from that file in one transaction, checked before and after its commit, in a mode you name.

Context and versions

Prerequisites

Where to run the commands

On a machine that reaches the store, with the Bridge's folder: the Bridge's own machine, or one with more memory (a restore only needs to reach the store). npm run bridge -- … in the engine repository; npx quest-n-click-bridge … with the standalone package.

Steps

  1. Back up the store:

    # on the Bridge's machine, in the engine repository
    npm run bridge -- backup --out=bridge-2026-10-23.json
    

    It prints ✔ 1 tenant(s), 1 signal(s), 0 run(s), 0 daily record(s) backed up to …/bridge-2026-10-23.json (schema 4, one snapshot, sha256 1edef3c0f48b…). The whole store is read in one read-only transaction: tenants, runs, daily records and the speedrun quota. The file is mode 600, written to a temporary file then renamed. It holds the hashes of capabilities, never a capability or a key.

    Its envelope gives the backend, the Bridge and SQL schema versions, what it holds (completeness: whole-store for a SQL store, tenants-only for the journal or memory), the start and end, the counts per family, and a SHA-256 of all of it (scope: envelope).

Limit · bridge-backup-digest-not-signature The digest is an integrity check against a file changed or truncated by accident, not a signature: whoever can change the file can compute it again. Keep backups where only you can write.

Limit · bridge-backup-bounds A store above --max-rows (2 000 000) or a file above --max-bytes (256 MiB) is refused and no file is written. Past a few hundred MiB, use the database's own tools (pg_dump, the provider's snapshots, a copy of the SQLite file with the Bridge stopped).

  1. To restore, stop every serve on that store first. From 4.1.20 the store refuses a restore while anything holds a live lease, naming who holds it and until when; a restore is never run beside live writers.
  1. Restore with --mode=merge: into an empty store, or with --force over the tenants the file holds.

    # on a machine that reaches the store, in the engine repository
    npm run bridge -- restore --from=bridge-2026-10-23.json --mode=merge          # into an empty store, or refused
    npm run bridge -- restore --from=bridge-2026-10-23.json --mode=merge --force  # the file's tenants replaced, the others kept
    

    A conflict (a tenant, a run or a quota of its tenants, a daily key) refuses it without --force: ✖ the store already holds tenant(s) default: restore into an empty store, or --force; nothing restored. With --force the file's families replace what they clash with, and what the file does not speak of stays (another tenant, another daily key). Any schema, 1 to 4.

  1. Or restore with --mode=replace-store: the store becomes the file.

    # on a machine that reaches the store, in the engine repository
    npm run bridge -- restore --from=bridge-2026-10-23.json --mode=replace-store --yes-replace-store --safety-out=before-restore.json
    

    Every table but the control ones (schema_migrations) is emptied in the same transaction, then the file is written and read back, nothing more held. It takes only a schema 4 backup of a whole SQL store, of this store's SQL schema. First, a backup of the store being replaced is written to --safety-out, a path that must not exist (no link either), read back and checked; if that fails, nothing is deleted. Its path is said in every outcome.

Limit · bridge-replace-store-two-transactions The safety backup and the replacement are two transactions: stop every writer between them (the maintenance lease refuses them from 4.1.20 on). The safety file is written through a hard link, which never replaces a name: give it a path on a file system that has them (not FAT or exFAT).

  1. Check the store, then start the Bridge again:

    # in the engine repository
    npm run bridge -- doctor
    

Guarantee · bridge-restore-names-its-mode · referenced · executed on aff6caab7514 · scope self-checked by the author (AI), not reviewed Without --mode, or with --force alone, restore stops with exit code 2, and its usage names both modes.

Guarantee · bridge-restore-refused-while-serving · referenced · scope self-checked by the author (AI), not reviewed While a Bridge serves the store, a restore is refused and says nothing was restored. Once the Bridge stops, it restores.

Expected result, and how to check it

restore exits 0 with one line: ✔ 1 tenant(s), 0 run(s), 0 daily record(s), 0 quota row(s) restored from <date> (schema 4, merge), checked before and after the commit (with replace-store, followed by (the store before: <safety file>)). The file was read bounded and checked whole before the store was opened (schemas 1 to 4; every row, duplicates, relations, schema 3's counts and payload digest, schema 4's envelope digest, dates, version and completeness), written in one transaction, read back and compared before the commit, then audited after it. doctor then lists each tenant's players and signals, with no gap.

Common errors

Message or exit codeCauseFix
exit 2, say the mode: --mode=merge … --mode=replace-store …; nothing restoredNo --modeName the mode
the store already holds tenant(s) …: restore into an empty store, or --force; nothing restoredA conflict in mergeA new store, or --force
replace-store needs a schema 4 backup of a whole SQL store (…)An older file, or one from the journal (tenants-only)--mode=merge, or a new backup
the backup comes from a store at schema N, newer than this one (M): upgrade the BridgeThe engine is older than the backup'sUpgrade the Bridge
the JSON-lines journal cannot be restored all or nothing: migrate it to SQLite first …The store is the journalmigrate --from=jsonl --to=sqlite, then restore with --store=sqlite
the backup would be N bytes, more than M (--max-bytes=): no file writtenA store too large for one fileThe database's own tools
A refusal naming the lease's holder ; nothing restoredA Bridge still serves the storeStop it
exit 3, P0: the restore committed but …A difference found after the commitStop every instance, keep this store for inspection; a writer during the restore may explain it
exit 3, the commit's answer was lost …The connection dropped at the commitdoctor, and take a backup, to see which way it went

Files or data changed

Recovery or going back

Health and measures

Memory: size the machine to the backup

backup, tenant export and restore hold the whole file in memory, parsed and canonical; a restore holds it twice (the file, then the store read back to compare).

Measure · bridge-backup-memory Peak memory (RSS) of the archive's Bridge, against about 72 MB at rest: a 64 MiB backup took 0.6 GB to export, 1.4 GB to restore on SQLite and 1.3 GB on Postgres; 128 MiB, 1.1 to 1.3 GB, 2.7 GB and 2.4 GB; 256 MiB, 2.1 to 2.2 GB, 3.1 GB and 3.7 GB. Read it as about 10 MB of memory per MiB of file for a backup, 20 MB for a restore: an observation, not a promise for your machine.

  • date: 2026-10-13
  • version: 4.1.20
  • volume: backups of 64, 128 and 256 MiB, on SQLite and on Postgres
  • conditions: the candidate's backup-memory job (tools/backup-memory.ts), a GitHub Linux runner, the Bridge from the candidate's archive; one measure per size and store
  • source: REALITY-OPS.md, "Health, measures, backups"

In a 512 MiB container, a restore holds up to about 20 MiB of backup. Run a larger one from a machine that has the memory, or lower --max-bytes and --max-rows to what the machine holds, so that a file too large is refused before it is parsed.

Maintenance leases (4.1.20)

From schema 5 (migration 0005), every writer holds a lease in the store itself (ADR 0023):

Limit · bridge-leases-older-bridges A Bridge older than 4.1.20 takes no lease: stop every older instance before the first restore. migrate --schema=N below 5 drops the lease table with its first step, so stop every writer for it.

One tenant's rows

tenant export --tenant=<id> [--out=<file>] writes one tenant's rows, in every table (to standard output without --out); tenant delete --tenant=<id> --yes deletes them. Without --yes, nothing is deleted. The backup is logical, portable and bounded; for a large deployment, pg_dump or the provider's snapshots remain the backup of the database itself.

References


Edit the source of this page · Report an error