# Quest Student Deploy Guide

Use this guide when deploying a student browser game to Quest Student Deploy.

Quest hosting is static only: HTML, CSS, JavaScript, and assets.

Uploads are capped at 100 MiB per file, 250 MiB total across allowed files, and
300 files per upload. The runner also uploads a latest-only admin source
snapshot zip capped at 200 MiB after excluding build output, `node_modules`,
and common cache folders.

## What To Deploy

Deploy only the browser output:
- `index.html`
- CSS files
- JavaScript files
- image, audio, font, and other browser assets

Do not deploy:
- `node_modules`
- cache folders
- Express servers
- Socket.IO servers
- Colyseus servers
- custom WebSocket servers
- backend-only source files

If the project uses Vite or another build tool, build first and deploy the browser output folder, usually `dist`.

## Browser Storage

Student games should use normal browser `localStorage` and `sessionStorage` for
small local saves. Quest serves deployed game content from an isolated student
project origin in production, ideally a stable per-project origin, so the game
can use real browser storage without sharing Quest app cookies or Quest app
storage. A stable per-project origin also prevents one game from sharing browser
storage with another game.

All versions of a project are served under the same origin. The version number
is part of the path, such as `/projects/content/student/game/v/1/` and
`/projects/content/student/game/v/2/`, not a separate storage origin. This means
progress saved in version 1 is available when the same player opens version 2,
as long as the game keeps using the same localStorage key.

Quest production should use a content origin that is stable for the project and
does not include the deploy version. A shared content origin can still run the
game safely away from the Quest app, but browser storage is origin-wide, so a
per-project origin is the least error-prone setup for simple keys like
`saveData`, `highScore`, or `settings`.

This is why a storage feature can work when the student opens the game locally
but fail after deploy if it depends on browser storage features outside this
simple storage model. The safe path is to keep saves small and use
`localStorage` directly.

Supported examples:

```js
localStorage.setItem("bestScore", String(score));
const bestScore = Number(localStorage.getItem("bestScore") || "0");

localStorage.playerName = "Ari";
localStorage["save-slot-1"] = JSON.stringify({ level: 3, coins: 25 });
```

Recommended save helper:

```js
const SAVE_KEY = "my-game-save-v1";

export function loadSave() {
  try {
    const raw = localStorage.getItem(SAVE_KEY);
    return raw ? JSON.parse(raw) : null;
  } catch {
    return null;
  }
}

export function saveGame(saveData) {
  try {
    localStorage.setItem(SAVE_KEY, JSON.stringify(saveData));
  } catch {
    // Keep the game running if storage is full or unavailable.
  }
}
```

Good things to save:
- best score
- unlocked levels
- settings
- tutorial completion
- small save slots

Avoid saving large generated maps, images, audio, private information, or data
every animation frame.

Do not rely on IndexedDB, localForage, browser extension storage,
service-worker storage, or storage inside Web Workers unless Quest explicitly
documents support for it.

If a student asks to add or debug saved progress, high scores, settings, save
slots, `localStorage`, or `sessionStorage`, use the Quest local storage skill:
`https://agents.joinquest.com/skills/use-local-storage/SKILL.md`.

If a student asks for an online leaderboard, shared/global high scores,
fastest times, points rankings, money rankings, or a persistent score database
shared across devices, use the Quest leaderboard skill:
`https://agents.joinquest.com/skills/use-leaderboard/SKILL.md`.

## Required Auth

Quest deploy requires a project-specific deploy token. The token is the only project identifier for deploys.

Resolve the token in this order:
1. The student's current message, including a full Quest deploy prompt or command.
2. `.env.local` in the project root, first `QUEST_DEPLOY_TOKEN`, then `STUDENT_DEPLOY_TOKEN`.
3. Existing shell environment variables, first `QUEST_DEPLOY_TOKEN`, then `STUDENT_DEPLOY_TOKEN`.
4. If no token is available, ask the student for the Quest deploy token for this project.

When the student provides a token, write it to `.env.local` in the project root:

```txt
QUEST_DEPLOY_TOKEN=<exact token>
```

If `.env.local` already has `QUEST_DEPLOY_TOKEN` or `STUDENT_DEPLOY_TOKEN`,
replace the old value with the new token so future deploys use the latest
project key. Preserve other `.env.local` entries when possible. Make sure
`.gitignore` contains `.env.local` or `.env*.local` so the token is not
committed or shared. Do not add, infer, fix, or validate a project slug from
the repo, folder, package, or command. Old owner-wide deploy tokens are no
longer valid; if the API says the token is outdated or expired, ask for the
latest project deploy token.

Do not invent placeholder tokens, versions, or URLs. Do not print the token in the final deploy response.

If the student provides an older full Quest deploy prompt or command, use it for the current shell. Preserve build-related arguments such as `--entry`, `--dir`, `--build-dir`, and `--no-build`; project names and slugs are not needed.

## Deploy Command Rules

Choose the command for the current shell automatically.

Run only one deploy command at a time. If the command is still running, wait for it to finish; do not start another deploy in parallel or because output is taking a while.

Run the deploy command once. Do not add `--project`; the deploy token selects the Quest project. If the runner prints `QUEST_DEPLOY_RESULT {"status":"success",...}`, do not rerun the command, even if later source-backup output is slow, noisy, or warning-only.

Canonical URLs use `v1`, `v2`, `v3`, and so on. Friendly release labels and
change logs are optional metadata on top of those canonical versions.

During a normal deploy, publish first. Do not run `--release-preview` unless the
student specifically asks to preview release metadata without publishing.

Do not pause before deploy to write a changelog. Do not pass `--change-kind` or
`--change-summary` during a normal deploy. Quest can publish the game without a
change message.

Do not pass `--release-label` during a normal deploy. Quest automatically uses
the next version number. Only pass `--release-label` if the student explicitly
asks for a specific friendly label like `3.1`.

After the deploy succeeds, make one best-effort metadata update to add a
kid-friendly changelog. Use the separate `edit-version-changelog` skill and the
metadata API. Leave the version label unchanged unless the student explicitly
asked to rename or relabel the version.

Keep that automatic changelog short and scannable: one line for one meaningful
change, or 2-3 separate lines when there are multiple meaningful
player-visible changes. Do not write a paragraph or string together every
changed file.

For that changelog, prioritize what you know from the work you just did, but do
not assume your current-chat edits are the entire deploy. Use the server diff in
the deploy result as a guardrail for files that changed before this chat or
outside your immediate work.

If that post-deploy metadata update fails, do not mark the deploy as failed. The
game is already published. Mention briefly that the changelog update can be
retried later.

If the student later asks to rename a release or edit the change log, use the
same `edit-version-changelog` skill and the metadata API instead of redeploying.

macOS/Linux:

```bash
curl -fsSL "https://app.joinquest.com/student-deploy/runner.js" | node - --api-base "https://app.joinquest.com"
```

Windows PowerShell:

```powershell
node --version
$env:NODE_OPTIONS=(($env:NODE_OPTIONS + " --use-system-ca").Trim())
(iwr "https://app.joinquest.com/student-deploy/runner.js" -UseBasicParsing).Content | node - --api-base "https://app.joinquest.com"
```

On Windows, use `--use-system-ca` on the first deploy when Node is 22.15 or
newer. This allows Node to trust certificates already trusted by Windows and
prevents failures on school or company networks that inspect HTTPS. This
setting lasts only for the current PowerShell session.

If Node is older than 22.15, deploy without `NODE_OPTIONS`. Only ask the student
to update Node if the deploy fails with a certificate trust error.

The runner reads the token from `.env.local` or shell environment variables. If a one-off deploy needs an explicit token, pass `--token "<QUEST_DEPLOY_TOKEN>"` and preserve all characters.

Treat the deploy as successful if the runner prints:

```txt
QUEST_DEPLOY_RESULT {"status":"success",...}
```

Use that structured line as the source of truth over earlier warnings or noisy build output.

If that marker is missing, treat the deploy as successful only if the runner prints all of:
- `Success`
- `Version`
- `Game URL`

Warnings like this are not deploy failures:

```txt
Some chunks are larger than 500 kB after minification
```

Only report `Failed` when:
- The command exits non-zero.
- The runner prints `QUEST_DEPLOY_RESULT {"status":"failed",...}`.
- The runner prints `Student deploy failed:`.

Never invent values like `unknown` or `unavailable`. Copy the exact version and URL printed by the runner.

## Multiplayer During Deploy

If the project needs multiplayer, use the Quest Multiplayer SDK:
- `https://app.joinquest.com/student-deploy/quest-multiplayer.mjs`
- `https://app.joinquest.com/student-deploy/quest-multiplayer.js`

Do not upload custom multiplayer backend code.

## Final Reply Format

Reply with only:

```txt
- Success/Failed
- Version
- Game URL
```

Rate limit response:

```txt
- Failed
- You've hit the limit of 10 game updates/versions in the last 24 hours
```

Upload size response:

```txt
- Failed
- Your Quest upload is over the total upload limit or per-file upload limit. Limit: 100 MiB per file, 250 MiB total. Include largest files from QUEST_DEPLOY_RESULT when available. Build first and deploy only the browser output folder, then reduce or compress large assets if needed.
```

TLS certificate trust response:

If Node fails with `UNABLE_TO_VERIFY_LEAF_SIGNATURE` on Windows, PowerShell can download `runner.js`, and Node cannot, do not report failure yet. Check `node --version`, then automatically retry with Node's Windows certificate store when Node is 22.15 or newer:

```powershell
$env:NODE_OPTIONS=(($env:NODE_OPTIONS + " --use-system-ca").Trim())
(iwr "https://app.joinquest.com/student-deploy/runner.js" -UseBasicParsing).Content | node - --api-base "https://app.joinquest.com"
```

If `--use-system-ca` is not recognized, update Node or export the network root certificate as a PEM file and set `NODE_EXTRA_CA_CERTS` to that file.

Do not use `NODE_TLS_REJECT_UNAUTHORIZED=0`.

Do not permanently change user or system environment variables without asking.

Other failure response:

```txt
- Exact error message
- The command you ran
- Shell used, either macOS/Linux or Windows PowerShell
```
