Pular para o conteúdo

Rastros de conteúdo — texto canônico

Este é o texto canônico da explicação sobre rastros de conteúdo. Ele é a fonte da seção “Meus dados e rastros” do perfil no aplicativo e da seção correspondente da Política de Privacidade (politica_privacidade.md).

O documento tem duas partes. A primeira, até a seção “Quando uma demanda ou um lugar sai de cena por outros fluxos”, é o texto canônico exibido ao usuário. O anexo técnico ao final é documentação interna de engenharia: ele amarra cada afirmação do texto ao código que a implementa e não é exibido no aplicativo.

  • Demandas que você registrou, como relator.
  • Confirmações de demanda e de lugar feitas por você.
  • Fotos e áudios enviados como evidência.
  • Atualizações que você publicou como conselheiro.
  • Lugares e empresas que você cadastrou.
  • O identificador do aparelho e, com login Google, nome e e-mail.

Os três caminhos que removem arquivos ou conteúdo

Seção intitulada “Os três caminhos que removem arquivos ou conteúdo”
  1. Eliminação a pedido do titular (LGPD). Você pede na tela “Meus dados”. O perfil é anonimizado, os textos das demandas e da relatoria são apagados e as mídias são removidas do armazenamento.
  2. Retenção automática. Demanda concluída tem o conteúdo pessoal anonimizado e as mídias removidas após 60 meses.
  3. Remoção por moderação. Conteúdo ilegal ou que fere direitos é apagado do armazenamento e dos registros por decisão humana, sem possibilidade de reversão.
  • O protocolo e os registros públicos de processo, com o conteúdo já sanitizado.
  • O hash do conteúdo removido (uma impressão digital calculada do arquivo, que não permite recuperá-lo), usado em eventual encaminhamento a autoridade.
  • Identificadores pseudonimizados nos registros internos.

Quando a moderação remove o texto de uma demanda

Seção intitulada “Quando a moderação remove o texto de uma demanda”

A demanda continua como objeto coletivo. O título e a descrição saem da vitrine e dos registros afetados, e a demanda recebe a marca pública “Conteúdo removido por decisão de moderação humana”. A relatoria e o acompanhamento continuam, com os textos afetados limpos.

A mídia sai do armazenamento e da vitrine, e todas as cópias do mesmo arquivo são removidas. O registro da remoção permanece na timeline pública.

Quando uma demanda ou um lugar sai de cena por outros fluxos

Seção intitulada “Quando uma demanda ou um lugar sai de cena por outros fluxos”
  • Retirada pelo autor: o lugar sai do mapa público a pedido de quem registrou.
  • Desativação por operador: o lugar sai do mapa na validação de qualidade.
  • Conteúdo removido: a demanda ou a evidência sai da vitrine pública, com a marca de remoção no lugar.

Documento interno de engenharia. A seção “Meus dados e rastros” acima é o texto canônico exibido no aplicativo e espelhado na Política de Privacidade; este anexo não é exibido ao usuário. Ele amarra cada afirmação do texto ao código que a implementa, para auditoria. Referências no formato arquivo:linha; api/ = repositório mvp-api, web/ = repositório mvp-web.

O log de eventos (core.event_log) é a memória de processo do sistema. O dado pessoal não é gravado nele: EventBusService.publicar() aplica redigirPayload antes do insert (api/src/nucleo/n-0a-event-bus/event-bus.service.ts:96), com regras por tipo de evento em REDACAO_POR_TIPO (api/src/nucleo/n-0a-event-bus/redacao/redacao-eventos.ts:17). Cada regra tem cinco mecanismos:

Mecanismo Efeito
permitidos Campo copiado como está para o log.
textoParaHash Campo de texto vira { hash: SHA-256 hexadecimal, tamanho, redigido: true } (redacao-eventos.ts:265).
remover Campo descartado.
coordenadasParaArredondar Coordenada reduzida a 2 casas decimais (arredondarCoordenada, redacao-eventos.ts:275; precisão PRECISAO_NIVEL_UC = 2, :273), aceitando número ou { lat, lng }.
listasDeObjetos Subcampo de cada item de uma lista vira hash (ex.: url em midia_urls).

Campo que não aparece em nenhuma lista do tipo é descartado. Tipo sem regra é persistido como está — a suíte de redação garante que tipos sem regra não carregam campo pessoal no schema do Registry (redacao-eventos.spec.ts:470). Os consumidores ao vivo recebem o payload original em memória (event-bus.service.ts:113); replay e reentrega de DLQ leem o log e recebem o payload redigido (por isso handlers toleram campos ausentes).

Anonimizar, no código, é substituir conteúdo e identificadores de forma irreversível, sem chave de reversão. O MVP implementa isso com cinco operações:

  1. Marcador fixo no lugar do texto — [conteúdo removido a pedido do titular], [conteúdo anonimizado por retenção] e [conteúdo removido por decisão de moderação] (api/src/nucleo/n-0d-direitos-titular/titular.constants.ts:14-16).
  2. Nulo em colunas que identificam a pessoa (ex.: nome, email e google_sub em eliminarCidadao, titular.repository.ts:299).
  3. Pseudônimo por placeholder nil (00000000-0000-0000-0000-000000000000, titular.constants.ts:18), por UUID derivado (UUID v5, na L-3) ou por re-chave aleatória (UUID v4, na D-6a).
  4. Arredondamento de coordenadas para 2 casas decimais (aproximação de ~1 km), função compartilhada com a redação do log.
  5. Exclusão física do objeto de mídia no armazenamento (ArmazenamentoTitularService.removerObjetos, armazenamento-titular.service.ts:36, que chama removeObject por chave, :43), mantendo a linha e o hash como rastro.

Não existe, no MVP, cofre de chaves por titular nem destruição de chave no caminho de eliminação. A metáfora de “eliminar a chave criptográfica” descreve a arquitetura-alvo (docs/rede_civica.md:646, docs/contexto_IA.md:286); a irreversibilidade praticada hoje é a das cinco operações acima.

O UUID do titular permanece como chave pseudônima em vários registros — por exemplo, d1a.demandas_recebidas.cidadao_id não é alterado na eliminação e core.consentimentos.titular_id e core.solicitacoes_titular.titular_id permanecem —, mas a linha correspondente em d1a.cidadaos perde nome, e-mail, conta Google, endereço e segredo do dispositivo. Em outros schemas o vínculo é trocado: placeholder (L-1, L-3, E-1, D-12), pseudônimo determinístico (L-3) ou re-chave (D-6a). A escolha preserva contagem, antirrepetição e histórico de processo sem ligação nominal.

O pseudônimo determinístico da L-3 usa uuidv5(nome, NAMESPACE_ANONIMIZACAO_L3) com namespace fixo a1300000-0000-4000-8000-0000000000a1 (titular.repository.ts:14) e nomes l3-confirmacao:<confirmacao_id> e l3-denuncia:<denuncia_id> (:521 e :539): a mesma ação mantém o mesmo pseudônimo em replays, sem cruzar ações entre si.

SHA-256 é usado em três pontos distintos:

Uso Cálculo Onde é gravado
Texto no log de eventos sha256(utf8) do campo de texto core.event_log, como { hash, tamanho, redigido }
Conteúdo textual removido pela moderação sha256 de texto_bruto, titulo e descricao com trim, partes vazias descartadas, unidas por \n (remocao-conteudo.service.ts:155) core.remocoes_conteudo.hash_conteudo
Mídia sha256 dos bytes recebidos, calculado em streaming antes da limpeza (processamento-anexo.service.ts:473) d1c.anexos.hash_sha256 e payload de anexo.removido

O hash não permite recuperar o conteúdo; permite comparar. É por isso que ele serve de tombstone: o objeto permanente é gravado como anexos/<hash> (processamento-anexo.service.ts:574), o par hash_sha256 + demanda_id é único (prisma/schema.prisma:260) e o reenvio de arquivo com par removido é bloqueado (processamento-anexo.service.ts:562-569). No log, o hash da mídia em anexo.processado é redigido como hash-de-hash (redacao-eventos.ts:236); em anexo.removido ele é preservado legível (:258-262).

A decisão de moderação é a exceção ao log append-only. O tombstone tem três partes:

  1. core.remocoes_conteudo (prisma/schema.prisma:132): evento_id único, tipo (texto/anexo), referencia_id, demanda_id, hash_conteudo, motivo (truncado em 200 caracteres, remocao-conteudo.service.ts:13), moderador_id e removido_em. A idempotência do consumidor é essa tabela (remocao-conteudo.service.ts:58).
  2. Reescrita dos payloads de core.event_log (tombstonarEventLog, titular.repository.ts:874): todo evento com correlacao_id ou payload.demanda_id igual à demanda afetada passa por limparPayloadDeConteudo (remocao-payload.ts:60), que troca por marcador as chaves de texto de CHAVES_CONTEUDO_TEXTO (:3-20) e os object_key do conjunto removido (:42-48). O restante do payload é preservado.
  3. Linha do anexo: status e moderacao_status viram removido, com moderador e data; object_key e hash_sha256 permanecem (anexo.repository.ts:152). O download do conteúdo removido responde 410 (acesso-anexo.service.ts:63).

Em modo anônimo, a identidade é um UUID v4 gerado com crypto.randomUUID(), guardado no localStorage na chave rc_device_id (web/src/lib/device-id.ts:3 e :53) e enviado como X-Cidadao-Id. Com login Google, a identidade verificada é o cidadao_id da conta. A eliminação não apaga a chave local do aparelho; ela zera o device_secret_hash e os campos do perfil. O usuário pode gerar novo identificador a qualquer momento; a recuperação do vínculo exige a conta verificada.

  • Rota: DELETE /api/titular/dados (api/src/nucleo/n-0d-direitos-titular/titular.controller.ts:83), limite 5/min (:84).
  • Guarda: TitularGuard (guards/titular.guard.ts:15) exige Authorization: Bearer <jwt> e auth_provider === 'google' (:36-40). Device ID não é aceito nestas rotas.
  • Auditoria: cada operação vira uma linha em core.solicitacoes_titular (titular.repository.ts:632; tabela prisma/schema.prisma:108) com tipo (acesso, exportacao, correcao, eliminacao, revogacao), detalhes e conclusão. A resposta da eliminação traz protocolo, status e observações (titular.service.ts:77-123).
  • Prazo de resposta: 15 dias (PRAZO_RESPOSTA_DIAS, titular.constants.ts:1).
  • A N-0d não publica evento: a eliminação é operação síncrona do núcleo, com a exceção de escrita transversal prevista no AGENTS.md do repositório.

eliminarDados (titular.service.ts:84-104) executa, nesta ordem:

# Operação (titular.repository.ts) Tabela O que muda
1 eliminarConteudoDemandas d1a.demandas_recebidas, d7.demanda_snapshots, d7.d7_caminhos, d6b.d6b_acompanhamentos e d12.d12_agregados texto_bruto = marcador do titular; titulo e descricao = null em d1a; titulo, descricao, texto_ultima_atualizacao, resumo_agregado, resumo_ciclo e caminho_dossie = null no snapshot público da D-7, com descricoes_midia = []; texto_resumo_ciclo = null nos acompanhamentos das demandas; resumo_agregado = null nos agregados em que o titular é relator ou membro; dossie, documentos e gargalos = vazios nos perfis de caminho do par (município, subcategoria). cidadao_id permanece como chave pseudônima.
2 eliminarConteudoNormalizacoes:398 d1b.normalizacoes descricao_limpa = marcador; entidades_endereco, entidades_cep e entidades_nome_rua = null.
3 eliminarTextoReviewQueue:413 d3.d3_review_queue texto_original = marcador.
4 eliminarTextoFilaModeracao:423 d1d.d1d_fila_moderacao trecho = marcador.
5 eliminarDescricoesMidia:433 d1d.d1d_descricoes_midia Linhas das demandas do titular apagadas: descricao_original e descricao_traduzida saem do banco. O midia_object_key do item da fila permanece, mas deixa de casar descrição.
6 anonimizarModeradorFilaModeracao:442 d1d.d1d_fila_moderacao moderador_id = placeholder, nas linhas em que o titular decidiu.
7 anonimizarModeradorDecisoesModeracao:449 d1d.d1d_decisoes_moderacao moderador_id = placeholder, nas linhas de decisão em que o titular decidiu.
8 eliminarIdempotenciaLugares:456 d1a.idempotencia_lugares payload = JSON null quando o cidadao_id dentro do payload é o titular.
9 eliminarVinculosDuplicidade:463 d12.d12_confirmacoes, d12.d12_conclusoes, d12.d12_demanda_indice Confirmações e conclusões: cidadao_id = placeholder, coordenadas arredondadas, evidencia = JSON null. Índice: relator_cidadao_id = placeholder; titulo, descricao_limpa e texto_normalizado = null; tokens = JSON null.
10 marcarAnexosRemovidos:510 + ArmazenamentoTitularService.removerObjetos:36 d1c.anexos e bucket anexos status = removido e metadados_exif = JSON null; os objetos permanentes são apagados do armazenamento. Linha e hash_sha256 permanecem.
11 eliminarConteudoAtualizacoesRelatoria:538 d6b.d6b_atualizacoes, d6b.d6b_acompanhamentos texto_bruto e texto_estruturado = marcador; conteudo_estruturado = {}; sugestao_ia = JSON null; texto_resumo_final e texto_resumo_ciclo = null.
12 anonimizarConselheiro:555 d6a.d6a_conselheiros, d6a.d6a_atribuicoes, d6a.d6a_contador_recusas Novo UUID v4: atribuições e contador apontam para o novo id e o registro do conselheiro recebe id e cidadao_id novos. Contagens e histórico permanecem; o vínculo com a pessoa se rompe.
13 anonimizarLugares:578 l1.l1_lugares cidadao_id = placeholder; lugares do tipo residencia que ficaram com o placeholder: nome e descricao = null e status = removido.
14 anonimizarValidacaoLugares:590 l3.l3_status_lugares, l3.l3_confirmacoes, l3.l3_denuncias cidadao_id = placeholder no status; confirmações e denúncias recebem pseudônimo UUID v5 determinístico e coordenadas arredondadas.
15 anonimizarEmpresas:630 e1.e1_empresas representante_id = placeholder.
16 eliminarCidadao:300 d1a.cidadaos nome, email, avatar_url, google_sub, endereco, uc_residencia e device_secret_hash = null. A linha permanece como pseudônimo.
17 revogarConsentimentos:637 core.consentimentos Consentimentos ativos viram revogado com revogado_em. O histórico de aceite permanece.
  • core.solicitacoes_titular: o protocolo e os metadados da solicitação (quantidade de demandas e de anexos afetados).
  • core.event_log: o processo. O texto bruto das demandas nunca foi gravado; campos de texto previstos na redação estão em hash desde a publicação (ver seção 5).
  • core.consentimentos: o histórico de aceites e revogações.
  • d1c.anexos: linha, object_key e hash_sha256, com o objeto ausente do bucket. Downloads respondem 410.
  • d12, l1, l3, e1 e d6a: pseudônimos e contagens.
  • d1d.d1d_decisoes_moderacao: a decisão, o motivo e a data da trilha permanecem; o moderador_id vira o placeholder anônimo, em paridade com a fila.
  • A projeção pública da D-7 tem titulo, descricao e texto_ultima_atualizacao do snapshot anulados pela eliminação; a timeline pública e os demais campos de processo permanecem.
  • Os campos derivados de resumo e dossiê (d12.agregados.resumo_agregado, d6b.acompanhamentos.texto_resumo_ciclo e d7.demanda_snapshots.resumo_agregado, resumo_ciclo e caminho_dossie) são textos sanitizados antes de persistir e publicar. A eliminação limpa as fontes e também as cópias derivadas: os resumos em d12 e d6b, o dossiê público em d7.caminhos e os campos de resumo e dossiê do snapshot. A D-24 mantém o caso e o perfil internos sanitizados; a cópia pública do dossiê é limpa na D-7. O limite residual está registrado na seção 8.
  • A N-0d registra duas observações fixas na resposta: identificadores de vínculo anonimizados e permanência dos registros públicos de processo (titular.service.ts:106-109).
  • Agenda: todo dia às 04:00 (@Cron('0 4 * * *'), retencao.service.ts:24).
  • Corte: agora menos RETENCAO_CONTEUDO_MESES meses (:27-28; padrão 60, titular.constants.ts:3).
  • Seleção: demandas com data_conclusao anterior ao corte na projeção d7.d7_demanda_snapshots (listarDemandasConcluidasAntesDe, titular.repository.ts:671). O relógio da retenção é a data de conclusão projetada, não o status da D-1a.
  • Efeito (anonimizarDemandasPorRetencao, titular.repository.ts): d1a.demandas_recebidas recebe texto_bruto = [conteúdo anonimizado por retenção], titulo e descricao = null; d1b.normalizacoes recebe descricao_limpa = marcador e entidades = null; d7.demanda_snapshots tem titulo, descricao, texto_ultima_atualizacao, resumo_agregado, resumo_ciclo e caminho_dossie = null, com descricoes_midia = []; d1d.d1d_fila_moderacao.trecho = marcador; d6b.d6b_acompanhamentos.texto_resumo_ciclo = null; d12.d12_agregados.resumo_agregado = null nos agregados das demandas vencidas; d7.d7_caminhos tem dossie, documentos e gargalos vazios no par; d1c.anexos recebe status = removido e metadados_exif = null; por fim os objetos são apagados do bucket (retencao.service.ts:36).
Job Agenda Efeito
Limpeza da DLQ (retencao.service.ts:52) 04:30 Apaga linhas de core.dead_letter_queue com status resolved ou failed_permanent e última tentativa anterior a RETENCAO_DLQ_DIAS (padrão 90, titular.constants.ts:4; titular.repository.ts:724).
Limpeza de anexos temporários (retencao.service.ts:71) a cada hora, no minuto 15 Remove do bucket os d1c.anexos.object_key_temp com processamento anterior a 24h (RETENCAO_ANEXOS_TEMPORARIOS_HORAS, titular.constants.ts:5), em lotes de 200 (LOTE_LIMPEZA_ANEXOS_TEMPORARIOS, :6), e zera a coluna (titular.repository.ts:265-295). É esse job que remove os temporários no fluxo de eliminação.

A retenção limpa o conteúdo textual, a mídia e os resumos derivados (d6b, d12 e o dossiê público na D-7), mas não mexe em perfil, consentimentos, D-6a, L-1, L-3, E-1 nem D-3. Também não grava hash de conteúdo removido por retenção; o hash da mídia continua na linha de d1c.anexos.

Espelhados no Apêndice C de docs/rede_civica.md (linhas 2238-2243) e em web/public/public-parameters.json (seção lgpd-retencao), conforme a regra de sincronização dos AGENTS.md.

  • Rotas fora do prefixo api: GET /admin/moderacao/fila, GET /admin/moderacao/fila/:item_id/historico, POST /admin/moderacao/fila/:item_id/decidir e POST /admin/moderacao/fila/:item_id/reverter (moderacao.controller.ts:36), 60/min (:41, :73, :103 e :137), sob PapelModeradorGuard.
  • A remoção exige motivo e é irreversível; a reversão aceita apenas aprovado e bloqueado. O item do tipo relato aceita apenas aprovado e bloqueado: removido responde 400.
  • remover_texto_demanda só é aceito em item do tipo anexo.
  • Cada decisão e cada reversão gravam uma linha append-only em d1d.d1d_decisoes_moderacao, na mesma transação que atualiza a fila, com decisão, motivo, moderador, data, marca de reabertura e o evento_id publicado. A eliminação do titular anonimiza o moderador nessa trilha.
  • A decisão publica moderacao.decidida 1.1.0 com item_id, tipo (texto/anexo/relato), referencia_id, decisao, moderador_id, demanda_id, motivo, remover_texto_demanda e reaberto quando aplicáveis.

processarModeracaoDecidida (processamento-anexo.service.ts:271) atua apenas em itens do tipo anexo, com idempotência por event_id (:274). Na decisão removido (:321):

  1. removerCopiasDoHash (:366) busca todas as linhas de d1c.anexos com o mesmo hash_sha256 (buscarPorHash, anexo.repository.ts:125), em qualquer demanda.
  2. Apaga do bucket cada object_key permanente e cada object_key_temp das cópias ativas.
  3. Publica anexo.removido 1.0.0 por cópia, com anexo_id, demanda_id e hash_sha256 (:412-432).
  4. marcarRemovidos (anexo.repository.ts:152) marca status e moderacao_status como removido, grava moderador e data e zera object_key_temp.

O par hash_sha256 + demanda_id permanece na tabela e bloqueia o reenvio do mesmo arquivo (processamento-anexo.service.ts:562-569); o download responde 410 (acesso-anexo.service.ts:63). Aprovar e bloquear seguem outro ramo: atualizam a moderação e publicam anexo.moderado (:329-363).

4.3 N-0d — texto nos schemas de origem e tombstone do log

Seção intitulada “4.3 N-0d — texto nos schemas de origem e tombstone do log”

RemocaoConteudoService consome moderacao.decidida com cursor e replay no boot (remocao-conteudo.service.ts:28-47). Para cada decisão removido ainda não registrada (:53-61):

  1. Resolve a demanda do payload (resolverDemandaId, :139).
  2. Define limparTexto = tipo === 'texto' || remover_texto_demanda === true (:77).
  3. Se for anexo, coleta as chaves de mídia da linha (:81-95); se houver texto, calcula o hash do conteúdo textual (:97-100).
  4. Limpa os schemas de origem (limparConteudoPorDemanda, titular.repository.ts:780):
Tabela O que muda
d1a.demandas_recebidas texto_bruto, titulo e descricao = marcador de moderação (quando limparTexto); midia_urls filtrado para remover as mídias do conjunto removido.
d1b.normalizacoes titulo e descricao_limpa = marcador; entidades = null; termos_suspeitos = []; processamento_detalhes = {}.
d3.d3_review_queue texto_original = marcador.
d6b.d6b_atualizacoes texto_bruto e texto_estruturado = marcador; conteudo_estruturado = {}; sugestao_ia = JSON null.
d6b.d6b_acompanhamentos texto_resumo_final = null.
d12.d12_demanda_indice titulo, descricao_limpa e texto_normalizado = null; tokens = JSON null.
d1d.d1d_fila_moderacao trecho = marcador; motivos que começam com termos: viram denylist_texto.
  1. Zera a evidência de confirmações e conclusões da demanda (limparEvidenciasDuplicidadeevidencia = [], titular.repository.ts:858).
  2. Tombstone do log (tombstonarEventLog, :874), conforme a seção 1.5.
  3. Grava core.remocoes_conteudo com evento_id único, hash, motivo truncado e moderador (registrarRemocaoModeracao, :765).

processarModeracaoDecidida (d7.service.ts:1265) só age em tipo === 'texto':

  • removido: limparConteudoRemovido (:1294) troca a descricao das entradas de timeline da demanda por Conteúdo removido por decisão de moderação humana. (DESCRICAO_CONTEUDO_REMOVIDO, d7.constants.ts:77), apaga os campos titulo e descricao dos dados_relevantes (d7.repository.ts:461) e zera titulo, descricao, texto_ultima_atualizacao e descricoes_midia do snapshot (:447).
  • aprovado: liberarConteudoSuspenso publica as entradas internas e restaura título e descrição (d7.service.ts:583).
  • bloqueado com reaberto: ocultarConteudoDaVitrine esconde a timeline de normalização e zera título e descrição (:1307).

Cada decisão também gera uma entrada de timeline com texto de processo (descricao-mapper.ts:161-169). Para evidência, a remoção chega pela D-1c via anexo.removido: a D-7 busca a evidência, remove a entrada correspondente de descricoes_midia pelo object_key_temp (removerDescricaoMidia, d7.repository.ts:470), apaga a linha de d7.d7_demanda_evidencias (processarAnexoRemovido, :1240; removerEvidencia, d7.repository.ts:928) e registra a entrada “Anexo removido em definitivo por decisão de moderação humana.” (descricao-mapper.ts:158-159).

O resumo público da demanda passa a expor conteudo_removido: true com a data, calculado por buscarRemocaoConteudoEm (d7.repository.ts:968; d7.controller.ts:203).

A demanda segue como objeto coletivo, com protocolo, timeline de processo, relatoria e acompanhamento. Permanecem a entrada da decisão na timeline pública, o hash em core.remocoes_conteudo (texto) ou na linha do anexo e a marca de remoção na vitrine. A remoção é irreversível: reverter só é possível para aprovado e bloqueado.

Efeito de redigirPayload sobre o payload que vai para core.event_log (redacao-eventos.ts:17-263). Tipos não listados são persistidos sem alteração.

Tipo de evento Texto vira hash Campos removidos Arredondamento
demanda.recebida texto_bruto; url de cada item de midia_urls localizacao_bruta
demanda.normalizada descricao_limpa entidades_extraidas coordenadas_validadas
demanda.georreferenciada coordenadas_lat, coordenadas_lng
conselheiro.prazo_próximo descricao_prazo
conselheiro.cadastrado
conselheiro.atribuicao_recusada
conselheiro.atualização_registrada texto_bruto, texto_estruturado conteudo_estruturado
conselheiro.atualização_publicada texto_estruturado conteudo_estruturado, sugestao_ia
cidadão.cadastrado nome, email, avatar_url
cidadão.perfil_atualizado nome, email, avatar_url, endereco
lugar.recebido descricao posicao
lugar.cadastrado coordenadas_brutas
lugar.validado
lugar.desativado
empresa.cadastrada representante_id
empresa.folha_submetida cargos
demanda.conclusao_confirmada
anexo.processado hash_sha256 (hash-de-hash) url_canonica, url_acesso
demanda.evidencia_adicionada url, object_key
moderacao.decidida motivo
anexo.removido
caminho.atualizado
demanda.resumo_agregado_atualizado
demanda.resumo_ciclo_atualizado

Em demanda.normalizada, o titulo é mantido no log (conteúdo público do processo, sanitizado na D-7) e as midias_descritas ficam preservadas em claro: a descrição automática não é dado pessoal, a D-1d usa as legendas como contexto do moderador e a D-7 projeta a descrição por imagem em descricoes_midia. Na remoção por moderação o título é sobrescrito pelo tombstone e a D-7 zera descricoes_midia. Em lugar.recebido e lugar.cadastrado, nome e descricao do lugar público são mantidos por não serem dado pessoal do titular. Em anexo.processado, o object_key_temp entra na allowlist da redação ao lado do object_key: a chave temporária da captura é preservada para a D-1d casar a descrição automática da imagem no item da fila, que a guarda em midia_object_key, e para a D-7 casá-la com a evidência e expor a descricao da imagem no resumo e no relatório.

Nos três tipos derivados (caminho.atualizado, demanda.resumo_agregado_atualizado e demanda.resumo_ciclo_atualizado), o texto sai sanitizado das colônias de origem e a redação preserva dossie, resumo, gargalos, documentos e fontes para o replay reconstruir as projeções. Os campos que identificam pessoa não entram nesses payloads.

Item Valor / algoritmo Fonte
Hash de texto e conteúdo SHA-256 hexadecimal (createHash('sha256')) redacao-eventos.ts:265; remocao-conteudo.service.ts:168; processamento-anexo.service.ts:473
Re-chave da D-6a UUID v4 aleatório titular.repository.ts:479
Pseudônimo da L-3 UUID v5, namespace a1300000-0000-4000-8000-0000000000a1, nomes l3-confirmacao:<id> e l3-denuncia:<id> titular.repository.ts:14, :521, :539
Placeholder anônimo 00000000-0000-0000-0000-000000000000 titular.constants.ts:18
Precisão de coordenada 2 casas decimais redacao-eventos.ts:273-277
Marcadores [conteúdo removido a pedido do titular], [conteúdo anonimizado por retenção], [conteúdo removido por decisão de moderação] titular.constants.ts:14-16
Tamanho máximo do motivo guardado 200 caracteres remocao-conteudo.service.ts:13
Retenção de conteúdo 60 meses (env RETENCAO_CONTEUDO_MESES) titular.constants.ts:3, :27-31
Retenção da DLQ 90 dias (env RETENCAO_DLQ_DIAS) titular.constants.ts:4, :33-37
Retenção de anexo temporário 24 horas, lote de 200, job horário titular.constants.ts:5-6; retencao.service.ts:71
Bucket de mídia MINIO_BUCKET (padrão anexos); objeto permanente em anexos/<hash> armazenamento-titular.service.ts:15; processamento-anexo.service.ts:574
Unicidade de anexo @@unique([hash_sha256, demanda_id]) prisma/schema.prisma:260
Unicidade do tombstone core.remocoes_conteudo.evento_id único prisma/schema.prisma:134
Idempotência do consumidor da moderação cursor próprio (core.consumer_offset) + replay no boot + existeRemocaoModeracao remocao-conteudo.service.ts:28-61
Afirmação do texto Implementação
Demandas registradas como relator d1a.demandas_recebidas.cidadao_id
Confirmações de demanda e de lugar d12.d12_confirmacoes.cidadao_id; l3.l3_confirmacoes.cidadao_id e l3.l3_status_lugares.cidadao_id
Conclusões de demanda d12.d12_conclusoes.cidadao_id
Fotos e áudios como evidência d1c.anexos + objetos no bucket MinIO
Atualizações publicadas como conselheiro d6b.d6b_atualizacoes
Lugares e empresas cadastrados l1.l1_lugares.cidadao_id; e1.e1_empresas.representante_id
Identificador do aparelho e, com login, nome e e-mail web/src/lib/device-id.ts; d1a.cidadaos
Caminho 1 — eliminação a pedido seção 2 (rota DELETE /api/titular/dados)
Caminho 2 — retenção automática seção 3 (job diário às 04:00)
Caminho 3 — remoção por moderação seção 4
Protocolo e registros públicos de processo core.solicitacoes_titular, core.event_log redigido, projeções d7.*
Hash do conteúdo removido core.remocoes_conteudo.hash_conteudo; anexo.removido.hash_sha256; linha d1c.anexos
Identificadores pseudonimizados internos seção 1.3
Marca pública “Conteúdo removido por decisão de moderação humana” DESCRICAO_CONTEUDO_REMOVIDO (d7.constants.ts:77) + conteudo_removido no resumo (d7.controller.ts:234)
Mídia removida e todas as cópias do mesmo arquivo removerCopiasDoHash (processamento-anexo.service.ts:366)
Registro da remoção permanece na timeline entrada de moderacao.decidida e anexo.removido em d7.d7_timeline_entries
Retirada pelo autor POST /api/l3/lugares/:lugar_id/retirada (l3.controller.ts:115), que publica lugar.desativado (l3.service.ts:539); a D-7 tira o lugar do mapa (d7.service.ts:1473)
Desativação por operador POST /api/l3/lugares/:lugar_id/reativar (l3.controller.ts:150) sob guarda de operador; o ciclo de status vive na L-3
Conteúdo removido da vitrine limparConteudoRemovido e processarAnexoRemovido na D-7 (seção 4.4)
Descrição automática das imagens na vitrine d7.demanda_snapshots.descricoes_midia, com a evidência casada pelo object_key_temp de d7.demanda_evidencias; limpa na remoção de texto, na remoção de anexo, na eliminação e na retenção
Resumo público do agregado d12.agregados.resumo_agregado, publicado em demanda.resumo_agregado_atualizado e projetado no snapshot da D-7
Resumo público do ciclo d6b.acompanhamentos.texto_resumo_ciclo, publicado em demanda.resumo_ciclo_atualizado e projetado no snapshot da D-7
Dossiê histórico do caminho d24.caminhos_perfis.dossie, publicado em caminho.atualizado; a D-7 projeta d7.caminhos e o caminho_dossie do snapshot
Caminho conhecido do conselheiro d7.caminhos e d7.demanda_snapshots.caminho_dossie, lidos pelo workspace pela projeção pública

Pontos verificados no código atual, registrados para revisão do operador. Nenhum deles altera o texto canônico do aplicativo; são descrição do que a implementação faz hoje.

  1. A D-7 tem o conteúdo do snapshot limpo pela eliminação, pela retenção e pela moderação. A eliminação e a retenção anulam titulo, descricao, texto_ultima_atualizacao, resumo_agregado, resumo_ciclo e caminho_dossie em d7.demanda_snapshots e zeram descricoes_midia; a moderação usa limparConteudoTimeline/limparConteudoSnapshot. A timeline pública não é limpa pela eliminação nem pela retenção e o rebuild da D-7 não é disparado pela N-0d; um rebuild manual reaplica os eventos do log e pode restaurar os campos derivados que não passaram por tombstone no log. As descricoes_midia, preservadas em claro na redação, voltam pelo replay de forma intencional.
  2. d1b.normalizacoes.titulo não é sobrescrito na eliminação (titular.repository.ts:342) nem na retenção (:681); só a moderação o substitui (:807).
  3. O log de eventos mantém cidadao_id (pseudônimo) em vários tipos e mantém o titulo de demanda.normalizada; a eliminação não aplica tombstone de texto — só a moderação.
  4. d1a.demandas_recebidas.midia_urls não é filtrado na eliminação (só na moderação, titular.repository.ts:911); as URLs permanecem apontando para objetos apagados, e o acesso responde 410.
  5. d1c.anexos.object_key_temp não é zerado na eliminação; o objeto temporário correspondente é removido pelo job de 24h (seção 3.2).
  6. A retenção cobre d1a, d1b, d1d, d1c, o snapshot e o dossiê público da D-7, os resumos derivados da D-6b e da D-12; não alcança a timeline da D-7, d3, d6a, l1, l3, e1 nem o perfil (d1a.cidadaos). A timeline pública e os casos internos da D-24 permanecem após o prazo; o dossiê publicado é limpo na D-7.
  7. A D-7 só limpa texto em decisão de tipo texto (d7.service.ts:1265); a remoção de anexo com remover_texto_demanda limpa os schemas de origem via N-0d, mas não o snapshot da D-7.
  8. O hash de texto só é guardado na remoção por moderação. A eliminação e a retenção não registram hash do texto removido; no caso da mídia, o hash permanece na linha de d1c.anexos.
  9. Os campos derivados de resumo e dossiê são limpos pela eliminação e pela retenção em d12.agregados.resumo_agregado, d6b.acompanhamentos.texto_resumo_ciclo, d7.demanda_snapshots.resumo_agregado, resumo_ciclo e caminho_dossie e em d7.caminhos. A limpeza é aplicada no momento da operação; um rebuild posterior da D-7 reaplica os eventos e pode restaurar os textos, porque o log não guarda tombstone desses campos. A D-24 mantém d24.caminhos_casos.resumo, d24.caminhos_casos.documentos, d24.caminhos_casos.gargalos e d24.caminhos_perfis.dossie: são derivados sanitizados, o schema é interno e não há endpoint que os exponha, e a cópia pública é limpa na D-7.