Error Reporting Retention

mandatory

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) walka root, remove

    *.jsonl files com mtime ≤ now − maxAge, preserva o file da data corrente UTC

  • cmd/reporter/main.go roda Prune(7d) a cada 24h numa

    goroutine; configurável via -prune-after / -prune-every

  • -prune-after=0 desativa 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=168h0m0s

Smoke 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)

Source: ../home/koder/dev/koder/meta/docs/stack/policies/error-reporting-retention.kmd