# INSUMOS — Etapa Audit Log append-only (Sprint compliance, SEC-002)

> Material de contexto para que Claude (Projects, arquitecto) redacte el ETAPA_PROMPT.md
> de esta etapa. No es el prompt en sí — es lo que el arquitecto necesita para escribirlo
> bien, sin tener que releer todo el repo.

---

## 1. Por qué esta etapa, y por qué este alcance

`BACKLOG.md` (SEC-002) y `COMPLIANCE.md` §5.1 piden, dentro del sprint de compliance,
un "registro de auditoría append-only (log inmutable de operaciones sensibles)". El
director priorizó esta pieza primero, antes de hashing de integridad, trazabilidad de
cambios o cifrado en reposo (esas quedan para etapas futuras del mismo sprint).

COMPLIANCE.md §5.1 pide loggear 3 cosas: accesos a datos sensibles, cambios de
configuración, y ejecución de migraciones. Evalué las 3 y propongo **acotar el alcance
de esta etapa a 2 de las 3**:

1. **Migraciones (DDL)** — factible con un event trigger nativo de Postgres
   (`ddl_command_end`), sin tocar el flujo de deploy.
2. **Cambios de configuración** — triggers `AFTER INSERT/UPDATE/DELETE` en
   `user_roles` (altas/bajas/cambios de `app_role`/`org_id`) y en `surveys` cuando
   cambia `status` (activación/desactivación de encuestas).
3. **Acceso a datos sensibles — propongo DEFERIR explícitamente.** Verificado en
   COMPLIANCE.md §3.2: hoy ningún camino de código (ni `/admin/responses` ni las RPCs
   `rpc_me_*`) lee columnas `is_sensitive=true`. Instrumentar "acceso" sin que exista
   un acceso real sería simular cobertura. Cuando se construya el primer camino que sí
   lea sensibles, ESE PR debe agregar su propio log de acceso junto con el código que
   lo necesita — no antes.

El arquitecto puede confirmar o ajustar este recorte; lo presento como punto de partida,
no como cerrado (esto todavía no se aprobó con el director, se está armando el prompt).

---

## 2. Decisiones de diseño que ya tienen forma (para que el prompt las fije)

- **Tabla nueva:** `public.audit_log` — append-only real:
  - Ningún rol (`anon`, `authenticated`, ni `service_role` vía API) tiene
    `INSERT`/`UPDATE`/`DELETE` directo. Solo escriben funciones `SECURITY DEFINER`
    de los triggers, propiedad de `postgres` (mismo patrón que
    `custom_access_token_hook`, ver `supabase/migrations/20260627000001_auth_user_roles.sql`).
  - Trigger `BEFORE UPDATE OR DELETE ON audit_log` que aborta con excepción — segunda
    barrera, defensa en profundidad (no a prueba de un operador con `psql -U postgres`
    en el VPS — ver gap conocido abajo).
  - Lectura: RLS, solo `platform_admin` (reusar `current_app_role()`, ver
    `supabase/migrations/20260531000003_rls_policies.sql:17`).
- **Columnas sugeridas (a validar por el arquitecto):** `id bigserial pk`,
  `occurred_at timestamptz default now()`, `actor_role text`, `actor_user_id uuid`,
  `action text`, `target_table text`, `target_id text`, `detail jsonb`.
- **Gap conocido a documentar, no a resolver en esta etapa:** esto no protege contra
  `TRUNCATE`/`DROP` ejecutado por un superusuario directo en el VPS — mismo gap ya
  registrado en `SEC-003` (BACKLOG.md). Append-only a nivel de aplicación/RLS, no a
  prueba de acceso root a la base.

---

## 3. Convenciones del repo que el prompt debe respetar

- **Migraciones:** `supabase/migrations/`, naming `YYYYMMDDHHMMSS_descripcion.sql`.
  Última migración aplicada: `20260627000001_auth_user_roles.sql`. La próxima debe
  fecharse después de esa.
- **Patrón SECURITY DEFINER + GRANT EXECUTE puntual:** ver
  `custom_access_token_hook` en `20260627000001_auth_user_roles.sql` — función
  `SECURITY DEFINER STABLE`, `GRANT EXECUTE ... TO supabase_auth_admin` (rol
  específico, no `PUBLIC`). Mismo patrón a replicar para las funciones de trigger
  de `audit_log`.
- **Helpers RLS existentes a reusar, no reinventar:** `public.current_app_role()`
  y `public.current_org_id()` (`20260531000003_rls_policies.sql:17-30`).
- **Verificación obligatoria antes de aplicar en VPS:** mostrar el SQL final completo
  y esperar OK explícito del director antes de correrlo en producción (ya es práctica
  establecida en este proyecto — ver incidente VPS del 27 jun, recuperación documentada
  en `docs/estado/ESTADO_PROYECTO_JUNIO_2026.md` §2.1).
- **Formato esperado del archivo de etapa:** seguir la estructura de
  `sprints/sprint-4/ETAPA_AUTH_PROMPT.md` o `ETAPA_4G_PROMPT.md` — Objetivo,
  Decisiones ya tomadas (no consultar), Tareas, DoD, Qué reportar (datos concretos,
  no "funcionó"), Qué NO hacer, Versionado (mensaje de commit sugerido con tag
  `[compliance]` o similar).

---

## 4. Qué NO debería pedir el prompt (fuera de alcance de esta etapa)

- Hashing de integridad SHA-256 (siguiente pieza del mismo sprint, etapa separada).
- Cifrado en reposo (es trabajo de VPS/infraestructura, no de migración SQL).
- Trazabilidad histórica completa de cambios de datos (triggers de historial / WAL) —
  etapa separada también.
- Logging de acceso a datos sensibles (ver §1, punto 3 — deferido a cuando exista
  código que lea sensibles).
- pgaudit o cualquier extensión nueva — no está en el Supabase self-hosted actual,
  agregar una extensión es Nivel 3 aparte (afecta infraestructura/dependencias).

---

## 5. Referencias

- `COMPLIANCE.md` §5 (roadmap auditabilidad), §5.2 (por qué no blockchain), §3.2
  (qué es sensible y cómo se protege hoy).
- `BACKLOG.md` → SEC-002, SEC-003 (gap de TRUNCATE), SEC-004 (bypass REST, contexto
  de otro riesgo residual ya aceptado, mismo espíritu de "documentar en vez de
  sobre-construir").
- `TECH_DEBT.md` → TECH-DEBT-008 (resuelto 27 jun, ejemplo de cómo se cierra un ítem).
- `docs/estado/ESTADO_PROYECTO_JUNIO_2026.md` — estado general post-release v3.0.0
  + incidente VPS, para que el arquitecto tenga el panorama completo antes de escribir
  el prompt.
