# 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 ```sh 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](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 `/certificates/`: - `root-ca.pem` / `root-ca-key.pem` — self-signed root CA - `server.pem` / `server-key.pem` — HTTP server certificate (issued for `localhost`) - `admin-client.pem` / `admin-client-key.pem` — the initial admin client identity The SQLite database is created at `/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 ```sh 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: X-Request-Timestamp: X-Request-Nonce: X-Request-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-Length` over 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_module` table (single active row) and a `core_uploaded` event is published over `/api/events` ```sh 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: ```sh 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](src/cli.rs)): `status`, `create-user `, `delete-user `, `stop-game`, `start-game`, `update-agent `. ## Building the example WASM agent [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](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: ```sh 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: ```sh # 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`).