# Publish a game

> The full path from invite code to released game. Create a draft, prepare and upload a build (in the browser or from a terminal), add a cover, preview it and submit it to containment.

Most steps happen in your browser, at `/dev`. Builds can also go up from a terminal or a coding agent, with an API token. The last step is a 48-hour vote by strangers.

## Checklist

An agent connected to the [MCP server](/docs/agents#connect-your-agent) can do steps 3 to 8: create the draft, prepare and deploy the build, upload a cover, fill in the description, and submit it when you say so. Without MCP, an agent can do steps 4 to 6 with an API token.

1. Sign in with Discord or GitHub and claim a handle at `/welcome`. That's your badge.
2. Redeem an invite code at `/dev`. Codes come from an admin.
3. Create the game with **New game** on My games (`/dev`). The slug can't change later.
4. Build for the web: `index.html` at the top, relative paths, allowed file types only. Run `node check-build.mjs <folder-or-zip>` until it is clean.
5. Upload the zip or folder at `/dev/<slug>`, or deploy it from a terminal: `node check-build.mjs deploy <folder> --game <slug>`. Add patch notes if you want them.
6. Add a cover on the same page.
7. Make sure the game has a description. Click **Preview** and play the draft.
8. Hold the **Hold to submit** button. A draft needs a live build, a cover and a description.
9. Wait 48 hours. Released games are listed on `/games`. Buried games can come back after 7 days with a new build.

## Get access

Developer access is invite-only.

1. Sign in with Discord or GitHub.
2. Claim a handle at `/welcome`. That's your badge. Your handle appears on your games and your profile.
3. Open `/dev`. Without developer access the page shows an **Invite code** field.
4. Enter the code. The format is `XXXX-XXXX-XXXX`. Case and dashes don't matter.

Every code has a use limit, and some expire. A code that is used up, expired or mistyped is refused. `/dev` then becomes **My games**, which is also in the menu under your avatar.

## Create the game

Click **New game** on My games. `/dev/new` opens the same form.

| Field | Rules |
| --- | --- |
| Title | Required. 1 to 60 characters. Editable later. |
| Slug | Permanent. See below. |
| Engine | Three.js, Unity, Godot or HTML5. A label on the game's card. It doesn't change how the build runs or what the uploader accepts. |
| Origin | Handmade, AI-assisted or AI-generated. Shown on every card. Be straight about it. |
| What did AI make? | Appears for AI-assisted and AI-generated. Pick at least one of Code, Art, Audio and Text. |
| Description | Up to 600 characters. Optional now, required before containment. A sentence or two: what it is and how it plays. |

### Slug

The slug is the game's own origin: `https://<slug>.containment.cloud`. That is where its saves live, so it can't be changed.

- 1 to 47 characters.
- Lowercase `a-z`, digits `0-9` and single dashes. No `--`.
- Starts and ends with a letter or a digit.
- Some names are reserved, for example `www`, `api`, `admin`, `dev`, `docs`, `play` and `test`. Anything starting with `xn-` is refused.

The form fills the slug from the title until you edit it, and checks availability as you type. The **Create draft** button stays disabled until the slug is free.

### AI disclosure

Handmade means no AI flags. AI-assisted and AI-generated need at least one. The form and the server both refuse any other combination.

The new game is a **draft**. Only you and admins can see it. You can have ten drafts at once. Everything except the slug can be edited later under **Details** on the game's page.

## Prepare a build

A build is the web export of your game. Before you upload:

- `index.html` sits at the top of the build, or `fradiation.json` names another `entry`.
- Every path is relative. `assets/game.js` works. `/assets/game.js` breaks.
- Every file has an extension on the allowed list.
- The game fills its window and handles resize.

`fradiation.json` at the top is optional. It sets `entry`, `viewport` and `threads`, and declares mutations and scoreboards. See [fradiation.json](/docs/fradiation-json). Games talk to the site through the [SDK](/docs/sdk), which is also optional.

| Engine | Guide |
| --- | --- |
| Unity | [Unity](/docs/unity) |
| Godot | [Godot](/docs/godot) |
| Three.js, Vite, plain HTML5 | [Web](/docs/web) |

[Builds and uploads](/docs/builds) has every limit, path rule and file type. The machine-readable version is `https://www.fradiation.games/docs/build-rules.json`. Check a build before you open the browser:

```bash
node check-build.mjs <folder-or-zip>
```

Download the script and see the other commands in [Check a build first](/docs/builds#check-a-build-first).

## Upload the build

Open `/dev/<slug>` and find the **Upload a build** panel. To upload from a terminal instead, see [From a terminal](#from-a-terminal).

1. Drop a `.zip` on the tray, or click **browse**. To upload a folder, click **pick a folder**. Dropping a folder doesn't work.
2. The browser reads the build and checks it. Problems are listed, up to eight, and **Upload build** stays disabled until none remain.
3. Set the **Stage size**. It appears only if `fradiation.json` has no `viewport`. Width is 160 to 4096 and height is 120 to 4096. The default is 1280 by 720, or the size of your current build. The site letterboxes the game to that aspect ratio.
4. Write **Patch notes** if you want to. Up to 2,000 characters, shown in the game's update log.
5. Choose what happens when the build is sealed. The choices depend on the game, as the table below shows.
6. Click **Upload build**.

### When it's sealed

| Game status | Choice | Result |
| --- | --- | --- |
| Draft or buried | none | The upload becomes the live build. A draft always runs its newest build. |
| In containment or released | **Go live now** (default) or **Upload only** | Go live now makes it the live build. Upload only seals it and leaves the live build alone. |
| Any game with a playtest | **Send to testers** (default) or **Keep their build** | Testers get the new build on their next load, with your patch notes, or keep the one they have. |

### What the uploader checks

Before it sends anything:

- The entry file is at the top.
- At most 1,000 files, 500 MB in total and 200 MB per file.
- Every path follows the path rules and every file type is allowed.
- `fradiation.json`, if present, is valid JSON.

The site then checks `fradiation.json` against its schema and confirms the entry file is in the file list. After the upload it checks that every file arrived at its full size. Then it seals the build. A sealed build never changes.

### Progress

The bar moves through **Checking files**, **Requesting a grant**, **Uploading** and **Verifying and sealing**. Stay on the page. Leaving mid-upload asks you to confirm. If an upload fails or you cancel, start it again. The site cleans up what it left behind.

Only files the game doesn't already have are sent. An update that changes one file uploads one file. Each build in the Builds list shows how much it sent, for example `(1.6 KB new)` or `(nothing new)`.

### From a terminal

`check-build.mjs` uploads the same way the browser uploader does, with an API token instead of your browser session.

1. Under **Agents & tokens** (`/dev/agents`), create a token in the **API tokens** panel, for all your games or one. Copy it: it's shown once.
2. Set it as `FRADIATION_TOKEN` in your shell or your CI secrets.
3. Deploy the build folder or zip:

```bash
node check-build.mjs deploy Builds/Fradiation --game <slug> --notes "What changed"
```

It checks the build first, uploads only what the game doesn't have, and seals it. `--live` makes it live and `--playtest` sends it to testers, with the same rules as the choices above. `node check-build.mjs live <buildId>` ships or rolls back later. See [Deploy from the command line](/docs/agents#deploy-from-the-command-line) for every option and the API behind it.

### Limits

- 20 uploads per rolling 24 hours, across all your games, from the browser and from tokens together. Failed and cancelled uploads count.
- Files over 64 MB upload in parts. This is automatic.
- Storage has a soft limit of 2 GB per developer. See [Retention and storage](/docs/builds#retention-and-storage).

## Add a cover

Use the **Cover** panel on the game's page.

1. Drop an image on it, or click to browse. PNG, JPEG, WebP or AVIF.
2. The browser crops it to 4:3 from the center and re-encodes it as an 800 by 600 JPEG. The upload limit is 2 MB.
3. It uploads at once. There is no separate save.

A draft needs a cover before it can be submitted. You can replace the cover at any time.

From a terminal or an agent: `node check-build.mjs cover cover.png --game <slug>` with an API token, or the MCP server's `upload_cover` (a presigned URL the agent PUTs the image to). The site does the same crop, from images up to 15 MB. See [How covers work](/docs/agents#how-covers-work).

## Preview the draft

Once the draft has a build, click **Preview** at the top of `/dev/<slug>`. That opens `/games/<slug>`, the real game page. A **Draft** notice says only you and admins can see it. Click **Start** to run the build inside the cabinet.

- SDK calls run in test mode. `ready()` reports the mode `preview`. Calls are checked, and nothing is kept.
- A mutation key the build doesn't declare, a negative score and a score above the board's `max` are rejected.
- A toast marks each call: `Test unlock: <name>` or `Test score: <value>`.
- Your own plays of your own draft don't count as plays.
- **Open** on a build in the Builds list loads it directly on `https://<slug>.containment.cloud/<buildId>/`, outside the cabinet. There is no SDK connection there.

Other players can't see a draft. To get feedback before you submit, start a private playtest. See [Playtests](/docs/playtests).

## Submit to containment

A draft can be submitted when the status panel shows all three checks:

- A live build.
- A cover.
- A description.

Hold the **Hold to submit** button for about a second. The game moves to **In containment** and is listed on `/containment`. For the next 48 hours, players who have played it vote to release it or bury it. You can't vote on your own game, and the tally is hidden until judgment closes. You can keep uploading builds while it's in there.

Send people the link to your game's page. Votes come only from players who have played it.

When judgment closes, the game is released or buried. [Containment](/docs/containment) has the exact rules, the payouts and the way back from a burial.

## After release

A released game has the same page at `/dev/<slug>`. New builds go live when you say so.

### Updates

Upload a new build. Choose **Go live now** and players get it on their next load. Choose **Upload only** and the build is sealed but not live. Make it live later from the Builds list. From a terminal, `deploy` without `--live` is **Upload only**, and `deploy --live` is **Go live now**.

Patch notes show in the game's update log. Saves survive updates, because the game's origin doesn't change.

### Rollbacks

Every sealed build that still has its files can be made live. Open the Builds list, find the build and click **Make live**, or run `node check-build.mjs live <buildId> --game <slug>`. That is how you ship and how you roll back. Builds whose files were deleted can't be made live. Upload them again. Pin a build to keep its files. See [The builds list](/docs/builds#the-builds-list).

### Playtests

A playtest is a private link that serves any sealed build you choose, independent of the live one. Use it to try your next update on testers before players get it. With a playtest open, an upload offers **Send to testers** or **Keep their build**. See [Playtests](/docs/playtests).

### If it was buried

A buried game keeps its page, and its developer can send it back for another round after 7 days with a new build. See [Coming back](/docs/containment#coming-back).
