# Self-contained compose for Sanad Package 2 (Premium Communication). # NO sibling folders, NO sanad-base — engine vendored from SanadV3 in ./vendor, # the flat BLE Mask lib in ./vendor/mask. This dir is all you need. # # docker compose up -d --build # build + run -> http://:8012 # docker compose logs -f # logs # docker compose down # stop # # Jetson Docker without buildx: DOCKER_BUILDKIT=0 docker compose up -d --build # # Audio: SANAD_AUDIO_PROFILE=builtin (G1 chest) | plugged (USB/Anker) # Mask: needs the BLE "Shining Mask" paired/in range + freed from the phone app. # License: point SANAD_LICENSE_FILE at your signed sanad.lic (default = example). services: p2: build: context: . dockerfile: Dockerfile args: BASE_OS_IMAGE: "${BASE_OS_IMAGE:-python:3.10-slim-bookworm}" WITH_UNITREE_SDK: "${WITH_UNITREE_SDK:-1}" image: "${SANAD_IMAGE:-sanad-p2:latest}" container_name: sanad-p2 # Host networking REQUIRED — G1 DDS link + Gemini cloud + chest audio + BlueZ. network_mode: host restart: unless-stopped # BlueZ talks over D-Bus; mask BLE needs adapter management. cap_add: - NET_ADMIN environment: SANAD_PACKAGE: P2 SANAD_DASHBOARD_PORT: "8012" SANAD_DASHBOARD_HOST: "0.0.0.0" SANAD_VOICE_BRAIN: gemini # builtin (G1 chest) needs the robot's 192.168.123.x net (network_mode host # ON a G1). OFF a G1 the chest-mic discovery crashes the voice subprocess — # set plugged on any non-G1 host. On the G1 itself, builtin is correct. SANAD_AUDIO_PROFILE: "${SANAD_AUDIO_PROFILE:-builtin}" # builtin (chest) | plugged (USB/Anker) SANAD_DDS_INTERFACE: "${SANAD_DDS_INTERFACE:-eth0}" # Plugged/Bluetooth audio (Anker USB, JBL BT) route through the HOST # PulseAudio server — chest ("builtin") uses DDS and needs none of this. PULSE_SERVER: "${PULSE_SERVER:-unix:/run/user/1000/pulse/native}" PULSE_COOKIE: "${PULSE_COOKIE:-/run/user/1000/pulse/cookie}" # root→uid-1000 PA auth SANAD_MASK_DIR: /app/mask SANAD_MASK_ADDRESS: "${SANAD_MASK_ADDRESS:-}" # optional: pin the mask BLE MAC SANAD_LICENSE: /etc/sanad/sanad.lic SANAD_PUBKEY: /etc/sanad/pubkey.ed25519 SANAD_LICENSE_BIND: "${SANAD_LICENSE_BIND:-0}" SANAD_LANGUAGE: "${SANAD_LANGUAGE:-}" # empty = multilingual auto-detect devices: - "/dev/snd:/dev/snd" # USB/plugged audio (Anker) via ALSA/Pulse - "/dev/bus/usb:/dev/bus/usb" # USB BLE dongle (if the adapter is USB) volumes: - "${SANAD_LICENSE_FILE:-./license/sanad.lic.example}:/etc/sanad/sanad.lic:ro" # Plugged/Bluetooth audio (Anker/JBL): mount the host PulseAudio runtime dir # (socket + auth cookie) so the container sets ANY sink's volume/output — the # volume slider then drives chest (DDS) AND plugged (PA), exactly like SanadV3. # Chest ("builtin") audio uses DDS only and needs none of this. One-time host # setup for a stable boot-time socket: `loginctl enable-linger unitree`. - "${SANAD_PULSE_DIR:-/run/user/1000/pulse}:/run/user/1000/pulse" - "./data:/app/Sanad/data" # persist persona/recordings on host # Persist mask face colors (mask_face writes /app/Sanad/config/mask_config.json, # otherwise a baked layer lost on recreate). Single-file mount — pre-seeded. - "./config/mask_config.json:/app/Sanad/config/mask_config.json" - "/var/run/dbus:/var/run/dbus" # host BlueZ/D-Bus for bleak (the LED mask) # Bound license (SANAD_LICENSE_BIND=1) also needs the host machine-id: # - "/etc/machine-id:/etc/machine-id:ro"