162 lines
6.8 KiB
Markdown
162 lines
6.8 KiB
Markdown
# 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 `<data-dir>/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 `<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
|
|
|
|
```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: <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-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 <username>`, `delete-user <client_id>`, `stop-game`,
|
|
`start-game`, `update-agent <path>`.
|
|
|
|
## 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`).
|