Repository navigation
EN Bridge Backup And Restore
Français · Type: how-to · Audience: admin · Context: Reality Bridge · Status: ready · Applies from: 4.2.5
The Reality Bridge,
localprofile (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: jobsreality,reality-xcheck; CI planreality; 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-bridgesThe Reality Bridge,
distributedprofile (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: jobsbridge-postgres,deployment-smoke; CI planreality; 1 test file · people's passes:bridge-postgres-httpsnot 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).
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.
- A Bridge on a SQL store (SQLite or Postgres). The JSON-lines journal can be backed up, but not restored: migrate it to SQLite first (Stores).
- From 4.1.20: backup schema 4 (ADR 0023), the two restore modes, and the maintenance leases. Schema 3 came with
4.1.19 (ADR 0021). A restore reads schemas 1 to 4. This page applies from 4.2.5 (the
quest-n-click-bridgenames). -
Verified on:
backup,restore --mode=merge(refused, then with--force) andrestore --mode=replace-storewere run on 23 October 2026 on a SQLite store of the gamesignals(one tenant, one signal), macOS, Node 22.14, engine 4.4.0. A large or a Postgres store was not replayed for this page; the rehearsals in CI are named under References.
- The Bridge's folder (
--dir, withconfig.json) and access to its store (the SQLite file, or the Postgres URL inBRIDGE_STOREorBRIDGE_STORE_FILE). - Memory for the file's size, about 10 MB per MiB backed up and 20 MB per MiB restored (the measure below).
- For a restore: every Bridge instance on that store stopped, and every instance older than 4.1.20 stopped before the first restore (they take no maintenance lease).
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.
-
Back up the store:
# on the Bridge's machine, in the engine repository npm run bridge -- backup --out=bridge-2026-10-23.jsonIt 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-storefor a SQL store,tenants-onlyfor 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-signatureThe 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-boundsA 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).
- To restore, stop every
serveon 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.
-
Restore with
--mode=merge: into an empty store, or with--forceover 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--forcethe 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.
-
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.jsonEvery 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-transactionsThe 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).
-
Check the store, then start the Bridge again:
# in the engine repository npm run bridge -- doctor
Guarantee ·
bridge-restore-names-its-mode· referenced · scope self-checked by the author (AI), not reviewed Without--mode, or with--forcealone,restorestops 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.
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.
| Message or exit code | Cause | Fix |
|---|---|---|
exit 2, say the mode: --mode=merge … --mode=replace-store …; nothing restored
|
No --mode
|
Name the mode |
the store already holds tenant(s) …: restore into an empty store, or --force; nothing restored |
A conflict in merge
|
A 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 Bridge |
The engine is older than the backup's | Upgrade the Bridge |
the JSON-lines journal cannot be restored all or nothing: migrate it to SQLite first … |
The store is the journal |
migrate --from=jsonl --to=sqlite, then restore with --store=sqlite
|
the backup would be N bytes, more than M (--max-bytes=): no file written |
A store too large for one file | The database's own tools |
A refusal naming the lease's holder ; nothing restored
|
A Bridge still serves the store | Stop it |
exit 3, P0: the restore committed but …
|
A difference found after the commit | Stop 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 commit |
doctor, and take a backup, to see which way it went |
-
backup: the--outfile only (mode 600). It reads the store. -
restore --mode=merge: the tenants, runs, daily records and quotas the file holds (with--force, those it clashes with are replaced). The rest of the store stays. -
restore --mode=replace-store: every table butschema_migrations; and the--safety-outfile, written first.
- A failed restore says "nothing restored" and leaves the store as it was (one transaction).
- After a
replace-store, the way back is its safety file:restore --from=<safety file> --mode=replace-store …with a new--safety-out. -
An instance killed or crashed: start another; nothing is lost (a transaction is committed or not there;
tests/bridge-fanout.test.tskills one of three during 1 000 proposals). -
The database lost: restore the last backup into a new one, then
doctor. Connectors send again what they sent since (theirdedupeKeymakes it idempotent), and players keep their cursors. -
A journal that will not start:
doctornames the line. - A key or a Biscuit root compromised, or an instance's host: Security.
-
GET /livez: the process answers.GET /readyz: the store answers (503 otherwise).GET /healthz: both, with the tenants, the store's kind and the open streams. No token, no tenant's data. - Measures: acceptances, duplicates, refusals by code, acknowledgements, store retries, quarantine, refused streams,
the backlog and a proposal's latency. Kept in the process always; through OpenTelemetry's metrics API when
@opentelemetry/apiis installed beside the Bridge, exported over OTLP whenOTEL_EXPORTER_OTLP_ENDPOINTis set and@opentelemetry/sdk-nodeis installed. Attributes: the tenant and a code, never a player or a payload. - A stored row that no longer verifies is quarantined, never delivered, and
doctorlists it.
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-memoryPeak 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-memoryjob (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.
From schema 5 (migration 0005), every writer holds a lease in the store itself (ADR 0023):
-
serveand every command that writes (migrate,tenant delete,backupandtenant export, which bring the schema up) hold aservinglease, side by side. -
restore, andmigrate --schema=Ngoing down, take theexclusiveone. They are refused, naming who holds the store and until when, while anything else holds a live lease: at once on SQLite; on Postgres after the advisory lock's--lock-timeoutat most (30 s by default, never waited for longer). - An expired lease (a process killed) is deleted, never in the way. Every time is the database's own.
- A
servewhose lease is lost (not renewed, or its lock's connection dropped) holds its writes, which answer 503, and fails/readyzuntil it takes a lease again; so does a process that stalls past two thirds of its lease, on its own clock. A restore whose lease is lost before its commit is rolled back.
Limit ·
bridge-leases-older-bridgesA Bridge older than 4.1.20 takes no lease: stop every older instance before the first restore.migrate --schema=Nbelow 5 drops the lease table with its first step, so stop every writer for it.
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.
- The code:
bridge/src/backup/,bridge/src/cli-store.ts,bridge/src/maintenance.ts. - The design: ADR 0021 (one snapshot, all or nothing), ADR 0023 (completeness, the modes, the leases).
- Rehearsed by
tests/bridge-backup.test.ts,tests/bridge-backup-v4.test.ts,tests/bridge-maintenance.test.tsandtests/bridge-ops.test.ts; the whole topology destroyed and restored bynpm run ops:qualify(The reference deployment). - The Bridge's command line · Troubleshooting.
Generated publication · publication générée — current documentation · documentation courante: quest'n'click 4.4.0 r1, documentation revision · révision de la documentation 8dc717c69acb62c4742b69c54a7e7dfb620f75d8.
Earlier versions and permalinks · versions précédentes et liens permanents: https://wanoo.github.io/quest-n-click/docs/
An edit made in this wiki is overwritten at the next publication · une modification faite dans ce wiki est écrasée à la publication suivante.
Edit the source · Modifier la source · Report an error · Signaler une erreur
Index (6)
Start (6)
Play (9)
Install (17)
- Requirements
- Install from the release archive
- Install from npm, once published
- Install from the repository to work on the engine
- doctor: checking your machine
- Image toolchain
- Audio toolchain: ffmpeg, fluidsynth, Furnace, uv
- Upgrade
- Upgrade notes: 4.4
- Upgrade notes: 4.3
- Upgrade notes: 4.2
- Upgrade notes: 4.1
- Upgrade notes: v3
- Upgrade notes: v2 to v3
- Coming from web-scumm
- Package reference
- Install troubleshooting
Create a game (55)
- Your first game
- Layout of a created project and of the engine repository
- The working loop
- Content principles and the identity contract
- Rooms
- Geometry: placement, walk zones, masks, layers, camera
- Something is visible but unreachable
- Reactions
- Game commands
- Conditions
- Flags, variables and events: which to use
- Custom code
- Characters
- Items and inventory
- World map
- Dialogues
- Scripts and events
- Moving characters: patrols and walks
- Exits
- Chapters and objectives
- State and saves: persistent or temporary, renaming an id, migrations
- Staging: wide rooms and animations
- The stage: lights, particles, floors
- Skin and fonts
- Title and credits
- Heroes: several characters and hero choice
- Built-in minigames
- A custom minigame
- Translations
- Music in stems
- Sound effects
- Voices: per language and per hero
- Images: prompts, cutting sheets, transparency, scale
- Asset provenance
- The storyboard, source of truth
- Puzzle design
- Classic mechanics
- Review pages
- DSL reference
- Checking your game
- What the solver proves
- Solver options
- Playtests
- Remix
- Speedrun for authors
- The sealed ending
- Production template
- Example games
- Working with an AI
- Build and release a game
- Publish on GitHub Pages
- The static game on Clever Cloud
- Other static hosts
- Sub-path, cache and service worker, incomplete deploys, going back
- Testing on a phone over the LAN
Local Studio (24)
- The local Studio: overview and launch
- Edit a room
- Placement and stage
- Preview, apply, undo
- Storyboard
- Assets and cutting
- Check: validator, solver, critical path, heat, health, coverage
- Before and after captures
- Semantic diff
- Play and replay a session
- Rule explainer
- Language and objectives
- Voices and the heroes × languages matrix
- Map
- Notes
- Remix in the Studio
- Music
- The Assistant: bring your own key
- Demo mode and studio-apply
- Local and LAN security
- Studio API
- Local MCP: setting up each client
- MCP tools
- Studio troubleshooting
Team Studio (21)
- What runs where
- Concepts: revision, transaction, span, lease, preview, build
- Starting as an invited dev
- Working together day to day
- Roles and actions: the full matrix
- Invitations
- Managing members
- Write refused on the hosted Studio: stale base revision (409)
- Write refused on the hosted Studio: span locked (423)
- Recovering after a lost lease, a disconnection or a stale preview
- Comments
- Map and semantic diff
- Publishing builds
- Remote MCP
- The game in Git
- Upgrading the engine of a hosted game
- Hosted assets
- The server's command line
- Health and metrics routes
- Trying it locally with compose.yml
- Known limits
Clever Cloud (23)
- Clever Cloud: overview, cost and prerequisites
- Apps and add-ons
- Domains and TLS
- Keycloak
- Generic OIDC and GitLab
- Configuration
- First deployment
- Backups
- RPO and RTO
- Restore the Studio on a fresh installation
- Redeploys
- Upgrading the server and checking state after a migration
- Limits of going back
- Monitoring
- Security
- Rotating keys
- Recovering admin access
- Changing domain or OIDC provider
- Worker, PostgreSQL or Cellar down
- Exporting the game and leaving the installation
- Retention
- Clever Cloud troubleshooting
- Costs and teardown
Reality Bridge and connectors (16)
- The Reality Bridge: what it is
- Quick start
- Guarantees
- Install and run the Bridge
- Backup and restore
- Several tenants
- Signals and the manifest
- The Bridge's command line
- Connectors overview
- Email connector
- Telnet connector
- SSH connector
- Open Badges connector
- The connector SDK
- Security: Biscuit and signatures
- Bridge and connector troubleshooting
Contribute (25)
- Welcome
- Developer setup
- The dev loop
- Before a pull request
- Conventions
- Writing documentation
- Architecture
- Code tour
- Import boundaries
- Which tests run when
- Mutation testing
- The coverage ratchet
- The quality baseline
- CI explained
- Field passes
- Benchmarks
- Releasing
- npm packages
- Decisions and ADRs
- Governance
- Bot pull requests
- Contributing with AI agents
- Security policy
- Code of conduct
- Licences and credits