Skip to main content
A shard is one running simulation: a match, a zone, a room. The server owns its state. Clients send inputs, the shard applies them on its tick, and every subscriber gets a snapshot after each tick. You write the simulation in Rust with the pylon-shard-guest crate and compile it to WebAssembly. The pylon binary loads the module at boot. You do not build a custom server binary, so the same app runs on pylon dev, pylon start, a Docker image, and Stack0 Cloud. The complete example is examples/shard-arena.

Set up

Install the WebAssembly target once:
Make a library crate inside the app, for example shards/arena:
shards/arena/Cargo.toml
If the app is inside a Cargo workspace, add an empty [workspace] table so the crate builds on its own.

Write the simulation

Implement Shard and export it with export_shard!:
shards/arena/src/lib.rs
The trait has these methods: auth carries user_id, is_admin, roles, tenant_id, and the verified ticket. Read ticket claims with auth.claim("realm"). The module has no clock, no random source, and no I/O. Put a seed in params when the game needs random numbers. The same inputs then give the same state, which a replay needs. pylon_shard_guest::log(Level::Info, "…") writes to the server log.

Declare the shard kind

Add the kind to buildManifest in app.ts:
app.ts

Build the module

The command runs cargo build --release --target wasm32-unknown-unknown for each kind with a crate, and copies the module to its wasm path. pylon dev runs the same build at start. It builds again and restarts the server when a .rs file or Cargo.toml in the crate changes. Commit the .wasm file. pylon build copies it into the artifact, and pylon deploy uploads it even when .gitignore lists it. When cargo is on the path, pylon deploy builds the crates first. A GitHub deploy uses the committed file, because the Cloud builder has no Rust toolchain.

Start a shard and join it

An action starts a shard and gives the player a ticket:
functions/joinArena.ts
ctx.shards has these calls: A shard id is 1 to 128 letters, digits, -, _, ., or :. The client connects with the ticket:
The client connects to /shard on the app’s own origin, so it needs no extra port or proxy rule. See React client for acks, rejections, and custom decoders.

Limits and failures

  • A tick that runs past tickBudgetMs stops the shard.
  • A module that grows past memoryMb stops the shard.
  • A panic stops the shard. The server log shows the panic message.
  • ctx.shards.get(id) returns the reason in error until the host removes the stopped shard (within a few seconds).
  • A module may import only pylon.log. The server refuses a module built for WASI or with any other import, and the boot fails.
  • Shard state is in memory. A restart or a deploy ends every shard.

Hosted on Stack0 Cloud

Stack0 Cloud runs shards with no extra setup. Clients connect to wss://your-app.stack0.app/shard, or to /shard on your custom domain.
  • An open shard connection keeps the app’s machine running.
  • A machine with autostop on can stop when no client is connected. Its shards end, like on any restart.