Repository navigation
EN Clever Key Rotation
Français · Type: how-to · 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:builds-old-key
How to replace each secret of a hosted Studio, and what each change does to the people logged in: the session key, the worker's job key, the provider's client secret, the Git deploy key and the metrics token.
A new secret in place, the old one retired, and no one logged out unless you choose to (a compromised session key).
- A hosted Studio on Clever Cloud, from 4.2.3 (
STUDIO_GIT_KEYandSTUDIO_METRICS_TOKENarrived in 4.2.3). - Verified on: not verified on a live installation. No rotation was exercised on the reference deployment. The effects on sessions are read from the code; the session key's are backed by the test named below.
-
Configuration done, the current secrets in
~/secrets/my-game/(0600). -
openssl, and the engine repository withnpm cidone.
On your machine, in the engine repository where both apps are linked. A variable change applies at the next deploy or
restart, and a redeploy is the overlap of Redeploys: plan its minute of slow writes. Never show
clever env's output: it prints values (Configuration).
The session cookie is a random token stored by its hash: it does not depend on this key. The key seals each session's refresh token and id_token (AES-256-GCM). It is a ring, comma-separated: the first key seals, any key opens.
-
Make a new key and put it first, the old one after:
# on your machine, in the engine repository openssl rand -base64 48 > ~/secrets/my-game/session-key.new && chmod 600 ~/secrets/my-game/session-key.new clever env set --alias my-game-studio -- STUDIO_SESSION_KEY "$(cat ~/secrets/my-game/session-key.new),$(cat ~/secrets/my-game/session-key)" clever restart --alias my-game-studio
Nobody is logged out. New logins are sealed with the new key; existing sessions still open with the old one.
-
After the sessions' absolute lifetime (24 h by default,
sessions.absoluteHours), every session sealed with the old key has ended. Remove it from the ring, and keep the new file as the current one:# on your machine, in the engine repository mv ~/secrets/my-game/session-key.new ~/secrets/my-game/session-key clever env set --alias my-game-studio -- STUDIO_SESSION_KEY "$(cat ~/secrets/my-game/session-key)" clever restart --alias my-game-studio
If the key leaked, skip the ring: set the new key alone and restart. Each session's next check by the provider (within 5 min) cannot open its refresh token, and the session ends: the member sees "the session can no longer be checked: log in again" (401) and logs in again. The Bearer tokens of the CLI and the MCP are not sealed with this key and are not affected.
Guarantee ·
unreadable-session-ends· referenced · scope self-checked by the author (AI), not reviewed A session whose sealed refresh token can no longer be opened (its key left the ring) ends at its next check, with a 401, rather than living unchecked to its absolute lifetime.
The Studio signs every job with the private key; the worker accepts only jobs its public key checks. The worker holds one public key at a time, so there is a window during which jobs are refused.
-
Make a new pair in a new folder (the command refuses to overwrite a key):
# on your machine, in the engine repository mkdir -p ~/secrets/my-game/keys-new && npm run studio-server -- worker-keys --out ~/secrets/my-game/keys-new
-
Set both variables, then deploy the worker and the studio one right after the other:
# on your machine, in the engine repository clever env set --alias my-game-worker -- WORKER_PUBLIC_KEY "$(cat ~/secrets/my-game/keys-new/studio-worker.pub)" clever env set --alias my-game-studio -- STUDIO_WORKER_KEY "$(cat ~/secrets/my-game/keys-new/studio-worker.key)" clever restart --alias my-game-worker clever restart --alias my-game-studio
Between the two, the worker refuses the old Studio's jobs (403): writes that need a check are refused with a 503 ("the edit cannot be judged now: …"), previews and the health fail and are tried again. Sessions are not affected.
Limit ·
builds-old-keyThe published builds keep the signature made with the old key; their manifest names that key by its id (a hash of the public key). Keep the oldstudio-worker.pubto check builds published before the rotation.
-
Regenerate the
studioclient's secret at the provider (in Keycloak: the client's Credentials tab), save it to the 0600 file, set it, and restart the studio at once:# on your machine, in the engine repository clever env set --alias my-game-studio -- STUDIO_OIDC_CLIENT_SECRET "$(cat ~/secrets/my-game/oidc-client-secret)" clever restart --alias my-game-studio
Until the restart, new logins fail. The provider then answers the sessions' checks
invalid_client, which the Studio treats as its own misconfiguration, not the user's: a session lives on up to R (15 min) after its last check, then is suspended (503) until the secret matches again. Nobody is logged out once the restart is done in time.
-
Add a new deploy key to the game's repository at your Git host, set it, restart, then remove the old key at the host:
# on your machine, in the engine repository (ssh-keygen writes the pair; the .pub goes to the Git host) ssh-keygen -t ed25519 -N '' -C my-game-studio -f ~/secrets/my-game/git-deploy-key-new clever env set --alias my-game-studio -- STUDIO_GIT_KEY "$(cat ~/secrets/my-game/git-deploy-key-new)" clever restart --alias my-game-studio
No effect on sessions. A push or an import during the switch may fail and is run again.
- Make a new token (32 characters or more), set it, restart, and give it to your scraper. Between the restart and the
scraper's update,
/metricsanswers 401: expect a gap in the graphs.
- Session key: members keep working after step 1; after step 2, a session older than the ring's change would have ended anyway.
- Worker key: a write that needs a check goes through again, and a preview builds; the worker logs no
worker-job-refused. - OIDC secret: a new login works;
/auth/meanswers. - Git key: a push from the Studio succeeds.
- Metrics token: the scraper's target is up again.
| Symptom | Cause | Fix |
|---|---|---|
a session key is 32 characters or more at start |
A short key in the ring | openssl rand -base64 48 |
| Everyone logged out within 5 min | The old session key removed at once (no ring) | Expected for a leaked key; otherwise use step 1 first |
| Writes 503 "the edit cannot be judged now: the processing worker refused the job (403…)" | The worker and the studio hold different keys | Finish step 4 on both apps |
| 503 "the identity provider cannot be reached: the session is suspended" | The OIDC secret changed at the provider, not yet in the Studio for more than R | Set the new secret and restart |
clever env set reads the key as an option |
No -- before the name |
The commands' form |
The apps' variables; the key files in ~/secrets/my-game/; at the provider and the Git host, the client secret and
the deploy keys. Sessions end only in the leaked-key case.
Each step is undone by setting the previous value back and restarting, as long as you kept its file. A session key removed from the ring cannot be brought back for the sessions it sealed: they have ended.
- The ring:
studio-server/src/server/auth/store.ts(Sealer); the checks:studio-server/src/server/auth/gate.ts; the refresh errors:studio-server/src/server/auth/oidc.ts; the job keys:studio-server/src/worker/job.ts; the builds' signature:studio-server/src/server/builds.ts. -
STUDIO_SESSION_KEY,STUDIO_WORKER_KEY,STUDIO_GIT_KEY,STUDIO_METRICS_TOKEN. - The test behind the guarantee:
tests/studio-auth.test.ts.
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