Files
le-mystere-professeur-zacus/docs/DEPLOYMENT_RUNBOOK.md
L'électron rare 20aed903ba feat: AI integration — voice pipeline, hints engine, MCP server, analytics, security
- Voice pipeline: ESP32 WebSocket client → voice bridge → LLM → Piper TTS (Tower :8001)
- Hints engine: 3 puzzles (LA_440, LEFOU_PIANO, QR_FINALE), anti-cheat, 3 hint levels
- MCP hardware server: 6 tools (puzzle, audio, LED, camera, scenario, status), stdio transport
- Analytics: ESP32 module + 6 web endpoints + Dashboard UI with chat interface
- Security: auth middleware (Bearer NVS), rate limiting, input validation on 30 endpoints
- Frontend: code-split (1.1MB → 210KB initial), ErrorBoundary, API timeout, WS reconnect
- Tests: 24 Python + 38 TypeScript + 18 MCP = 80 project tests (+ 19 mascarade)
- Specs: AI_INTEGRATION_SPEC, MCP_HARDWARE_SERVER_SPEC, QA_TEST_MATRIX_SPEC
- Docs: SECURITY, DEPLOYMENT_RUNBOOK, voice pipeline guide, AI architecture map
- 6 AI agent definitions (.github/agents/ai_*.md)
- TUI orchestration script (tools/dev/zacus_tui.py)
- Docker compose TTS for Tower + KXKM-AI
- CHANGELOG, README, mkdocs.yml updated
- Cycle detection (DFS) in runtime3 validator
- Sprint plan: plans/SPRINT_AI_INTEGRATION.md

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 13:52:45 +01:00

12 KiB

Deployment Runbook

Status

  • State: draft
  • Date: 2026-03-21
  • Audience: Game master, field technician
  • Related: docs/QUICKSTART.md, docs/SECURITY.md

1) Pre-Deployment Checklist

Hardware

  • ESP32-S3 Freenove board powered and accessible via USB
  • Speaker connected to I2S output (verified with test tone)
  • LED strips connected and addressed (WS2812B data pin)
  • Camera OV2640 connected (if vision features enabled)
  • Microphone INMP441 connected (if voice features enabled)
  • All puzzle actuators (servos, relays) wired and tested
  • Backup ESP32 board available on-site

Network

  • WiFi access point configured and powered
  • SSID and password documented (not the default)
  • ESP32 IP address reserved (DHCP reservation or static)
  • mascarade server reachable from game WiFi (if AI features)
  • Game master device (laptop/tablet) on same network

Content

  • Scenario YAML validated: bash tools/test/run_content_checks.sh
  • Runtime 3 IR compiled: python3 tools/scenario/compile_runtime3.py game/scenarios/zacus_v2.yaml
  • Runtime 3 simulation passed: python3 tools/scenario/simulate_runtime3.py game/scenarios/zacus_v2.yaml
  • Audio files present in hardware/firmware/data/audio/
  • All audio files referenced in scenario exist on filesystem
  • Printable materials generated and printed

Security

  • API bearer token generated and stored in NVS
  • WiFi credentials stored in NVS (not hardcoded)
  • .env file configured on server with matching token
  • Serial debug disabled in production firmware
  • Review docs/SECURITY.md checklist

2) Firmware Flash Procedure

2.1 Prerequisites

# Install PlatformIO CLI (if not installed)
pip install platformio

# Verify toolchain
pio platform update espressif32

2.2 Build Firmware

cd hardware/firmware

# Build for Freenove ESP32-S3
pio run -e freenove_esp32s3

# Build for ESP8266 OLED (secondary display)
pio run -e esp8266_oled

2.3 Flash Firmware

# Connect ESP32-S3 via USB, identify port
ls /dev/tty.usb*        # macOS
ls /dev/ttyUSB*          # Linux

# Flash firmware + bootloader + partition table
pio run -e freenove_esp32s3 -t upload --upload-port /dev/tty.usbmodem*

# Monitor serial output to verify boot
pio device monitor -e freenove_esp32s3 --port /dev/tty.usbmodem* -b 115200

2.4 Expected Boot Output

[INFO] Zacus Runtime 3 v3.1
[INFO] Free heap: 245000 bytes
[INFO] PSRAM: 8388608 bytes
[INFO] WiFi connecting to: EscapeRoom-Net
[INFO] WiFi connected, IP: 192.168.0.50
[INFO] HTTP server started on port 8080
[INFO] Scenario loaded: ZACUS_V2 (11 steps)
[INFO] Initial step: RTC_ESP_ETAPE1
[INFO] Audio manager ready
[INFO] LED manager ready (60 LEDs)
[INFO] Ready.

2.5 Provisioning Credentials

On first boot (or after NVS erase):

# Erase NVS to force setup mode
pio run -e freenove_esp32s3 -t erase

# Flash and boot — device enters AP mode "Zacus-Setup"
pio run -e freenove_esp32s3 -t upload

# Connect to "Zacus-Setup" WiFi from your phone/laptop
# Open http://192.168.4.1 in browser
# Enter WiFi SSID, password, and API token
# Device reboots in STA mode

3) Content Deployment (LittleFS)

3.1 Prepare Filesystem Image

Content files are stored in hardware/firmware/data/:

data/
  story/
    runtime3/
      DEFAULT.json          # Runtime 3 IR (compiled)
    scenarios/
      DEFAULT.json          # Legacy scenario
  audio/
    ambient_01.mp3
    hint_01.mp3
    sfx_unlock.mp3
    ...
  config/
    device.json             # Device-specific config

3.2 Upload LittleFS Image

cd hardware/firmware

# Build and upload filesystem image
pio run -e freenove_esp32s3 -t uploadfs --upload-port /dev/tty.usbmodem*

3.3 Verify Content Upload

# Via serial
> ls /littlefs/story/runtime3/
DEFAULT.json (4523 bytes)

# Via API (requires auth)
curl -H "Authorization: Bearer <token>" http://192.168.0.50:8080/api/status
# Should show: "scenario": "ZACUS_V2", "steps": 11

3.4 Update Content Without Reflashing Firmware

For content-only updates (new scenario, audio files):

# Recompile scenario
python3 tools/scenario/compile_runtime3.py game/scenarios/zacus_v2.yaml

# Copy to firmware data directory
cp artifacts/runtime3/latest/DEFAULT.json hardware/firmware/data/story/runtime3/

# Upload only filesystem (firmware unchanged)
pio run -e freenove_esp32s3 -t uploadfs

4) Frontend Deployment

4.1 Build Frontend

cd frontend-scratch-v2

# Install dependencies
npm install

# Run tests
npm test

# Run lint
npm run lint

# Build for production
VITE_STORY_API_BASE=http://192.168.0.50:8080 npm run build

Build output is in frontend-scratch-v2/dist/.

4.2 Serve Frontend

Option A: From ESP32 (embedded)

# Copy built files to firmware data directory
cp -r frontend-scratch-v2/dist/* hardware/firmware/data/www/

# Upload filesystem
cd hardware/firmware
pio run -e freenove_esp32s3 -t uploadfs

The ESP32 serves the frontend at http://<esp32-ip>:8080/.

Option B: From separate server

# Serve with any static file server
npx serve frontend-scratch-v2/dist -l 3000

# Or via zacus.sh
./tools/dev/zacus.sh frontend-serve

4.3 Using the Shell Shortcut

# Build + lint + test in one command
./tools/dev/zacus.sh frontend-build

5) Network Setup

5.1 WiFi Access Point Configuration

Parameter Recommended Value
SSID EscapeRoom-Net (hidden optional)
Security WPA2-PSK
Channel 1, 6, or 11 (least congested)
Band 2.4 GHz (ESP32 does not support 5 GHz)
DHCP range 192.168.0.50 - 192.168.0.99
DNS Not required (local network only)

5.2 IP Address Assignments

Device IP Port Purpose
WiFi AP / Router 192.168.0.1 Gateway
ESP32 main 192.168.0.50 8080 Game device
ESP32 salle 2 192.168.0.51 8080 Secondary device
Game master laptop 192.168.0.10 3000 Dashboard
mascarade server 192.168.0.119 4001 AI backend

5.3 ESP-NOW Pairing

For multi-device setups using ESP-NOW mesh:

# On primary device (serial console)
> espnow pair
Pairing mode active. Press button on secondary device...

# On secondary device (serial console)
> espnow join
Scanning for primary...
Paired with zacus-main (MAC: AA:BB:CC:DD:EE:FF)

5.4 Verify Network Connectivity

# Ping ESP32
ping 192.168.0.50

# Check API health
curl http://192.168.0.50:8080/health
# Expected: {"status":"ok"}

# Check authenticated endpoint
curl -H "Authorization: Bearer <token>" http://192.168.0.50:8080/api/status
# Expected: full status JSON

# Check mascarade (if AI features)
curl http://192.168.0.119:4001/health

6) Post-Deployment Validation

6.1 Functional Checks

# Check Command / Action Expected
1 Device boots Power on, watch serial "Ready." message
2 WiFi connects Serial log IP address assigned
3 API responds curl /health {"status":"ok"}
4 Auth works curl /api/status with token 200 OK
5 Auth rejects curl /api/status without token 401
6 Scenario loaded Check /api/status response Correct scenario ID
7 Audio plays Trigger audio via API or button Sound from speaker
8 LEDs respond Trigger LED via API or scenario Correct colors
9 Frontend loads Open browser to device IP UI renders
10 Full walkthrough Play through scenario start to finish All transitions work

6.2 Automated Validation Script

#!/bin/bash
# tools/deploy/validate.sh
set -euo pipefail

ESP_IP="${1:-192.168.0.50}"
TOKEN="${2:-$(cat .env | grep API_TOKEN | cut -d= -f2)}"

echo "Validating deployment at $ESP_IP..."

# Health check
curl -sf "http://$ESP_IP:8080/health" | jq .status
echo "Health: OK"

# Auth check
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $TOKEN" \
  "http://$ESP_IP:8080/api/status")
[ "$HTTP_CODE" = "200" ] && echo "Auth: OK" || echo "Auth: FAIL ($HTTP_CODE)"

# Scenario check
SCENARIO=$(curl -sf -H "Authorization: Bearer $TOKEN" \
  "http://$ESP_IP:8080/api/status" | jq -r .scenario)
echo "Scenario: $SCENARIO"

# Step count
STEPS=$(curl -sf -H "Authorization: Bearer $TOKEN" \
  "http://$ESP_IP:8080/api/status" | jq .steps)
echo "Steps: $STEPS"

echo "Validation complete."

7) Rollback Procedure

7.1 Firmware Rollback

# Keep previous firmware binary
cp .pio/build/freenove_esp32s3/firmware.bin backups/firmware_$(date +%Y%m%d).bin

# To rollback:
pio run -e freenove_esp32s3 -t upload --upload-port /dev/tty.usbmodem* \
  --firmware backups/firmware_20260320.bin

7.2 Content Rollback

# Content is versioned in git
git log --oneline hardware/firmware/data/story/

# Restore previous version
git checkout HEAD~1 -- hardware/firmware/data/story/runtime3/DEFAULT.json

# Re-upload filesystem
cd hardware/firmware
pio run -e freenove_esp32s3 -t uploadfs

7.3 Frontend Rollback

# Previous builds in git
git log --oneline frontend-scratch-v2/

# Restore and rebuild
git checkout HEAD~1 -- frontend-scratch-v2/
cd frontend-scratch-v2 && npm run build

7.4 Emergency: Factory Reset

# Full erase (firmware + NVS + filesystem)
pio run -e freenove_esp32s3 -t erase

# Reflash everything from scratch
pio run -e freenove_esp32s3 -t upload
pio run -e freenove_esp32s3 -t uploadfs

# Re-provision credentials via AP mode

8) Troubleshooting Quick Reference

Symptom Likely Cause Fix
No serial output Wrong USB port or baud rate Try 115200 baud, check cable (data-capable)
Boot loop Stack overflow or panic Increase stack in platformio.ini (16 -> 24 KB)
WiFi won't connect Wrong credentials or channel Erase NVS, re-provision. Check 2.4 GHz only
WiFi keeps disconnecting Signal too weak or interference Move AP closer, change channel
API returns 401 Token mismatch Re-provision token via AP setup portal
API returns 429 Rate limited Wait 1 s, reduce request frequency
No audio I2S pin mismatch or volume 0 Check platformio.ini pin defines, set volume > 0
Audio distorted DMA buffer underrun Reduce concurrent tasks, increase DMA buffer count
LEDs wrong color GRB/RGB order mismatch Check LED type in code (WS2812B = GRB)
LEDs flicker Insufficient power supply Use dedicated 5V supply, add 100uF cap
LVGL crash Pool too small Increase to 96 KB in platformio.ini
Scenario stuck Missing transition for current state Check DEFAULT.json transitions, use serial > status
ESP-NOW not pairing Different WiFi channels Both devices must be on same channel
Camera timeout I2C conflict or power issue Check camera ribbon cable, power cycle
OOM / heap exhaustion Memory leak or too many features Monitor with > heap, disable unused features
Frontend won't connect Wrong VITE_STORY_API_BASE Rebuild with correct ESP32 IP
mascarade unreachable Network or Docker issue Check ping, docker ps, firewall rules

Serial Debug Commands

> status              # Device state, heap, uptime
> heap                # Detailed memory breakdown
> wifi                # WiFi RSSI, IP, channel
> scenario            # Current step, available transitions
> transition <event>  # Manually trigger a transition
> audio list          # List available audio files
> audio play <file>   # Play an audio file
> led test            # Cycle through LED test patterns
> reboot              # Software restart
> factory-reset       # Erase NVS + reboot into AP mode

Useful Monitoring Commands

# Watch serial output continuously
pio device monitor -e freenove_esp32s3 -b 115200

# Stream API status every 5 seconds
watch -n 5 'curl -s -H "Authorization: Bearer <token>" http://192.168.0.50:8080/api/status | jq .'

# Check WiFi signal from ESP32 perspective
curl -s -H "Authorization: Bearer <token>" http://192.168.0.50:8080/api/status | jq .wifi_rssi