Error Reporting Retention
Janelas de retenção pra eventos do reporter público (design-error / design-feedback) — raw 7 dias, aggregated counts 24 meses; deletion-by-user inaplicável (sem koder_user_id).
Error Reporting Retention
Escopo
services/foundation/reporter é o *ublic unscoped collector*das landings Koder (default hoje: kds.koder.dev via /v1/design-error + /v1/design-feedback). Esta policy define as janelas de retenção pros dados que ele persiste.
Regras
R1 — Raw events: 7 dias
Toda entrada JSONL em <store-root>/<stream>/<UTC-day>.jsonl é *emovida 7 dias após a data do arquivo*(mtime ≤ now − 168h).
O ficheiro da janela corrente (today em UTC) é *unca removido* mesmo se o mtime estiver stale (clock skew, file copy preservando timestamp, restore from backup). Defensive: dados ativos não desaparecem.
R2 — Aggregated counts: 24 meses
Resumos pre-computados (counts por payload.msg, distribuição de payload.url, contagem por janela horária) podem ser retidos por *té 24 meses*
Implementação atual: o pre-compute ainda não roda — vem com backlog/pending/002-triage-cron.md quando kdb-ts estiver disponível (depende de #001). Até lá, nada de aggregated counts existe e R2 é dormente.
R3 — Deletionbyuser é inaplicável
O endpoint *ÃO recebe koder_user_id*— landlord pattern de multi-tenancy/contract.kmd §edge-cases. Sem identidade na wire, não é possível atender "delete all my data" requests porque não há "my" para mapear.
Mitigação: o payload em si é zero-PII por contrato (cliente assets/js/error-report.js truncates UA, omite cookies / IP / session, payload < 8 KB). A janela R1 de 7 dias garante que qualquer dado eventualmente vai embora sem ação explícita.
R4 — Restore from backup respeita R1
Restaurar uma .jsonl velha de backup *ão revive*a retenção — o prune diário (R1) volta a remover assim que o mtime indicar fora da janela. Operadores que precisam de retenção estendida pra incident investigation devem copiar o ficheiro pra fora do store-root.
Implementação atual (MVP — JSONL stopgap)
internal/store/jsonl.go::Prune(maxAge)walkaroot, remove*.jsonlfiles com mtime ≤now − maxAge, preserva o file da data corrente UTCcmd/reporter/main.gorodaPrune(7d)a cada 24h numagoroutine; configurável via
-prune-after/-prune-every-prune-after=0desativa o pruning (dev / debugging)
Implementação futura (pós#001 — kdbts)
Quando o store migrar pra kdb-ts (services/foundation/reporter#001), R1 vira *TL declarativo no schema*
CREATE TABLE design_errors (
ingest_ts TIMESTAMPTZ NOT NULL,
...
) WITH (ttl = '7 days');E R2 vira *ontinuous aggregate*com retenção própria:
CREATE MATERIALIZED VIEW design_errors_daily WITH (
ttl = '24 months'
) AS SELECT date_trunc('day', ingest_ts), msg, count(*) ...;A goroutine em main.go fica como fallback / dev-only quando o JSONL store estiver ativo.
Verificação
# Confirma que o ciclo de retenção tá rodando
ssh s.khost1 'journalctl -u koder-reporter.service | grep "reporter: prune"'
# Output esperado a cada 24h:
# reporter: prune removed=N kept=M max_age=168h0m0sSmoke test cobre o caminho em internal/store/jsonl_test.go:
TestPrune_RemovesOldFilesKeepsRecent(R1 happy path)TestPrune_NeverDeletesToday(R1 defensive — clock skew)TestPrune_MissingRootNoOp(boot antes do primeiro Append)