# Architect — CLUB-004

## Objetivo
Permitir que un socio anónimo recupere su membresía en un dispositivo nuevo usando códigos de recuperación.

## Diseño

### Flujo
1. **Alta**: al unirse al Club se generan 3 códigos (mostrados una sola vez).
2. **Generar más**: `POST /club/recovery-codes/generate` (autenticado con device token o cuenta).
3. **Listar**: `GET /club/recovery-codes` devuelve códigos activos (fingerprints, nunca plaintext).
4. **Recuperar**: `POST /club/recover` acepta `{ code, newDeviceToken }` → consume código + vincula nuevo dispositivo.

### Formato de código
`XXXX-XXXX-XXXX-XXXX-XXXX-XXXX` (6 grupos de 4 chars alfanuméricos, sin I,O,0,1 para legibilidad).
~44 bits de entropía (~10⁹⁶ combinaciones).

### Almacenamiento
- `code_hash = SHA-256(plaintext)` — para verificación
- `code_fingerprint = SHA-256(UPPER(plaintext))[0:16]` — para lookups rápidos y dedup
- `expires_at = now() + 30 días`
- `used_at = NULL` initially

### Seguridad
- Código hasheado, nunca se guarda plaintext
- `FOR UPDATE` en la misma transacción para evitar race conditions
- Valida que no esté usado ni caducado antes de vincular

### Endpoints nuevos
| Método | Ruta | Auth | Descripción |
|--------|------|------|-------------|
| POST | `/club/recovery-codes/generate` | device token o sesión | Generar códigos |
| GET | `/club/recovery-codes` | device token o sesión | Listar códigos activos |
| POST | `/club/recover` | ninguno (código + token) | Recuperar con código |
