> ## Documentation Index
> Fetch the complete documentation index at: https://openturn.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Deploy to openturn cloud

> Build your openturn project and ship it to openturn.io with `openturn deploy`.

`openturn deploy` ships your game to the openturn.io cloud. The control plane provisions a hosted backend per game and returns a play URL anyone can open. `openturn build` produces the bundle; `openturn deploy` uploads it.

Under the hood, openturn cloud runs your bundle on Cloudflare Workers and Durable Objects (one DO per room) — but you don't manage any of that. You only interact with `openturn login` and `openturn deploy`.

## Project layout

You need the same three files as [tutorial: tic-tac-toe multiplayer](/docs/tutorials/tic-tac-toe-multiplayer):

* `app/game.ts` — exports `game` and `match`.
* `app/page.tsx` — the React entry point.
* `app/openturn.ts` — metadata with `runtime: "multiplayer"`.

### The `openturn.ts` metadata

```ts theme={null}
export const metadata = {
  name: "Tic Tac Toe Multiplayer",
  runtime: "multiplayer",
  multiplayer: {
    gameKey: "tic-tac-toe-multiplayer",   // stable identifier for this game
    schemaVersion: "1",                    // bump to reset persisted rooms
  },
};
```

`gameKey` is the identifier the cloud control plane uses to route play URLs. `schemaVersion` lets you force-reset persisted rooms when you change `G` in an incompatible way.

## Build

```bash theme={null}
openturn build app
```

This produces, by default, `.openturn/deploy/`:

* Static assets (`index.html`, client JS, CSS).
* `entry.<hash>.js` — the client bundle Vite emitted.
* `worker.js` + `openturn.worker-meta.json` — the server bundle and its room bindings.
* `openturn.manifest.json` — a manifest summarizing the build.

Inspect the manifest:

```bash theme={null}
cat .openturn/deploy/openturn.manifest.json
```

Fields of interest:

* `deploymentID`, `projectID`, `runtime` — identifiers.
* `entry` — the JS entry the HTML shell loads.
* `assets`, `styles` — static files.
* `multiplayer.gameKey`, `multiplayer.schemaVersion`, `multiplayer.digest` — hash of the server bundle, used for versioning rooms.
* `build.openturnPackages` — the resolved `@openturn/*` versions baked in.

## Log in

```bash theme={null}
openturn login --token <your-cloud-token>
```

This writes the token to `~/.openturn/cloud-auth.json`. The CLI defaults to openturn.io; pass `--url` to point at a staging environment.

## Deploy

```bash theme={null}
openturn deploy app
```

The CLI:

1. Builds the project (equivalent to `openturn build app`).
2. Uploads the static bundle and server script to the openturn cloud control plane.
3. Registers or updates the deployment under the `gameKey` from `openturn.ts`.
4. Prints the play URL — a URL that encodes the backend in a fragment, so any browser can open it and join a room.

You can pass a project slug or display name explicitly:

```bash theme={null}
openturn deploy app --project my-tic-tac-toe --name "Tic Tac Toe"
```

## Play URL fragments

Deployed apps read `window.location.hash` for a `HOSTED_FRAGMENT_KEY` value (encoded JSON from `@openturn/bridge`). Share the full play URL — the fragment is what tells the React app how to find the hosted backend.

## Static assets and images

There are two supported ways to ship images, fonts, or other static files:

1. **Import from JS or CSS** — `import logo from "./logo.png"` or `background-image: url("./bg.svg")`. Vite emits the file under `assets/<name>-<hash>.<ext>` so the URL is stable forever; small images (\< 4 KiB by default) are inlined as data URLs. This is the right pattern for assets your code references directly.
2. **Drop into `public/`** — files under `<projectDir>/public/**` are copied into the deployment root verbatim and served at the same path. A file at `public/banner.png` is reachable at `/banner.png` from your page. These files are *not* hashed; the per-deployment cache key comes from the deployment ID, so a redeploy with a changed file invalidates downstream caches.

### Bundle size limits

`openturn deploy` enforces these limits before contacting the cloud, and the cloud re-validates them on receipt:

| Limit                               | Cap    |
| ----------------------------------- | ------ |
| Per-asset size                      | 25 MiB |
| Total assets                        | 25 MiB |
| Total images                        | 25 MiB |
| Multiplayer worker bundle (gzipped) | 3 MiB  |

The worker-bundle ceiling matches the Cloudflare Workers Free-tier script size. The asset and image caps keep iframe load times reasonable. If a build breaches a limit, the CLI fails fast with a structured error like `bundle_too_large_per_asset` and prints the offending file. To shrink: optimize images (`squoosh`, `oxipng`), code-split with dynamic `import()`, or drop unused dependencies.

## Schema versioning

If you change `G` in a way the reducer no longer accepts old action logs for, bump `schemaVersion` in `app/openturn.ts`. Deployed rooms with the old version are closed on next restart; new rooms start fresh.

You do not need to bump `schemaVersion` for additive changes (new optional fields, new moves that do not invalidate old sequences).

## Debugging a deploy

* **`openturn_deploy_error`** — the cloud returned an error. The CLI prints the JSON body.
* **Play URL opens to "missing\_hosted\_backend"** — the page loaded without the fragment. Share the exact URL the CLI printed, or ensure your hosting environment preserves URL fragments.
* **Moves fail with `unauthorized`** — the player's room token expired. Refresh the page; `@openturn/bridge` handles token refresh automatically after that.

## A deploy-ready reference

[`examples/games/splendor/app`](https://github.com/openturn-io/openturn/tree/main/openturn/examples/games/splendor/app) is wired end-to-end for cloud deploy — `runtime: "browser"` metadata, all bundle prerequisites in place, plus `attachBots(splendor, splendorBotRegistry)` so the deployed lobby exposes the three bots (`random`, `greedy`, `strategic`). To walk it:

```bash theme={null}
cd openturn/examples/games/splendor/app
bun run deploy
```

The CLI builds the browser bundle, generates and uploads the multiplayer Worker, and prints the openturn-cloud play URL. Use the splendor app's `package.json` deploy script (`--project splendor --name "Splendor"`) as a template for your own.

## Related

* [How-to: run a local hosted stack](/docs/how-to/run-local-hosted) for the localhost equivalent.
* [Reference: deploy](/docs/reference/deploy) documents the manifest and build pipeline.
* [Reference: cli](/docs/reference/cli) for the full CLI flag surface.
