secubox-deb/docs/wiki/Eye-Remote-Implementation.md
CyberMind-FR 0a199ae362 docs(eye-remote): add bootstrap references to existing wiki pages
- Add "Bootstrap Role (v2.1.0)" section to Eye-Remote-Implementation.md with capabilities, use cases, architecture, configuration, workflow, and security considerations
- Update Table of Contents in Eye-Remote-Implementation.md to include bootstrap section
- Add bootstrap reference to Eye Remote section in Home.md
- Add bootstrap functionality note to Architecture-Boot.md with cross-reference to Eye-Remote-Bootstrap.md

This cross-references the new Eye-Remote-Bootstrap feature across related documentation pages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-23 11:49:07 +02:00

583 lines
17 KiB
Markdown

# Eye Remote v2.0.0 Implementation Guide
## Table of Contents
1. [Project Overview](#project-overview)
2. [Implementation Timeline](#implementation-timeline)
3. [Architecture Design](#architecture-design)
4. [Component Details](#component-details)
5. [Build System](#build-system)
6. [Display Configuration](#display-configuration)
7. [API Reference](#api-reference)
8. [Development Tools](#development-tools)
9. [Bootstrap Role](#bootstrap-role)
10. [Deployment](#deployment)
11. [Lessons Learned](#lessons-learned)
---
## Project Overview
### Purpose
SecuBox Eye Remote provides a dedicated physical display for SecuBox security appliances. The circular 480x480 LCD shows real-time system metrics, alerts, and status information.
### Goals Achieved
- [x] Offline-capable SD card image (no internet at boot)
- [x] USB OTG connectivity with fallback to WiFi
- [x] Secure device pairing via QR codes
- [x] Real-time metrics dashboard
- [x] Chromium kiosk mode auto-start
- [x] HyperPixel 2.1 Round display support
- [x] Gateway emulator for development
### Tech Stack
| Layer | Technology |
|-------|------------|
| OS | Raspberry Pi OS Bookworm (armhf) |
| Display | X11 + fbdev + DPI framebuffer |
| Window Manager | Openbox |
| Browser | Chromium (kiosk mode) |
| Backend | FastAPI + Uvicorn |
| Auth | JWT + Device Tokens |
| Build | QEMU ARM emulation |
---
## Implementation Timeline
### Task Breakdown (14 Tasks)
| Task | Component | Status |
|------|-----------|--------|
| 1 | Metrics Models (`models/metrics.py`) | ✅ |
| 2 | Device Models (`models/device.py`) | ✅ |
| 3 | Pairing Models (`models/pairing.py`) | ✅ |
| 4 | Metrics Collection (`core/metrics.py`) | ✅ |
| 5 | Device Registry (`core/device_registry.py`) | ✅ |
| 6 | Pairing Manager (`core/pairing.py`) | ✅ |
| 7 | Devices Router (`api/routers/devices.py`) | ✅ |
| 8 | Pairing Router (`api/routers/pairing.py`) | ✅ |
| 9 | Metrics Router (`api/routers/metrics.py`) | ✅ |
| 10 | Gateway Emulator (`tools/secubox-eye-gateway/`) | ✅ |
| 11 | Debian Packaging (`debian/`) | ✅ |
| 12 | Build Script Update | ✅ |
| 13 | Integration Tests | ✅ |
| 14 | HyperPixel Fix (Legacy DPI) | ✅ |
### Development Approach
Used **Subagent-Driven Development**:
1. Fresh subagent dispatched per task
2. Spec compliance review after implementation
3. Code quality review before completion
4. Two-stage review ensures correctness
---
## Architecture Design
### System Architecture
```
SecuBox Appliance
┌─────────────────────────┐
│ │
│ ┌─────────────────┐ │
│ │ secubox-eye- │ │
│ │ remote.service │ │
│ │ │ │
│ │ FastAPI:8000 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ USB Host │ │
│ │ 10.55.0.1 │ │
│ └────────┬────────┘ │
│ │ │
└───────────┼────────────┘
│ USB OTG
│ (ECM + ACM)
┌───────────┼────────────┐
│ │ │
│ ┌────────▼────────┐ │
│ │ USB Gadget │ │
│ │ 10.55.0.2 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ nginx :8080 │ │
│ │ proxy → :8000 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Chromium Kiosk │ │
│ │ localhost:8080 │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ HyperPixel │ │
│ │ 480x480 LCD │ │
│ └─────────────────┘ │
│ │
│ RPi Zero W │
└─────────────────────────┘
```
### Data Flow
```
┌──────────┐ GET /metrics ┌──────────┐
│ Dashboard│◄──────────────────►│ FastAPI │
│ (JS) │ JSON response │ Backend │
└────┬─────┘ └────┬─────┘
│ │
│ fetch() every 2s │ reads /proc/*
│ │ os.statvfs()
▼ ▼
┌──────────┐ ┌──────────┐
│ Canvas │ │ Linux │
│ Rings │ │ Kernel │
└──────────┘ └──────────┘
```
---
## Component Details
### Models Layer
#### `models/metrics.py`
```python
class SystemMetrics(BaseModel):
cpu_percent: float # 0-100
memory_percent: float # 0-100
disk_percent: float # 0-100
cpu_temp: float # Celsius
load_avg_1: float # 1-minute load
uptime_seconds: int
hostname: str
timestamp: datetime
```
#### `models/device.py`
```python
class Device(BaseModel):
id: str # UUID
name: str
device_type: str # "eye-remote"
paired_at: datetime
last_seen: datetime
is_active: bool
class DeviceToken(BaseModel):
device_id: str
token_hash: str # SHA256
created_at: datetime
expires_at: Optional[datetime]
```
#### `models/pairing.py`
```python
class PairingSession(BaseModel):
session_id: str
gateway_url: str
expires_at: datetime
qr_data: str # JSON for QR code
class PairingRequest(BaseModel):
session_id: str
device_name: str
device_type: str = "eye-remote"
```
### Core Layer
#### `core/metrics.py`
- Reads CPU from `/proc/stat` (delta calculation)
- Reads memory from `/proc/meminfo`
- Reads disk via `os.statvfs('/')`
- Reads temperature from `/sys/class/thermal/thermal_zone0/temp`
- Caches results for 1 second (async)
#### `core/device_registry.py`
- Stores devices in `/var/lib/secubox/eye-remote/devices.json`
- Token validation with `secrets.compare_digest()`
- SHA256 token hashing
- Thread-safe file operations
#### `core/pairing.py`
- 5-minute session TTL
- QR code contains: gateway URL, session ID, timestamp
- Single-use sessions (consumed on completion)
### API Layer
#### Authentication
```python
# JWT for management endpoints
async def require_jwt(token: str = Depends(oauth2_scheme)):
payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
return payload
# Device token for metrics endpoint
async def require_device_token(x_device_token: str = Header(...)):
device = registry.validate_token(x_device_token)
return device
```
#### Endpoints
| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| GET | `/devices/` | JWT | List paired devices |
| POST | `/devices/pair` | JWT | Pair new device |
| DELETE | `/devices/{id}` | JWT | Unpair device |
| POST | `/pairing/start` | JWT | Start pairing session |
| GET | `/pairing/qr/{session_id}` | JWT | Get QR code PNG |
| POST | `/pairing/complete` | None | Complete pairing (from device) |
| GET | `/metrics/` | Device Token | Get system metrics |
---
## Build System
### QEMU-Based Offline Build
The build script creates a complete SD card image with all packages pre-installed using QEMU ARM emulation:
```bash
┌─────────────────────────────────────────────────────┐
│ Build Process │
├─────────────────────────────────────────────────────┤
│ 1. Decompress RPi OS Lite image │
│ 2. Expand image by 1GB for packages │
│ 3. Mount via loopback device │
│ 4. Copy qemu-arm-static for ARM emulation │
│ 5. chroot and apt-get install packages │
│ 6. Configure HyperPixel (legacy DPI mode)
│ 7. Install Eye Remote services │
│ 8. Configure kiosk (LightDM + Openbox + Chromium)
│ 9. Cleanup and create final image │
└─────────────────────────────────────────────────────┘
```
### Packages Pre-installed
```
chromium-browser xserver-xorg xinit
lightdm openbox unclutter
nginx python3-pigpio pigpio
i2c-tools fonts-dejavu-core
```
---
## Display Configuration
### The HyperPixel Problem
**Issue:** Pi Zero W does not support KMS (Kernel Mode Setting) properly.
**Symptom:** Black screen with KMS overlays (`vc4-kms-v3d`, `vc4-kms-dpi-hyperpixel2r`).
**Solution:** Use legacy DPI mode with manual pixel timings.
### Working Configuration
```ini
# /boot/config.txt
# Legacy DPI overlay (NOT KMS)
dtoverlay=hyperpixel2r
# Manual DPI timings for ST7701S LCD
enable_dpi_lcd=1
display_default_lcd=1
dpi_group=2
dpi_mode=87
dpi_output_format=0x7f216
dpi_timings=480 0 10 16 55 480 0 15 60 15 0 0 0 60 0 19200000 6
framebuffer_width=480
framebuffer_height=480
```
### ST7701S LCD Initialization
The HyperPixel 2.1 Round uses an ST7701S LCD controller that requires SPI initialization at boot:
```python
# /usr/bin/hyperpixel2r-init (simplified)
import pigpio
pi = pigpio.pi()
# Bit-bang SPI commands to ST7701S
# Initialize display registers
# Enable backlight
pi.stop()
```
### Service Dependencies
```
pigpiod.service
└── hyperpixel2r-init.service
└── lightdm.service
└── chromium (kiosk)
```
---
## API Reference
### GET /api/v1/eye-remote/metrics/
**Auth:** `X-Device-Token` header
**Response:**
```json
{
"cpu_percent": 23.5,
"memory_percent": 45.2,
"disk_percent": 28.0,
"cpu_temp": 48.3,
"load_avg_1": 0.42,
"uptime_seconds": 86400,
"hostname": "secubox-pro",
"timestamp": "2026-04-21T20:30:00Z"
}
```
### POST /api/v1/eye-remote/pairing/start
**Auth:** JWT Bearer token
**Response:**
```json
{
"session_id": "abc123",
"expires_at": "2026-04-21T20:35:00Z",
"qr_url": "/api/v1/eye-remote/pairing/qr/abc123"
}
```
### POST /api/v1/eye-remote/pairing/complete
**Auth:** None (uses session_id)
**Request:**
```json
{
"session_id": "abc123",
"device_name": "Living Room Eye",
"device_type": "eye-remote"
}
```
**Response:**
```json
{
"device_id": "dev_xyz789",
"token": "raw_token_for_device_storage",
"gateway_url": "http://10.55.0.1:8000"
}
```
---
## Development Tools
### Gateway Emulator
Simulates SecuBox metrics without physical hardware:
```bash
# Install
pip install -e tools/secubox-eye-gateway/
# Run with profile
secubox-eye-gateway --profile stressed --port 8765
# Profiles:
# idle - Low activity (CPU ~5%, temp ~40°C)
# normal - Typical load (CPU ~25%, temp ~50°C)
# busy - Heavy load (CPU ~65%, temp ~62°C)
# stressed - Near limits (CPU ~85%, temp ~72°C)
```
### Emulator Endpoints
| Endpoint | Description |
|----------|-------------|
| `GET /api/v1/health` | Health check |
| `GET /api/v1/system/metrics` | Simulated metrics |
| `GET /api/v1/eye-remote/discover` | Discovery info |
---
## Bootstrap Role (v2.1.0)
The Eye Remote can serve as a boot device for ESPRESSObin and MOCHAbin appliances, providing critical functionality during system initialization and recovery:
### Capabilities
- **Mass Storage LUN** — U-Boot loads kernel/DTB/initrd from active slot via USB Mass Storage
- **TFTP Shadow** — Test new images before promoting to active slot
- **Atomic Swap** — Safe active/shadow slot exchange with rollback protection
- **Boot Menu** — Interactive selection of kernel versions and boot parameters
### Use Cases
| Use Case | Benefit |
|----------|---------|
| **Factory Reset** | Boot fresh image without physical SD cards |
| **Recovery Boot** | Fallback when internal storage is corrupted |
| **Image Testing** | Try new Debian images before committing |
| **Firmware Updates** | Safe A/B slot switching with 4R rollback |
### Architecture
The Eye Remote exposes two USB LUNs:
1. **LUN 0** — Mass Storage (active boot image)
- Kernel + DTB + initrd partition
- Auto-synced from active slot
2. **LUN 1** — Shadow Test Storage
- Staging area for new images
- Isolated from live system
### Configuration
Bootstrap is configured in `/etc/secubox/eye-remote.toml`:
```toml
[bootstrap]
enabled = true
mass_storage_lun = 0
shadow_lun = 1
auto_sync_interval = 300 # 5 minutes
max_rollback_slots = 4
```
### Workflow
```
1. User initiates image test via dashboard
2. Eye Remote downloads image to shadow LUN
3. User selects "Boot from Shadow" in U-Boot menu
4. System tests image on next reboot
5. User confirms success → Image promoted to active
6. Or rejects → Automatic rollback to previous active
```
### Security Considerations
- Bootstrap operations are **JWT-authenticated** only
- All slot operations are **logged** to audit trail
- Image integrity verified via **SHA256 checksums**
- Rollback protected against **malicious substitution** via timestamp validation
---
**Full documentation:** See [Eye-Remote-Bootstrap](Eye-Remote-Bootstrap.md) for detailed implementation, testing procedures, and troubleshooting.
---
## Deployment
### SD Card Flashing
```bash
# Build image
sudo ./remote-ui/round/build-eye-remote-image.sh \
-i raspios-lite-armhf.img.xz \
-s "WiFiSSID" -p "password"
# Flash
sudo dd if=/tmp/secubox-eye-remote-2.0.0.img \
of=/dev/sdX bs=4M status=progress
# Or compressed
xz -9 /tmp/secubox-eye-remote-2.0.0.img
xzcat *.img.xz | sudo dd of=/dev/sdX bs=4M status=progress
```
### First Boot Checklist
- [ ] Insert SD card
- [ ] Connect USB DATA port (middle) to SecuBox
- [ ] Wait 60 seconds
- [ ] Display shows dashboard
- [ ] SSH accessible: `ssh pi@10.55.0.2`
### OTA Updates
```bash
# Update dashboard
scp index.html pi@10.55.0.2:/var/www/secubox-round/
# Update backend (on SecuBox host)
apt update && apt install secubox-eye-remote
# Restart services
systemctl restart secubox-eye-remote
```
---
## Lessons Learned
### 1. KMS vs Legacy DPI
**Problem:** Assumed KMS would work everywhere since it's the "modern" approach.
**Reality:** Pi Zero W (BCM2835) has limited GPU support. KMS overlays cause black screen.
**Solution:** Always test on actual hardware. Use legacy DPI mode for Pi Zero W.
### 2. ST7701S Initialization
**Problem:** Display stayed black even with correct overlay.
**Reality:** ST7701S LCD controller requires SPI commands at boot.
**Solution:** `hyperpixel2r-init` script using pigpio for GPIO/SPI access.
### 3. QEMU Package Installation
**Problem:** Pi Zero W is slow. Installing packages at first boot takes forever.
**Reality:** ARM emulation via QEMU allows pre-installing packages during image build.
**Solution:** QEMU chroot in build script. Offline-capable images.
### 4. Token Security
**Problem:** Device tokens stored in plain text.
**Reality:** Compromised token = unauthorized access.
**Solution:** SHA256 hash stored server-side. `secrets.compare_digest()` for timing-safe comparison.
---
## References
- [Pimoroni HyperPixel 2.1 Round](https://github.com/pimoroni/hyperpixel2r)
- [Raspberry Pi DPI Display](https://www.raspberrypi.com/documentation/computers/raspberry-pi.html#parallel-display-interface-dpi)
- [pigpio Library](http://abyz.me.uk/rpi/pigpio/)
- [FastAPI Documentation](https://fastapi.tiangolo.com/)
---
*CyberMind · SecuBox Eye Remote v2.0.0 · April 2026*