# sanad_api_g1 — copy to .env and fill in. docker compose reads it automatically. # ── fleet server (REQUIRED — YS Lootah gives you these two) ────────────────── SERVER_URL=https://fleet.example.com DEVICE_TOKEN=REPLACE_WITH_DEVICE_TOKEN # ── identity ───────────────────────────────────────────────────────────────── # This robot's fleet id — used as {sn} in POST /api/v1/fleet/ingest/{sn}/map. SN=g1_7892 # web_nav3 robot name: the maps subdir (//*.db) + X-Robot-Name. # Must match web_nav3's robot_config.yaml robot_name (default: sanad). ROBOT=sanad # ── where the map files live on the HOST (bind-mounted read-only) ──────────── # Point these at the robot's web_nav3 install. Defaults assume a Package_4 # robot (nav container -> /home/unitree/marcus_nav2_test). For the workstation # dev copy use .../Project/G1/Nav2_Projects/web_nav3/{maps,web/data}. MAPS_HOST_DIR=/home/unitree/marcus_nav2_test/maps DATA_HOST_DIR=/home/unitree/marcus_nav2_test/web/data STATE_HOST_DIR=./data/state # Optional legacy per-robot places file (older sanad setups). Leave blank if # each map already has its own places under DATA_DIR//places/. # LEGACY_PLACES=/data/legacy/places.json # ── behaviour ──────────────────────────────────────────────────────────────── # Which maps to upload: all | active | newest. active/newest need WEB_NAV3_URL. MAP_SELECT=all # Optional — only used to learn which map is ACTIVE (MAP_SELECT=active). WEB_NAV3_URL=http://127.0.0.1:8765 # Wire format the server accepts on the map endpoint: # multipart -> multipart/form-data: file field `db` + form field `meta` (JSON) # base64json -> JSON body with the .db as `db_base64` + the same meta fields MAP_UPLOAD_MODE=multipart # Endpoint path template ({sn} substituted). Change only if the server differs. MAP_ENDPOINT=/api/v1/fleet/ingest/{sn}/map # Scan cadence for the loop (seconds). Maps rarely change, so 30s is plenty. POLL_INTERVAL=30 # Verify the fleet server's TLS cert (1 recommended; 0 only for self-signed dev). VERIFY_TLS=1 HTTP_TIMEOUT=30 # ── logs + alerts ───────────────────────────────────────────────────────────── LOGS_INTERVAL=60 # ── project logs (shared alongside agent logs) ─────────────────────────────── # auto (default) = find a RUNNING Sanad project container (sanadr1, sanad-p4, # sanad*) via the /host mount and tail its docker json-log, labeled # "-logs" in the shipped lines; telemetry shows project_logs (null=none). # Or pin a container name / an explicit log file (PROJECT_LOG_PATH, via /host). PROJECT_LOG_CONTAINER=auto PROJECT_LOG_PATH= PROJECT_LOG_LABEL= # ── remote dashboard (register the Sanad web UI for the fleet to embed) ─────── # The agent probes these ports on the robot, finds the Sanad dashboard, and # POSTs its URL to /{sn}/remote (kind=web) — no changes to the Sanad app. REMOTE_ENABLE=1 REMOTE_PORTS=8001,8014,8011,8012,8013,8000,8080 REMOTE_KIND=web # REMOTE_HOST= # default: the robot's primary LAN IP (fleet-reachable) # REMOTE_URL= # pin an explicit URL (e.g. a public reverse-tunnel) — wins over discovery REMOTE_INTERVAL=60 # ── control panel (READ-ONLY loco mode; remote SWITCH is off by design) ─────── # The agent reads the Sanad dashboard's /api/controller/status (no DDS, no # motion) and reports the current mode (zero_torque/damp/lock/running) + the # switchable set in telemetry.control. CONTROL_ENABLE stays 0: remote mode- # SWITCHING is deliberately NOT built (it commands motion and can drop the robot; # switching is done in person on the Sanad dashboard). Leave URL blank to use the # auto-discovered dashboard port. CONTROL_STATUS_URL= CONTROL_ENABLE=0 # ── log-driven alerts (Gemini billing + any robot ERROR) ───────────────────── # The agent scans the robot's project (sanadr1) logs; each NEW matching signature # fires an alert (deduped per ALERT_LOG_COOLDOWN). "CODE=regex" entries split by ;; # ALERT_LOG_PATTERNS=GEMINI_BILLING=prepayment credits.{0,40}deplet|Please go to AI Studio|Failed to connect to Gemini;;ROBOT_ERROR=\bERROR\b|Traceback|Exception|CRITICAL ALERT_LOG_COOLDOWN=300 ALERT_SCAN_INTERVAL=10