# Autonomous Retail Infrastructure — Deployment Runbook
Physical Hardware (Jetson cluster)
│ MQTT over LAN (sub-10ms)
▼
Edge Engine (C++ binary on Jetson)
│ WebSocket / HTTPS
▼
Cloud API Server (Node.js, Docker)
│ REST / WebSocket
▼
Owner Dashboard (React, browser)
autonomous-retail/
├── edge-engine/
│ ├── include/
│ │ ├── retail_types.hpp # All shared types and constants
│ │ ├── spsc_ring_buffer.hpp # Lock-free sensor event buffer
│ │ ├── item_catalog.hpp # O(1) SKU weight profile lookup
│ │ ├── session_manager.hpp # 50+ concurrent session lifecycle
│ │ ├── sensor_fusion_engine.hpp # Weight × camera corroboration engine
│ │ ├── reconciliation_engine.hpp # Checkout, margin, inventory sync
│ │ ├── mqtt_subscriber.hpp # Paho MQTT topic routing
│ │ └── sqlite_store.hpp # WAL-mode SQLite persistence
│ ├── src/
│ │ └── edge_engine.cpp # Main orchestrator + thread pool
│ └── CMakeLists.txt # aarch64 Jetson build config
├── db-schemas/
│ ├── edge_schema.sql # SQLite schema (WAL, indices, views)
│ └── cloud_schema.sql # PostgreSQL schema (partitioned, RLS)
├── cloud-api/
│ ├── server.js # Node.js API + WebSocket relay
│ ├── package.json
│ └── Dockerfile
├── dashboard/
│ ├── src/
│ │ ├── App.jsx # Root — live/mock mode toggle
│ │ ├── main.jsx
│ │ ├── index.css
│ │ ├── OwnerDashboard.jsx # Full dashboard with 4 tabs
│ │ ├── contexts/
│ │ │ └── WebSocketContext.jsx # Live WS state + reducer
│ │ └── hooks/
│ │ └── useMockData.js # Simulation hook (dev/demo)
│ ├── index.html
│ ├── package.json
│ ├── vite.config.js
│ └── tailwind.config.js
├── infra/
│ ├── mosquitto.conf # MQTT broker config
│ └── nginx.conf # Production reverse proxy
└── docker-compose.yml # Full stack orchestration
sudo apt update
sudo apt install -y \
build-essential cmake ninja-build \
libsqlite3-dev libpaho-mqtt3-dev \
libcurl4-openssl-dev libwebsockets-dev \
pkg-configcd edge-engine
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DJETSON_ARCH=armv8.2-a \
-DBUILD_TESTS=OFF
cmake --build build -j$(nproc)Copy and edit the environment file:
cp .env.example .env| Variable | Description | Default |
|---|---|---|
MQTT_BROKER |
MQTT broker address | tcp://127.0.0.1:1883 |
SQLITE_DB_PATH |
Edge SQLite database path | /data/retail_edge.db |
CLOUD_SYNC_URL |
Cloud API base URL for sync | http://cloud-api:3001 |
EDGE_SECRET |
Shared secret for cloud API auth | (required) |
STORE_ID |
Numeric store identifier | 1 |
WS_PORT |
WebSocket gateway port | 8080 |
sudo cp infra/retail-edge.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now retail-edge
sudo journalctl -u retail-edge -f- Docker 24+ and Docker Compose v2
- A server with at least 2 GB RAM (4 GB recommended)
# Create environment file
cp .env.example .env
# Edit: set PG_PASSWORD, EDGE_SECRET, DASHBOARD_ORIGIN
# Start all services
docker compose up -d
# Apply schema (runs automatically via docker-entrypoint-initdb.d on first start)
# Verify:
docker compose exec postgres psql -U retail_app -d retail -c "\dt"docker compose --profile dev up
# Dashboard: http://localhost:5173
# API: http://localhost:3001
# MQTT: localhost:1883# Build dashboard assets first
cd dashboard && npm ci && npm run build && cd ..
docker compose --profile prod up -d
# Dashboard: http://localhost (nginx)cd dashboard
npm install
VITE_MOCK_DATA=true npm run dev
# Opens http://localhost:5173 with simulated sensor eventsTo connect to a live edge node or cloud API:
VITE_MOCK_DATA=false \
VITE_WS_URL=ws://your-cloud-api:3001 \
npm run dev| Topic | Publisher | QoS | Payload struct |
|---|---|---|---|
shelf/<shelf_id>/<slot>/weight |
Load cell node | 1 | WireShelfPayload |
camera/<cam_id>/event |
Jetson camera AI | 1 | WireCameraPayload |
door/<door_id>/scan |
Door controller | 2 | WireDoorPayload |
terminal/<terminal_id>/checkout |
Payment terminal | 2 | WireCheckoutPayload |
WireShelfPayload → 20 bytes (packed)
WireCameraPayload → 44 bytes (packed)
WireDoorPayload → 64 bytes (fixed, null-padded token)
WireCheckoutPayload→ 24 bytes (packed)
All payloads are binary-packed structs (see mqtt_subscriber.hpp).
No JSON on the sensor bus — minimum wire overhead for 50+ concurrent sessions.
After first startup, populate the planogram table to map shelf slots to SKUs and camera zones:
-- Example: Shelf 1, Slot 0 = Sparkling Water, covered by Camera 1
INSERT INTO planogram (shelf_id, slot_id, sku, camera_id, zone_label)
VALUES (1, 0, 1001, 1, 'aisle-1-left');The edge engine loads this at startup. To reload without restart:
send a SIGHUP to the retail_edge process.
| Scenario | Engine behaviour |
|---|---|
| Weight event, no camera within 2s | Cart NOT updated; WARNING incident raised for human review |
| Camera anomaly score ≥ 0.85 | Session SUSPENDED immediately; CRITICAL alert pushed to owner |
| Camera anomaly score 0.60–0.84 | Session monitored; WARNING incident logged |
| Unknown item weight (no SKU match) | Weight event held; WARNING incident with weight value |
| Store at 50+ session capacity | Door scan rejected; entry denied until a session closes |
| Cloud sync failure | Transactions buffered in SQLite; retried with exponential backoff |
| Edge process restart | SQLite WAL preserves all committed events; sessions rebuilt from DB |
# Active sessions
sqlite3 /data/retail_edge.db "SELECT COUNT(*) FROM sessions WHERE state=2;"
# Unsynced transactions
sqlite3 /data/retail_edge.db "SELECT COUNT(*) FROM transactions WHERE synced_to_cloud=0;"
# Recent incidents
sqlite3 /data/retail_edge.db \
"SELECT level, description FROM security_incidents ORDER BY timestamp_us DESC LIMIT 10;"curl http://localhost:3001/health
# → {"status":"ok","store_id":1,"ts":1749123456789}SELECT * FROM mv_daily_revenue ORDER BY day DESC LIMIT 7;- Rotate
EDGE_SECRETfrom default value - Enable MQTT TLS on port 8883 (
mosquitto.conf— addcertfile/keyfile) - Set
POSTGRES_PASSWORDto a strong random value - Enable PostgreSQL SSL (
sslmode=requireinDATABASE_URL) - Set
DASHBOARD_ORIGINto your actual domain (no wildcard*) - Add HTTPS to nginx with Let's Encrypt (
certbot --nginx) - Restrict
allow_anonymous falsein Mosquitto and add a password file - Enable Jetson Secure Boot for hardware attestation
- Configure automated PostgreSQL backups (
pg_dumpto S3 or equivalent) - Set up pg_cron for
fn_refresh_dashboard_views()every 5 minutes - Pin Docker image digests (replace
:latesttags)