RuView Docker & Self-Hosted Deployment

GitHub

ฮ  RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection โ€” all without a single pixel of video.

RAW Compose
1. Pull Image (DOCKERHUB) docker pull ruvnet/wifi-densepose
2. Docker Run (1-Click Launch) docker run -d --name ruview -p 3000:3000 --restart always ruvnet/wifi-densepose:latest
3. Podman Run podman run -d --name ruview -p 3000:3000 --restart always ruvnet/wifi-densepose:latest
4. Skopeo Inspect (Zero-Pull Manifest) skopeo inspect docker://docker.io/ruvnet/wifi-densepose:latest
5. ORAS Pull (OCI Artifact) oras pull ruvnet/wifi-densepose:latest
6. Compose Deployment docker compose up -d
docker/docker-compose.yml
Download .yml
services:
  sensing-server:
    build:
      context: ..
      dockerfile: docker/Dockerfile.rust
    image: ruvnet/wifi-densepose:latest
    # ESP32 CSI must accept LAN UDP; TCP APIs below remain loopback-only.
    # kics-scan ignore-line
    ports:
      - "127.0.0.1:3000:3000"   # REST API
      - "127.0.0.1:3001:3001"   # WebSocket
      # ESP32 UDP. On Linux/macOS this works with multiple ESP32 nodes out of
      # the box. On Docker Desktop for Windows, multi-source UDP is collapsed
      # to one source IP at the WSL/Hyper-V boundary, so all-but-one node's
      # frames are silently dropped (issue #374, #386).
      #
      # Windows workaround: change this to "5006:5005/udp" and run the host
      # relay so every datagram arrives from the same loopback source:
      #
      #   python scripts/udp-relay.py --listen-port 5005 --forward-port 5006
      #
      # See docs/TROUBLESHOOTING.md ยง9 for details.
      - "5005:5005/udp"
    environment:
      - RUST_LOG=info
      # CSI_SOURCE controls the data source for the sensing server.
      # Options: auto (default) โ€” probe for ESP32 UDP then host WiFi; **fail
      #                           hard with exit 78 if neither is detected**.
      #                           Synthetic data is no longer a silent fallback
      #                           (issue #937 fix) โ€” operators must opt in.
      #          esp32          โ€” receive real CSI frames from an ESP32 on UDP port 5005
      #          wifi           โ€” use host Wi-Fi RSSI/scan data (Windows netsh)
      #          simulated      โ€” explicitly generate synthetic CSI for demo mode
      - CSI_SOURCE=${CSI_SOURCE:-auto}
      # MODELS_DIR controls where the server scans for .rvf model files.
      # Mount a host directory and set this to make models visible:
      #   volumes: ["/path/to/models:/app/models"]
      #   MODELS_DIR=/app/models
      - MODELS_DIR=${MODELS_DIR:-data/models}
      # Auth (issue #864). The entrypoint refuses to start (exit 64) unless
      # RUVIEW_API_TOKEN is set. Export it before `docker compose up`:
      #   export RUVIEW_API_TOKEN=$(openssl rand -hex 32)
      # Bare names pass the host value through only when it is set.
      - RUVIEW_API_TOKEN
      # ESP32 UDP ingest (ADR-296, issue #2089). The UDP receiver defaults to
      # 127.0.0.1, which cannot receive from the published 5005/udp port.
      # Set ONE source guard and the entrypoint binds 0.0.0.0:
      #   RUVIEW_UDP_ALLOW=<node-ip-or-cidr>  restrict sources (preferred)
      #   RUVIEW_UDP_INSECURE_LAN=true        accept the spoofing risk
      # Docker Desktop and OrbStack on macOS rewrite the source address to the
      # bridge gateway, so a LAN-subnet allowlist drops every frame. There,
      # allow the gateway instead:
      #   docker network inspect <project>_default \
      #     --format '{{(index .IPAM.Config 0).Gateway}}'
      #   RUVIEW_UDP_ALLOW=<gateway>/32
      # The gateway admits anything that reaches the published port, so keep
      # 5005/udp off untrusted networks.
      - RUVIEW_UDP_ALLOW
      - RUVIEW_UDP_INSECURE_LAN
      - RUVIEW_UDP_BIND
      # Browsing the UI from another machine also needs the host name or IP
      # in SENSING_ALLOWED_HOSTS (otherwise 421), and the TCP ports above
      # published beyond 127.0.0.1.
      - SENSING_ALLOWED_HOSTS
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 1G
    healthcheck:
      test: ["CMD-SHELL", "kill -0 1"]
      interval: 30s
      timeout: 3s
      retries: 3
    # No explicit command needed โ€” docker-entrypoint.sh uses CSI_SOURCE.
    # Override with: command: ["--source", "esp32", "--tick-ms", "500"]

  python-sensing:
    build:
      context: ..
      dockerfile: docker/Dockerfile.python
    image: ruvnet/wifi-densepose:python
    ports:
      - "127.0.0.1:8765:8765"   # WebSocket
      - "127.0.0.1:8080:8080"   # UI
    environment:
      - PYTHONUNBUFFERED=1
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 512M
    healthcheck:
      test: ["CMD", "python", "-c", "import socket; socket.create_connection(('127.0.0.1', 8765), 2).close()"]
      interval: 30s
      timeout: 3s
      retries: 3