Development
Prerequisites
| Tool | Version | Purpose |
|---|---|---|
| Python | ≥ 3.11 | Backend (primary) |
| Node.js | ≥ 20 | Frontend build |
| npm | ≥ 10 | Frontend deps |
| Docker + Compose | any recent | Dev stack |
| Rust | ≥ 1.93 | Backend (optional, in-sync) |
| mosquitto-clients | optional | MQTT debugging |
Quick start (frontend-only, no Rust build)
The fastest way to develop the UI is against the mock agent simulator.
# Terminal 1 — MQTT broker + mock agents
docker compose -f compose.dev.yaml up -d
# Terminal 2 — Vite dev server (hot-reload on http://localhost:3000)
cd frontend
npm install
npm run dev
The mock simulator (scripts/mock-agents.mjs) publishes realistic MQTT events:
- 6 agents: main-actor, monitor-agent, data-fetcher, weather-agent, ml-classifier, nautilus-agent, udx-agent
- Heartbeats every 5 s
- Chat replies every 4 s
- Occasional alerts (30% chance every 8 s)
- Dynamic agent spawns every 20 s (20% chance)
The Vite dev server proxies /api/, /ws, and /mqtt to the local ports exposed by compose.dev.yaml.
Python-first dev mode
For day-to-day development, the repo can default to the Python backend while exposing the same frontend-facing API contract (which the optional Rust backend mirrors).
# Terminal 1 — MQTT broker
docker compose -f compose.dev.yaml up -d mosquitto
# Terminal 2 — Python backend, REST on :8080
WACTORZ_DEV_MODE=1 ./run.sh
# Terminal 3 — frontend SPA
cd frontend && npm install && npm run dev
In this mode:
- run.sh defaults to the Python backend unless WACTORZ_BACKEND overrides it.
- The Python backend defaults to INTERFACE=rest and PORT=8080.
- The primary frontend is frontend/index.html, not the legacy monitor.html.
Stop mock stack
docker compose -f compose.dev.yaml down
Full stack (Rust backend, optional in-sync)
# Terminal 1 — support services
docker compose up -d mosquitto dashboard # or 'docker compose up -d'
# Terminal 2 — Rust backend
cd rust
RUST_LOG=wactorz=debug cargo run --bin wactorz -- \
--mqtt-host localhost \
--llm-provider anthropic \
--llm-api-key "$LLM_API_KEY"
# Terminal 3 — frontend dev server
cd frontend && npm run dev
Project structure
wactorz/
├── rust/ Rust workspace
│ ├── Cargo.toml workspace manifest
│ ├── Dockerfile multi-stage: builder → runtime
│ └── crates/
│ ├── wactorz-core/ Actor trait, registry, messages
│ ├── wactorz-agents/ All concrete agents
│ ├── wactorz-mqtt/ MQTT client + topic helpers
│ ├── wactorz-interfaces/ REST, WebSocket, CLI
│ └── wactorz-server/ Binary entry point
│
├── frontend/ Vite + TypeScript + Babylon.js SPA
│ ├── src/
│ │ ├── main.ts App bootstrap, wires all components
│ │ ├── types/agent.ts Shared TypeScript types
│ │ ├── mqtt/MQTTClient.ts MQTT client, event routing
│ │ ├── io/
│ │ │ ├── IOManager.ts Sends messages to io/chat
│ │ │ ├── IOBar.ts Input bar (history, multiline, @mention)
│ │ │ ├── AgentImageGen.ts DiceBear + Gemini avatar generation
│ │ │ └── VoiceInput.ts Web Speech API integration
│ │ ├── scene/
│ │ │ ├── SceneManager.ts Babylon.js lifecycle, theme switching
│ │ │ ├── themes/ GraphTheme, GalaxyTheme, GraveTheme,
│ │ │ │ CardBabylonTheme, ThemeBase
│ │ │ ├── nodes/ GraphNode, PlanetNode, GraveNode,
│ │ │ │ AgentNodeBase, CardBabylonNode
│ │ │ └── effects/ AlertEffect, HeartbeatEffect,
│ │ │ MessageEffect, SpawnEffect
│ │ └── ui/
│ │ ├── ChatPanel.ts Per-agent chat threads
│ │ ├── SocialDashboard.ts Instagram/Twitter-style card grid
│ │ ├── CardDashboard.ts Minimal HTML card grid
│ │ ├── ActivityFeed.ts Collapsible MQTT event log
│ │ ├── MentionPopup.ts @mention autocomplete
│ │ ├── AgentHUD.ts Top-left agent count HUD
│ │ └── ThemeSwitcher.ts Theme toggle buttons
│ ├── public/
│ │ ├── favicon.svg
│ │ ├── site.webmanifest
│ │ └── robots.txt
│ ├── index.html All CSS is inline here (no external CSS files)
│ ├── vite.config.ts
│ └── tsconfig.json
│
├── infra/
│ ├── nginx/
│ │ ├── nginx.conf Full Docker mode
│ │ └── nginx-native.conf Native binary mode
│ ├── mosquitto/mosquitto.conf
│ ├── fuseki/ Apache Jena Fuseki
│ └── homeassistant/ HA configuration
│
├── scripts/
│ ├── deploy.sh Build + rsync + restart wizard
│ ├── mock-agents.mjs Dev-mode mock MQTT simulator
│ ├── package-full-release.sh Build full zip release
│ ├── package-native.sh Build native binary tar.gz
│ └── build-native.sh Build binary on the target host
│
├── systemd/wactorz.service systemd unit template
├── compose.yaml Full Docker stack
├── compose.dev.yaml Dev stack (mosquitto + mock only)
├── compose.native.yaml Native binary stack
├── .env.example Annotated config template
└── docs/ This documentation
Rust development
cd rust
# Check (fast — no codegen)
cargo check
# Build debug
cargo build --bin wactorz
# Build release
cargo build --release --bin wactorz
# Run with debug logging
RUST_LOG=wactorz=debug cargo run --bin wactorz
# Lint
cargo clippy -- -D warnings
# Format
cargo fmt
Backend parity proof
The repo now carries a shared backend parity contract for the core supervisor semantics that both runtimes claim to implement.
# Run language-specific tests plus the cross-runtime parity diff
make test
# Run only the parity proof
make parity
# Generate coverage artifacts used by CI
python -m pip install -r requirements-dev.txt
make coverage
The shared fixture lives at tests/parity_fixtures/backend_supervisor_parity.json.
Both the Python harness and the Rust harness execute those same scenarios and must
produce the same normalized JSON output for the build to pass.
Pre-commit
python -m pip install -r requirements-dev.txt
make precommit-install
make precommit-run
The configured hooks run: - Python unit tests - Rust backend parity test - Cross-backend parity diff - Frontend type-checking
Cross-compile for Linux from macOS
# Install target
rustup target add x86_64-unknown-linux-gnu
# Install linker (Homebrew)
brew install FiloSottile/musl-cross/musl-cross
# Build
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-musl-gcc \
cargo build --release --target x86_64-unknown-linux-gnu --bin wactorz
Alternatively, use Docker buildx (no linker needed):
docker buildx build --platform linux/amd64 --tag wactorz:local --load ./rust
Frontend development
cd frontend
# Install deps
npm install
# Dev server (hot-reload, http://localhost:3000)
npm run dev
# Type-check only
npm run typecheck # or: npx tsc --noEmit
# Production build
npm run build
# → static/app/
# Preview production build locally
npm run preview
Vite dev proxy
// vite.config.ts
proxy: {
"/api": { target: "http://localhost:8080", ... },
"/ws": { target: "ws://localhost:8081", ws: true },
"/mqtt": { target: "ws://localhost:9001", ws: true },
}
This means in dev, window.location.host resolves to localhost:3000, and the MQTT URL is automatically ws://localhost:3000/mqtt → proxied to Mosquitto.
Adding a new UI theme
- Create
frontend/src/scene/themes/MyTheme.tsextendingThemeBase - Add a node type in
frontend/src/scene/nodes/ - Register in
SceneManager.ts:typescript case "my-theme": this.activeTheme = new MyTheme(this.scene, ...); break; - Add a button in
index.htmlandThemeSwitcher.ts
Debugging tips
MQTT messages not arriving in the browser
# Subscribe to all topics from the terminal
mosquitto_sub -h localhost -p 1883 -t '#' -v
# Or check the mock is publishing
docker compose -f compose.dev.yaml logs mock-agents
Rust actor not responding
RUST_LOG=wactorz=debug cargo run --bin wactorz
# Look for: "[agent-name] handle_message" or heartbeat logs
TypeScript errors
cd frontend && npx tsc --noEmit
MQTT URL wrong in browser
Check browser console — the MQTTClient logs its connection URL. In dev it should be ws://localhost:3000/mqtt. Override with VITE_MQTT_WS_URL in .env if needed.