|
|
||
|---|---|---|
| .github/agents | ||
| example-wasm-agent | ||
| src | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| README.md | ||
README.md
Wolloseum
Wolloseum is a deterministic, procedurally generated game server. Players upload untrusted WASM agents (run in a sandboxed Extism/Wasmtime runtime with fuel and memory limits) that control spawned agents in a tick-based simulation. All identity and request authentication is certificate-based (Ed25519 client certificates signed by a locally issued root CA); there are no passwords or bearer tokens.
Building the server
cargo build --release
The binary is produced at target/release/wolloseum.
Configuration
The server takes its configuration from CLI flags (each with an environment variable fallback), defined in src/cli.rs:
| Flag | Env var | Default | Purpose |
|---|---|---|---|
--data-dir |
WOLLOSEUM_DATA_DIR |
data |
Directory for certificates and the SQLite database |
--bind |
WOLLOSEUM_BIND |
127.0.0.1:8080 |
HTTP listener address |
On first start the server bootstraps a PKI under <data-dir>/certificates/:
root-ca.pem/root-ca-key.pem— self-signed root CAserver.pem/server-key.pem— HTTP server certificate (issued forlocalhost)admin-client.pem/admin-client-key.pem— the initial admin client identity
The SQLite database is created at <data-dir>/wolloseum.sqlite3 and stores
identities, used request nonces, per-player agent WASM, and the core
simulation WASM module. Private key files and the certificate directory are
written with restrictive (owner-only) permissions on Unix.
Running the server
cargo run --release -- serve --data-dir data --bind 127.0.0.1:8080
On startup the server prints the admin certificate ID and the paths to the admin client certificate/key and root CA certificate — you'll need these to issue authenticated CLI/API requests.
API endpoints
All /api/* endpoints except /api/status and /api/events require request
signing. Each signed request must include:
X-Client-Id: <sha256 hex of the client's certificate PEM>
X-Request-Timestamp: <unix seconds, within 60s of server time>
X-Request-Nonce: <unique, ASCII alphanumeric/-/_, replay-checked>
X-Request-Signature: <base64 Ed25519 signature>
The signed message is METHOD\nPATH\nTIMESTAMP\nNONCE\n followed by the raw
request body bytes.
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | / |
none | Player web UI |
| GET | /admin |
none | Admin web UI |
| GET | /api/status |
none | Current game running state |
| GET | /api/events |
none (WebSocket) | Live game/agent/core state change events |
| POST | /api/create_user |
admin | Create a player identity, returns certificate + private key |
| POST | /api/delete_user |
admin | Delete a player identity and their uploaded agent |
| POST | /api/start_game |
admin | Resume the simulation |
| POST | /api/stop_game |
admin | Pause the simulation |
| POST | /api/update_agent |
any active identity | Upload/replace the caller's agent WASM module |
| POST | /api/upload_core |
admin | Upload/replace the core simulation WASM module |
/api/upload_core
Replaces the core simulation logic module. The request body is the raw WASM
bytes (not JSON), following the same conventions as /api/update_agent:
- Body limit: 1 MiB (
Content-Lengthover the limit is rejected) - The module is sandboxed-loaded (no WASI imports, bounded memory) to confirm it is valid before being persisted
- Requires admin authentication (signed request headers as above)
- On success the module is stored in the
core_moduletable (single active row) and acore_uploadedevent is published over/api/events
curl -k http://127.0.0.1:8080/api/upload_core \
-X POST \
--data-binary @core_simulation.wasm \
-H "x-client-id: $ADMIN_ID" \
-H "x-request-timestamp: $TS" \
-H "x-request-nonce: $NONCE" \
-H "x-request-signature: $SIGNATURE"
In practice, use the CLI (below) rather than constructing signatures by hand.
Using the CLI client
The same binary provides a signing HTTP client:
cargo run --release -- cli \
--server http://127.0.0.1:84080\
--root-ca data/certificates/root-ca.pem \
--client-cert data/certificates/admin-client.pem \
--client-key data/certificates/admin-client-key.pem \
status
Available subcommands (see src/cli.rs): status,
create-user <username>, delete-user <client_id>, stop-game,
start-game, update-agent <path>.
Building the example WASM agent
example-wasm-agent/ is a standalone crate implementing
a minimal Extism plugin agent matching the ABI expected by
src/wasm_agent.rs (a JSON decide call taking
{abi_version, tick, x, y, adjacent_terrain} and returning
{abi_version, action}). Movement is a deterministic function of the tick
and position — no RNG, clock, or host access is used, so replays are
reproducible.
Build it to a .wasm file:
cd example-wasm-agent
rustup target add wasm32-unknown-unknown # if not already installed
cargo build --release --target wasm32-unknown-unknown
The compiled module is at
example-wasm-agent/target/wasm32-unknown-unknown/release/example_wasm_agent.wasm.
Uploading the example agent
First create a player identity (as admin), then upload the agent WASM signed with that player's own certificate:
# As admin, create a player and save the returned certificate/private key:
cargo run --release -- cli \
--client-cert data/certificates/admin-client.pem \
--client-key data/certificates/admin-client-key.pem \
create-user alice
# -> write the returned certificate_pem/private_key_pem to alice.pem / alice-key.pem
# As alice, upload the compiled agent:
cargo run --release -- cli \
--client-cert alice.pem \
--client-key alice-key.pem \
update-agent example-wasm-agent/target/wasm32-unknown-unknown/release/example_wasm_agent.wasm
Core simulation WASM can be uploaded the same way by an admin using
POST /api/upload_core (no dedicated CLI subcommand yet — use curl as shown
above, or extend the CLI client following the same pattern as update-agent).