Repository navigation
EN Clever Backups
Français · Type: how-to + reference · Audience: admin · Context: hosted Studio · Status: ready · Applies from: 4.2.3
The Studio Server (hosted team Studio) · contract: experimental · verified by CI (support matrix of 4.4.0, 2026-10-23): Postgres, S3 and Keycloak in CI; Chromium and Firefox (job studio-hosted); WebKit on macOS, by hand only · automated checks: jobs
studio-hosted,studio-install-smoke; CI planstudioHosted; 3 test files · people's passes:studio-hostednot run (#149),studio-collabnot run (#150),studio-previewnot run (#166),studio-assets-hostednot run (#167),studio-mcp-remotenot run (#196),studio-oidc-genericnot run (#197) · known limits: none listed on this page
A hosted Studio protects its game twice: the journal of every confirmed change is shipped to the bucket as it is written, and a daily backup of the project's tables is written to the same bucket. This page sets up the daily backup, says what a backup holds and does not hold, and how long backups are kept.
A backup backups/<date>-r<revision>.json in the instance's bucket every day at 03:17 UTC, the 14 most recent kept
(or STUDIO_BACKUP_KEEP), its "event":"backup" line in the studio app's logs, and the way to take one by hand
before an upgrade. With the journal shipped since, a backup restores the game to its last shipped revision
(restore drill).
- A hosted Studio on Clever Cloud.
backup <file>andrestoreexist from 4.2.2;backup --to-bucket --keepand the daily cron from 4.2.3. -
Verified on: 19 October 2026, by the reference deployment (engine at commit c66fbf56, package.json 4.2.2 before
the 4.2.3 tag): the cron installed on the studio app (
crontab -lshowed it), the script run by hand on instance 0 without--config, and--keepdeleting the oldest. Not observed: the 03:17 UTC run itself, and whether exactly one backup a day is made.
- A deployed Studio (First deployment) whose configuration has
blobs.kind: "s3". - For a copy outside the bucket: an S3 tool (the Cellar console,
s3cmd,aws s3) and the Cellar credentials.
-
clever env set,clever ssh,clever logs: on your machine, in the engine repository where the studio app is linked. -
npm run studio-server -- backup …: in a shell on the studio app (clever ssh --alias my-game-studio, thencd "$APP_HOME"), where the configuration and the add-ons' credentials are already in the environment.
-
Turn the daily backup on, on the studio app only, never the worker:
# on your machine, in the engine repository clever env set --alias my-game-studio STUDIO_BACKUP_CRON 1 clever env set --alias my-game-studio STUDIO_BACKUP_KEEP 14 # optional; 14 when absent
The repository's
clevercloud/cron.jsonrunsops/studio/backup-cron.shat 03:17 UTC every day. Clever Cloud installs a repository's crons on every app and every instance deployed from it, so the script does nothing unless the app setsSTUDIO_BACKUP_CRON=1, and nothing on another instance than number 0 (INSTANCE_NUMBER). It then runsnpm run -s studio-server -- backup --to-bucket --keep "${STUDIO_BACKUP_KEEP:-14}". A variable change applies at the next deploy or restart.
-
The next morning, find the backup's line in the logs (
clever logs --alias my-game-studio --since 12h, stopped by its process id: it keeps following otherwise), or run the script by hand to see it at once:# in a shell on the studio app (clever ssh --alias my-game-studio) cd "$APP_HOME" && ops/studio/backup-cron.sh
Its line is
{"event":"backup","key":"backups/2026-10-19T19-38-41Z-r63.json","revision":63,"digest":"…","removed":[…]}.
Measure ·
backup-timeThe script run by hand on instance 0: 1.87 s,tsxstart-up included, for a backup of 253,958 bytes at revision 63. A second run at once withSTUDIO_BACKUP_KEEP=3deleted the one backup past 3, in 2.07 s.
- date: 2026-10-19
- version: 4.2.2
- volume: the demo game at revision 63 (500 files); backups of 170,746 bytes (r1) to 253,958 bytes (r63)
- conditions: reference deployment phase B, engine at commit c66fbf56 before the 4.2.3 tag (package.json said 4.2.2); studio app XS, PostgreSQL DEV, Cellar S, region par; one sample each
- source: QUALIF-4.2.3-REFERENCE.md §3
-
Before an upgrade, or any risky operation, take one by hand and note its revision. Without
--keep, nothing is deleted:# in a shell on the studio app (clever ssh --alias my-game-studio) cd "$APP_HOME" && npm run -s studio-server -- backup --to-bucket
backup <file>writes the same backup to a new file instead (mode 0600; it refuses an existing file), on the instance's disk, which does not survive a restart: prefer the bucket on Clever Cloud.
- Copy the bucket outside Cellar yourself, with an S3 tool, to storage only operators can write:
backups/, and alsosha256/(the files' contents) andjournal/(the operations shipped). A backup names its blobs without holding them, and a restore refuses a backup whose blobs are missing: a copy ofbackups/alone restores nothing. The bucket holds all three together: a backup survives the loss of the database, not of the bucket or its credentials.
Guarantee ·
backup-keep-own-only· referenced · scope self-checked by the author (AI), not reviewed--keep ndeletes only backups this command wrote (keysbackups/<date>-r<revision>.json) past the n most recent, the one just written counting as one of them: never another object of the bucket, nor a file of another name underbackups/.
| Holds | Does not hold |
|---|---|
The project's tables, read in one read-only snapshot: the head (studio_files, studio_meta), the confirmed operations (studio_ops), members, users, invitations, the audit log, personal undo, the health verdicts, previews, published builds, prepared sets, comments |
The files' contents: it lists the blobs it names, by hash; they stay in the bucket |
| A digest (sha256 of its tables), checked at restore: an altered or cut backup is refused | Sessions and logins under way: a restore logs everyone out |
| The schema version it was made at: a restore needs the same | The leases (the writer's, the service's, the spans'): nobody holds a span after a restore |
| Operations under way or aborted at the snapshot; the answers kept for a client's retry (7 days); the game origin's tickets and sessions |
A backup is trusted input, like the database's credentials: it can add owners. Its digest says it is whole, not who made it: keep it where only operators write.
Each confirmed operation is copied to the bucket, in order, as an immutable segment
(journal/<epoch>/<line>/<rev>.json; the contents are blobs already). A failure is logged ship-failed and retried
within 30 s. /readyz gives rpoMs, the age of the oldest operation not shipped yet (0 when everything is): the
target is one minute (D42), watched, never promised (RPO and RTO). The line is 0, then new
after each restore: the revisions a restored instance writes again never overwrite the segments of the timeline it
left. Take a new backup after a restore: the next ones replay on the new line.
The procedure is the restore drill; what the command does, in short. It restores a hosted
instance only, into an empty database (a new PostgreSQL add-on, the same bucket):
npm run studio-server -- restore <file> --replay --config <file> checks the digest, the tables and columns, and that
every blob of the head and of the journal is in the bucket (a preview, build or prepared set whose blobs were collected
since is left out and counted as dropped); writes the rows; then replays the journal shipped since the backup. Each
replayed operation must continue the head (its revision, each file's previous content, its blobs); the replay stops
at the first that does not (stopped: gap, not-the-head, blob-missing…). If it fails midway, the server refuses
to start until npm run studio-server -- replay --config <file> resumes it. Then start the server: its folder is made
from the head. Replayed operations keep no answer: a client retrying one gets null. Take a new backup after a
restore: the next ones replay on the new line.
The release archives do not carry ops/studio/backup-cron.sh. Copy it into your app's repository with its last line
replaced by:
# the last line of backup-cron.sh, in your app's repository (run by Clever's cron on the studio app)
exec npx quest-n-click-studio-server backup --to-bucket --keep "${STUDIO_BACKUP_KEEP:-14}"make it executable, and point your own clevercloud/cron.json at it: ["17 3 * * * $ROOT/backup-cron.sh"]. Clever
Cloud runs a cron with no environment unless its script starts with #!/bin/bash -l (the script's first line), and
with absolute paths only ($ROOT is the app's folder).
- A new
backups/…jsonobject each day in the bucket, and its"event":"backup"line in the logs. - No more than
STUDIO_BACKUP_KEEPof them (other objects underbackups/are left alone). - The real check is a restore: run the restore drill at least once, and after each upgrade.
| Symptom | Cause | Fix |
|---|---|---|
No "event":"backup" line one morning |
STUDIO_BACKUP_CRON not 1, not redeployed since, or no INSTANCE_NUMBER=0
|
Troubleshooting |
backup --to-bucket writes to the instance's bucket: blobs.kind is s3 |
A local configuration | Run it on the studio app |
backup: --keep is the number of backups kept (1 or more) |
STUDIO_BACKUP_KEEP is 0, negative or not a whole number |
A whole number from 1 |
backup: a new file is required (or --to-bucket [--keep n]) |
backup with no file, or an existing one |
A new file name, or --to-bucket
|
| Two backups the same minute | An overlapping redeploy at 03:17: two instances both numbered 0 for a moment | Harmless |
Objects under backups/ in the bucket; with --keep, older ones of its own deleted. Nothing in the database.
A backup deleted by --keep is gone, unless you copied it elsewhere (step 4). Restoring a backup is the
restore drill, into a new database.
npm run studio-server -- gc lists the blobs no row names (older than a day) and the worker's staging older than an
hour; --delete deletes them. A blob stored again gets a new date, so a write naming an old blob is covered by the
day of grace. The head's and the journal's blobs are never collected (the journal keeps every operation); an older
backup restored after a collection loses only the previews, builds and prepared sets the collection took (counted as
dropped). Never run gc --delete against a drill's database: it would delete blobs production still names.
-
studio-server/src/ops/backup.ts(what is kept, and why),studio-server/src/ops/backup-bucket.ts,studio-server/src/ops/gc.ts,ops/studio/backup-cron.sh,clevercloud/cron.json. -
STUDIO_BACKUP_CRON,STUDIO_BACKUP_KEEP. - Retention, Restore drill, RPO and RTO.
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