# Sprint 2.5 — RBAC + Studio mínimo + Flash Console

> Sprint nuevo añadido a petición del Super Admin (Álvaro). Cubre dos
> requisitos que no estaban en el brief original:
>
> 1. Sistema de 4 roles (Super Admin / Engineer / Owner / User) con
>    capabilities específicas y verificación de doble factor para
>    operaciones críticas.
> 2. Una "mini consola de Arduino" dentro del Studio que permita al
>    Engineer flashear firmware ESP32 con aprobación del Super Admin.

## Objetivo

Dejar Studio bootable con login por PIN, RBAC funcional, y la Flash
Console operativa. Engineer puede flashear firmware mostrando el PIN del
Super Admin en una ventana de confirmación. Super Admin (solo Álvaro)
flashea directamente sin segundo factor.

## Roles y capabilities

| Capability | Super Admin | Engineer | Owner | User |
|---|---|---|---|---|
| Editar código Python del proyecto (arautopilot, tools, scripts) | ✅ | ❌ | ❌ | ❌ |
| Editar código C++ del firmware (firmware/**) | ✅ | ✅ (sólo en su sandbox) | ❌ | ❌ |
| **Flashear firmware ESP32** | ✅ directo | ✅ con SA approval (2-of-N) | ❌ | ❌ |
| Editar gains PID base (IP del integrator) | ✅ | ❌ | ❌ | ❌ |
| Editar comisionado (rudder limits, calibración) | ✅ | ✅ | ❌ | ❌ |
| Editar preferencias operativas (rumbos fav, alarm vol, perfil) | ✅ | ✅ | ✅ | ❌ |
| Crear/gestionar Users del barco | ✅ | ❌ | ✅ | ❌ |
| Engage/disengage piloto | ✅ | ✅ | ✅ | ✅ |
| Leer telemetría | ✅ | ✅ | ✅ | ✅ |
| Acknowledge alarmas | ✅ | ✅ | ✅ | ✅ |
| Ver audit log completo | ✅ | ✅ | parcial (sólo su barco) | ❌ |

## Plan de implementación

### 1. RBAC core (`arautopilot/core/rbac.py`)

- Enum `Role`: SUPER_ADMIN / ENGINEER / OWNER / USER
- Enum `Capability`: cada acción gateable (FLASH_FIRMWARE, EDIT_BASE_GAINS,
  EDIT_COMMISSIONING, EDIT_OPERATIONAL, MANAGE_USERS, ENGAGE_PILOT,
  READ_TELEMETRY, ACK_ALARMS, VIEW_AUDIT_LOG_FULL)
- Matriz inmutable `_CAPABILITIES_BY_ROLE: dict[Role, frozenset[Capability]]`
- Función `requires_dual_auth(cap: Capability) -> bool`: True para
  FLASH_FIRMWARE cuando el actor es Engineer
- `has(role, capability) -> bool`

### 2. User model (`arautopilot/core/user.py`)

- Pydantic v2 `User`: user_id (UUID), display_name, role, pin_hash
  (argon2 o pbkdf2), created_at, last_login_at, active (bool)
- `set_pin(plain) / verify_pin(plain) -> bool` con hashing seguro

### 3. Audit log (`arautopilot/core/audit.py`)

- `AuditEvent`: timestamp UTC, user_id, action, target, outcome
  (success/denied/failed), reason, secondary_user_id (si dual-auth)
- `AuditLog` con append-only file en JSONL
- Cada permission check produce un evento

### 4. Studio mínimo (`arautopilot/studio/app.py`)

- Reemplaza el stub Sprint 0
- PySide6 QApplication + login window
- Main window con 3 áreas: sidebar (rol + user), main area (placeholder
  para project editor), toolbar (acciones gateadas por rol)
- Login = elegir user de lista + PIN
- Persistencia local (SQLite o JSON) de la lista de users

### 5. Flash Console (`arautopilot/studio/flash_console.py`)

- Widget dentro del Studio (no app separada)
- Lista puertos serie (vía pyserial)
- Combobox de variant (esp32-dev / esp32-debug)
- Botones: "Compile only", "Compile + Flash", "Open Serial Monitor"
- Para Engineer: al pulsar Flash, abre modal "Esperando aprobación del
  Super Admin" con form de PIN del SA. El SA introduce su PIN (si está
  presente) o el operario llama por teléfono y SA dicta un OTP que el SA
  generó desde otro Studio. Por simplicidad MVP: campo de PIN del SA en
  el modal.
- Bajo el capó: corre `pio run -t upload --upload-port COMx` y stream
  del output al widget
- Serial monitor: thread aparte que lee `pio device monitor`

### 6. Tests

- `test_rbac.py`: cada rol tiene exactamente las capabilities esperadas;
  intentos de escalada rechazados; dual-auth requerido para Engineer
  flash.
- `test_user.py`: PIN hash, verificación correcta/incorrecta, no se
  puede deserializar un User sin pin_hash.
- `test_audit.py`: append es atómico, lectura es ordenada por timestamp.

## Restricciones

- No requiere hardware ESP32 conectado para que arranque el Studio.
- La Flash Console **no funciona** sin board conectada (pero arranca,
  muestra "no boards found"). Funcionalidad real verificable cuando el
  usuario conecte hardware.
- PIN del Super Admin se introduce en cada flash (no se cachea). Sí se
  puede cachear PIN del operario actual durante la sesión.
- El binario flasheado por el Engineer debe ser uno compilado por el SA y
  firmado (futuro Sprint 8). Sprint 2.5 hace la versión simple: Engineer
  compila + flashea con PIN del SA.

## Verificación

- `pytest` verde (objetivo: 145+ tests)
- `python studio_main.py` abre la ventana de login sin errores
- Login con un user dummy de demo (`demo_users.json`) funciona
- Flash Console widget se renderiza (incluso sin board)
