# ADR-035 — Trilha de auditoria e evidências (rigorosa no CMS)

| Campo | Valor |
|---|---|
| **Status** | Aceito (2026-10-16) |
| **Data** | 2026-10-16 |
| **Plano de origem** | `wiki/plan/17-dados-abertos-lgpd-governanca.md` · `wiki/plan/12-admin-cms-editor.md` |
| **Relacionadas** | ADR-004, ADR-017, ADR-019, ADR-024, ADR-029, ADR-033, ADR-034 |

## Contexto

O Concórdia já possui **infraestrutura de auditoria** (`core.core_audit_logs`): imutável (trigger
`trg_immutable_audit`), particionada por ano, com `integrity_hash` **BLAKE3** (ADR-029),
`payload_before`/`payload_after`, e um `GlobalAuditSubscriber` que captura **mutações de modelo**
(`created`/`updated`/`deleted`).

Esse registro, porém, **não basta como evidência** para os incidentes que o produto precisa
documentar — por exemplo, uma conta comprometida usada para adulterar conteúdo (injete
desinformação ou remova matéria legítima). Falta a informação que responde *quem, onde, o quê,
antes/depois, quando*:

- o **ator** é apenas `user_id` (sem nome/e-mail) — e, se o cadastro for alterado ou excluído,
  perde-se a identidade do momento do fato;
- não há **módulo nem origem**: sabe-se o *model*, não **onde** (rota/elemento/link/botão) a ação partiu;
- a **ação** é genérica (`created/updated/deleted`), não o **o quê** semanticamente (ex.: `publicar`);
- não há **trilha de acesso** (o que foi lido/visto e quando);
- não há **política por área** (CMS × portal).

Além disso, o **log técnico** (ADR-024) é efêmero (stderr + `daily` 730d), de alta cardinalidade e
PII-masked — **não** é prova.

## Decisão

1. **Três camadas, papéis distintos:**
   - **Log técnico** (ADR-024): operar/depurar; efêmero; PII-masked; **não** é evidência.
   - **Trilha de auditoria** (esta ADR): **evidência forense** persistida e imutável (ADR-029) —
     é onde vive *quem/onde/o quê/antes/depois/quando*.
   - **Analytics público** (ADR-019/027): agregado e anônimo; **nunca** trilha individual do cidadão.
2. **Escopo por área:**
   - **CMS (rigoroso):** toda **mutação** (automática via Eloquent) + **leitura sensível** (dado
     pessoal, exports, listagens de usuários) + **login/logout**.
   - **Portal (simples):** sem trilha individual; apenas log técnico mínimo + analytics agregado.
   - **API:** por chave/rota conforme sensibilidade.
3. **Forma do registro** (base sempre presente; extensões por caso):
   - base: `id`, `created_at`, `area`, `module` (token canônico, ADR-004), `event`
     (`{module}.{action}`), `target{type,id}`, `actor{id,name,email}`, `before`, `after`,
     `request_id`, `trace_id`, `integrity_hash`.
   - extensões quando aplicável: `origin` (rota/elemento/`console`/`queue`), `access` (leitura),
     `changes` (diff só dos campos alterados).
4. **Via única de escrita:** a porta `AuditWriter` recebe `record(AuditEntry)` (DTO `readonly`); o
   `log(action, Model)` atual é o **atalho** para mutações de modelo. O `GlobalAuditSubscriber`
   **enriquece automaticamente** (`module` a partir do namespace, `actor`, `area`/`origin`,
   correlação) — **toda** mutação sai completa sem depender de o autor lembrar.
5. **Vocabulário único:** `{module}.{action}` já é usado por permissões (ADR-033) e por spans
   (ADR-024); a auditoria passa a falar a **mesma língua** (uma convenção, não três).
6. **Acesso (leitura):** middleware `audit.access` na área CMS + recursos sensíveis configuráveis;
   grava `event='{module}.view'` com `target` e origem. No portal **não** há trilha de leitura.
7. **Ator é snapshot** (`id`, `name`, `email`) no momento do evento — a evidência não depende do
   estado atual do cadastro.
8. **Proteções mantidas:** trigger de imutabilidade, BLAKE3, particionamento anual; a correlação
   `request_id`/`trace_id` liga a auditoria ao log técnico (ADR-024).
9. **LGPD (ADR-019):** a trilha contém PII — retenção explícita, `legal hold`, atendimento a DSAR e
   base legal (prevenção a fraude / obrigação legal). Campos sensíveis continuam mascarados.
10. **Não é APM nem log de aplicação:** a alta cardinalidade permanece no log técnico; a auditoria é
    **intencional e durável**.

## Justificativa (alternativas descartadas)

- **Tudo no log técnico (ADR-024)** — descartada: efêmero, alta cardinalidade, sem
  imutabilidade/hash — não serve como prova.
- **Apenas o `GlobalAuditSubscriber`** — descartada: não cobre ação semântica, origem, leitura nem ator.
- **Auditar o portal por cidadão** — descartada: volume, ruído e LGPD (sem base legal).
- **Serviço/fila de auditoria próprio** — descartada agora: nativo + tabela atendem (ADR-001/002).
- **Guardar `before`/`after` só como diff** — parcial: o diff (`changes`) é um atalho de consulta,
  mas o estado antes/depois completo permanece para prova.

## Consequências

**Positivas**
- Evidência íntegra e rastreável de *quem/o quê/onde/antes/depois*, correlacionável com log e telemetria.
- CMS rigoroso **sem** penalizar o cidadão anônimo.
- Uma única via de escrita e um único vocabulário — consistência por construção.

**Custos / negativas**
- Mais escrita no CMS (mutações + leituras sensíveis) e mais PII durável (retenção explícita).
- Migração aditiva do schema (ADR-017) + testes de *shape*.

## Exemplo mínimo (comparativo)

```php
// ❌ Hoje: só o modelo e a ação genérica
app(AuditWriter::class)->log('updated', $noticia); // sabe-se o model, não onde nem o quê

// ✅ Trilha completa (auto + explícito)
// (automático) mutação de Noticia no CMS → module=noticias, origin=<rota do CMS>,
//              actor={id,name,email}, before/after, request_id/trace_id
// (explícito, sem modelo)
app(AuditWriter::class)->record(
    AuditEntry::access(module: 'noticias', event: 'noticias.view', targetType: 'User', targetId: $id),
);
```

## Emenda (2026-10-20) — vocabulário da trilha

A trilha tem **dois níveis de vocabulário**, ambos no formato `{module}.{action}`:

- **Ambiente (Eloquent, `GlobalAuditSubscriber`):** `{module}.{created|updated|deleted}` — registro
  técnico de que houve mutação; garante cobertura total e serve de suporte forense.
- **Explícito (`AuditEntry::event`/`access`):** usa o **mesmo** `{module}.{action}` do span e do log
  da operação (`lgpd.update_dsar`, `lgpd.legal_hold`, `gallery.catalog`, `noticias.status`…).

Os dois **coexistem**: o ambiente não substitui a semântica do domínio; o explícito responde *o quê*
foi feito. Para a mesma operação de domínio, span, log e evento explícito compartilham o token de
ação; o evento ambiente permanece deliberadamente genérico. Operações sensíveis (ex.: legal hold)
**devem** ter evento explícito, além da trilha ambiente.

## Verificação

- Toda mutação de modelo na área **CMS** gera registro com `module/event/actor/origin/area/before/after`.
- `UPDATE`/`DELETE` direto na trilha → exceção (trigger de imutabilidade).
- Acesso a recurso sensível no **CMS** gera registro `event='{module}.view'`.
- **Portal:** nenhum registro de auditoria por cidadão.
- *Shape* completo validado por teste; `make check` verde **2×**.

## Plano de implementação

Fase **F2.5 — Auditoria e evidências** (executa **antes** da F3 — Layout & UX):
[`wiki/implementation/02b-auditoria-e-evidencias.md`](../implementation/02b-auditoria-e-evidencias.md).
