Deploying

A room that runs under siltrun dev deploys as-is. There is no separate production build of your contract, no configuration file, and no server to provision.

Terminal window
npx siltrun login

This opens your browser, signs you in with GitHub, and stores a session in ~/.silt/credentials.json. Deploys made afterwards are keyed to your account. siltrun whoami prints who you are signed in as, and siltrun logout removes the stored session.

Terminal window
npx siltrun deploy room.ts

The pipeline, in order:

  1. Bundles room.ts, exactly as siltrun dev does.
  2. Runs the determinism check locally. This is the gate: if replays of your room drift, nothing ships, and the output names the tick and field that drifted.
  3. Ships the bundle and boots your room on a hosted endpoint.
  4. Prints your room endpoint and a check page URL.

The room endpoint is what client code connects to: change the useRoom URL from http://localhost:4000 to the printed endpoint. The check page is a plain web page that joins the room and shows it live.

Remember the transport: the room endpoint is a WebTransport address, so it will not open in a browser tab and curl gets nothing from it. Use the check page to confirm the room is up.

Every room you deploy appears at console.silt.run. Sign in there with the same GitHub account and you get the list of rooms you own, each with its status, its room URL, and its determinism verdict. Opening a room shows its live state: the current tick, who is connected, and the authoritative state the room is broadcasting. The console is read-only, so watching a room cannot disturb it.

siltrun deploy resolves a credential in this order:

  1. SILT_DEPLOY_TOKEN in the environment, if set.
  2. The session stored by siltrun login.

With neither, the command stops before uploading anything and tells you so.

Every room has a name, and it resolves in this order:

  1. The --room <name> flag.
  2. The SILT_ROOM environment variable.
  3. Derived from the contract path: the file’s basename without extension. If the basename is generic (room, index, contract, main), the parent directory names it instead, so my-game/room.ts becomes my-game.

Explicit names must match [A-Za-z0-9._-]+. Invalid names are rejected rather than silently rewritten. Derived names are sanitized to that charset. Deployed room names are also capped at 12 characters: a deployed room maps to a Linux network interface named tap<room>, and the kernel caps interface names at 15. If the derived name is longer, the deploy stops and asks for a --room <name> within the cap.

Shipping a new contract restarts the room. State does not survive the restart: init() runs again and the room starts at tick 0.

This is the alpha posture, and it shapes what you should build today. A room holds live sessions well. It is the wrong place to keep anything that must outlive a restart, such as a days-long match or a persistent world. If a match must survive, keep the source of truth outside the room and re-seed a new room from it; init() may fetch, since it runs once with the full runtime. The Limits page has the full persistence story.

Your client is a static site. npm run build produces it, and any static host serves it. The only wiring between client and room is the useRoom URL.