ARCANUM
self-host

Run Wardstone

One container. The models ship inside the image.

1 · Config

The only value to change is sink.url, where verdicts are POSTed.

{ "http": { "port": 8090 }, "artifacts_dir": "/artifacts/2026.10.1", "metrics_path": "/data/wardstone.metrics", "rings": { "data_dir": "/data" }, "sink": { "url": "https://your-moderation-service/wardstone/verdicts", "hmac_secret_env": "WARDSTONE_SINK_SECRET" } }

2 · Run

Keep the /data volume: it holds queued work across restarts.

docker volume create wardstone-data docker run -d --name wardstone --restart unless-stopped \ -p 8090:8090 \ -v wardstone-data:/data \ -v $PWD/wardstone.json:/app/config/wardstone.json:ro \ -e WARDSTONE_SINK_SECRET=<shared secret> \ wardstone:<version> curl localhost:8090/healthz # ok, ready after 10–60 s

3 · Send messages

Same conversation_id per chat, same author per user, unique message_id, in order. Or stream binary frames over ws://host:8090/v1/stream.

curl -s localhost:8090/v1/messages -H 'Content-Type: application/json' -d '{ "messages": [ {"conversation_id": "c42", "message_id": "1001", "author": "u7", "text": "...", "ts": 1791712345123} ]}'

4 · Receive verdicts

Verify the signature, return 2xx, ignore verdict ids you have already seen. Format: API.

5 · Check

curl localhost:8090/v1/status # queued work, drops, model version docker logs wardstone # never contains message text