Repository navigation
EN Clever Configuration
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:clever-env-prints-values
The Studio Server reads a JSON configuration, which holds no secret, and a few secrets, each from a variable or a
file. This page sets both on the two Clever Cloud apps, and is the reference of STUDIO_CONFIG_JSON, field by field.
The studio app and the worker app carry every variable they need to build and start: the run commands, the build hooks, the configuration on one line, and the secrets, set from files so that no value is typed or shown.
- A hosted Studio on Clever Cloud, deployed from the engine repository, from 4.2.3 (
STUDIO_METRICS_TOKEN,STUDIO_BACKUP_CRONandgitarrived in 4.2.3). -
Verified on: 19 October 2026, by the reference deployment (engine at commit 52e68932, then c66fbf56, package.json
4.2.2 before the 4.2.3 tag): these variables, with
STUDIO_METRICS_TOKENandSTUDIO_BACKUP_CRON=1set, and three*.cleverapps.ionames. This page's exact commands are not verified on a live installation.
- Apps and add-ons, Domains and TLS, and the provider: Keycloak or Generic OIDC.
- On your machine: the engine repository with
npm cidone,openssl, and an empty folder kept out of Git for the secrets (~/secrets/my-game/below).
On your machine, in the engine repository where both apps are linked under their alias (Apps and add-ons).
Limit ·
clever-env-prints-valuesclever envprints every value, multi-line ones included (a PEM key spans many lines). Never paste its output anywhere; filter it to names:clever env --alias my-game-studio | grep -oE '^[A-Z][A-Z0-9_]*='.
-
Make the job keys. The worker only runs jobs the Studio signs with its private key:
# on your machine, in the engine repository mkdir -p ~/secrets/my-game && npm run studio-server -- worker-keys --out ~/secrets/my-game
studio-worker.key(0600) is the Studio's,studio-worker.pubthe worker's. The command refuses to overwrite them, and never prints them.
-
Make the session key and, if you want
/metrics, its token:# on your machine openssl rand -base64 48 > ~/secrets/my-game/session-key && chmod 600 ~/secrets/my-game/session-key openssl rand -base64 32 > ~/secrets/my-game/metrics-token && chmod 600 ~/secrets/my-game/metrics-token
-
Set the studio app's plain variables:
# on your machine, in the engine repository clever env set --alias my-game-studio CC_NODE_DEV_DEPENDENCIES install clever env set --alias my-game-studio CC_POST_BUILD_HOOK "npm run studio-client" clever env set --alias my-game-studio CC_RUN_COMMAND "npm run studio-server -- serve" clever env set --alias my-game-studio CC_HEALTH_CHECK_PATH /startz clever env set --alias my-game-studio STUDIO_BACKUP_CRON 1
-
Set its secrets from their files. A key's text begins with
-----BEGIN, whichclever env setreads as an option unless--comes first:# on your machine, in the engine repository clever env set --alias my-game-studio -- STUDIO_WORKER_KEY "$(cat ~/secrets/my-game/studio-worker.key)" clever env set --alias my-game-studio -- STUDIO_SESSION_KEY "$(cat ~/secrets/my-game/session-key)" clever env set --alias my-game-studio -- STUDIO_METRICS_TOKEN "$(cat ~/secrets/my-game/metrics-token)"
STUDIO_OIDC_CLIENT_SECRETwas set with the provider (Keycloak). The database and Cellar credentials arrive from the add-ons (POSTGRESQL_ADDON_URI,CELLAR_ADDON_KEY_ID,CELLAR_ADDON_KEY_SECRET): set nothing for them.
-
Write the configuration in a file, check it, and set it on one line as
STUDIO_CONFIG_JSON:{ "publicUrl": "https://studio.example.fr", "playUrl": "https://play.example.fr", "game": "my-game", "workDir": "/home/bas/work", "host": "0.0.0.0", "trustProxy": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"], "store": { "kind": "postgres" }, "blobs": { "kind": "s3", "host": "cellar-c2.services.clever-cloud.com", "bucket": "my-game-studio" }, "oidc": { "issuer": "https://keycloak.example.fr/realms/web-scumm", "clientId": "studio", "audience": "studio-api", "cliClientId": "web-scumm-cli" }, "worker": { "url": "https://worker.example.fr", "instance": "my-game-studio" } }# on your machine, in the engine repository, the file above saved as ~/secrets/my-game/studio.config.json clever env set --alias my-game-studio STUDIO_CONFIG_JSON "$(node -p 'JSON.stringify(JSON.parse(require("fs").readFileSync(process.argv[1], "utf8")))' ~/secrets/my-game/studio.config.json)"
The repository's
ops/studio/studio.config.example.jsonis the same example. The configuration holds no secret.
-
Set the worker app's variables.
WORKER_INSTANCEis the configuration'sworker.instance:# on your machine, in the engine repository clever env set --alias my-game-worker CC_NODE_DEV_DEPENDENCIES install clever env set --alias my-game-worker CC_RUN_COMMAND "npm run studio-worker" clever env set --alias my-game-worker CC_POST_BUILD_HOOK "python3 -m pip install --target .python --require-hashes --only-binary=:all: -r ops/studio/worker-requirements.lock" clever env set --alias my-game-worker WORKER_INSTANCE my-game-studio clever env set --alias my-game-worker -- WORKER_PUBLIC_KEY "$(cat ~/secrets/my-game/studio-worker.pub)"
The hook installs the asset tools' Python packages (Pillow, NumPy, SciPy), every hash checked, wheels only.
Measure ·
worker-hookThe worker's install, run by hand on the worker app: 7.0 s, 226 MB installed, no compilation. Not measured during a real Clever build at the time; the reference deployment then saw the hook take about 7 s in a worker deploy of 36 s.
- date: 2026-10-19
- version: 4.2.1
- volume: Pillow 12.3.0, NumPy 2.4.6, SciPy 1.18.1 (one run, pip cache empty)
- conditions: worker app of spike S, Clever Cloud test organisation, region par, size XS; installed into a tmpfs folder over SSH
- source: QUALIF-4.2.2-ASSETS.md §2
The names, never the values:
# on your machine, in the engine repository
clever env --alias my-game-studio | grep -oE '^[A-Z][A-Z0-9_]*='
clever env --alias my-game-worker | grep -oE '^[A-Z][A-Z0-9_]*='The studio lists the variables of steps 3 to 5, STUDIO_OIDC_CLIENT_SECRET and the add-ons'; the worker lists those
of step 6 and no add-on variable. The configuration is checked for real when the server starts: a refused one stops it
with a line naming the field (First deployment).
| Symptom | Cause | Fix |
|---|---|---|
clever env set says an option is unknown |
A PEM value without -- before the name |
Step 4's form |
<NAME>: the secret is empty or too short (8 characters at least) |
An empty file, or a wrong path in $(cat …)
|
Check the file |
a session key is 32 characters or more |
A short STUDIO_SESSION_KEY
|
openssl rand -base64 48 |
STUDIO_METRICS_TOKEN: 32 characters at least (openssl rand -base64 32) |
A short token | Step 2 |
STUDIO_CONFIG_JSON: unknown key "…" |
A typo, or a field of a newer version | The field list below |
trustProxy is required when the server is reachable beyond the loopback |
trustProxy left out with OIDC |
Keep it |
<NAME>_FILE (…) is readable by others: chmod 600 |
A secret file with a loose mode (outside Clever) | chmod 600 |
-
~/secrets/my-game/: the keys and tokens (0600), the configuration file. - The two apps' variables in Clever Cloud. A variable change applies at the next deploy or restart.
clever env rm <NAME> --alias <app> removes a variable. Changing a secret later is Rotating keys.
- The secrets are
STUDIO_DATABASE_URL,STUDIO_S3_KEY_ID,STUDIO_S3_KEY_SECRET,STUDIO_OIDC_CLIENT_SECRET,STUDIO_SESSION_KEY,STUDIO_WORKER_KEY,STUDIO_METRICS_TOKENandSTUDIO_GIT_KEY. - Each comes from
<NAME>_FILE(a file of mode 0600, preferred outside Clever; a looser mode is refused), else from<NAME>, else, for the first three, from the add-on's own variable (POSTGRESQL_ADDON_URI,CELLAR_ADDON_KEY_ID,CELLAR_ADDON_KEY_SECRET). - At start the server reads each one once, then removes every variable that held it from its environment: no child process inherits it, and it is never logged. A value shorter than 8 characters is refused.
-
STUDIO_WORKER_KEYmay have its line breaks written\n.STUDIO_SESSION_KEYmay hold several keys, comma-separated, to rotate (Rotating keys). - The worker reads
WORKER_PUBLIC_KEY(orWORKER_PUBLIC_KEY_FILE),WORKER_INSTANCEandPORT, andWORKER_PYTHON_PATHwhen the Python packages are not in.python.
Each variable has its entry in Environment variables.
In order: --config <file> on the command line; else the file STUDIO_CONFIG names; else STUDIO_CONFIG_JSON, the
JSON itself (what a platform without files, such as Clever Cloud, uses). Without any, a command says usage: studio-server <serve|import <dir>|export <dir>> --config <file> (or STUDIO_CONFIG_JSON). In clever ssh, the shell
has the app's variables, so no command needs --config.
An unknown top-level key is refused at start (unknown key "…"). Inside a field, an unknown sub-key is ignored.
| Field | Required | Value | Default |
|---|---|---|---|
publicUrl |
yes | the Studio's origin, http(s); the only Origin a cookie mutation may carry |
— |
playUrl |
yes | the game's origin, http(s); must differ from publicUrl's origin |
— |
game |
yes | the game's id (letters, digits, _, -); one game per instance |
— |
workDir |
yes | the instance's working folder (the game materialised, scratch), ephemeral on Clever Cloud: /home/bas/work
|
— |
port |
no | the port; leave it out on Clever Cloud, which gives PORT (a port here would override it) |
PORT, else 8080 |
host |
no | the address listened on; without oidc, only the loopback is accepted |
0.0.0.0 |
trustProxy |
with oidc beyond the loopback |
addresses or IPv4 networks whose X-Forwarded-For / X-Forwarded-Proto are believed |
[] |
store.kind |
yes |
postgres (hosted) or sqlite (local, with store.file) |
— |
blobs.kind |
yes |
s3 (with host, bucket) or fs (with dir); s3 with a worker |
— |
oidc |
for a shared Studio | the provider (its fields); without it, the local server on the loopback | none |
sessions.idleHours, sessions.absoluteHours
|
no | a browser session's idle and absolute lifetimes | 8 and 24 |
worker.url, worker.instance
|
with oidc
|
the worker's URL, and a name of 3 to 64 letters, digits, _ or - (the worker's WORKER_INSTANCE) |
— |
clientDir |
no | the built Studio; with oidc the server refuses to start without it |
.cache/studio-client |
publicDir |
no | the game's prepared images and sounds (npm run assets writes them, they are committed); the Studio shows them to members and every preview plays them; read only on a hosted Studio |
public/ of the deployed repository |
publicBuilds |
no |
true: the published builds (/play/ on the game's origin) are open to anyone without a session, with the icons their page names (D42, the owner's choice) |
false |
git |
no | the game's Git repository: remote (no credentials in it; the deploy key is STUDIO_GIT_KEY), branch (main), path (a folder of the repository), committer {name, email}, knownHosts (without it, trusted on first use) |
none |
The private ranges of the example (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) are an assumption: Clever's
proxies sit in them, so each person is told apart. They are to be narrowed to Clever's actual proxies by the
studio-hosted pass. Until then, an address in those ranges could claim any X-Forwarded-For, which matters for the
per-address rate limits.
- The loader:
studio-server/src/server/config.ts; the example:ops/studio/studio.config.example.json; the commands:studio-server/src/server/main.ts. - Environment variables; ADR 0027 (secrets in variables on Clever).
- Next: First deployment.
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