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.
Meus dados e rastros
Seção intitulada “Meus dados e rastros”O que fica vinculado a você
Seção intitulada “O que fica vinculado a você”- 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”- 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.
- Retenção automática. Demanda concluída tem o conteúdo pessoal anonimizado e as mídias removidas após 60 meses.
- 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 que permanece
Seção intitulada “O que permanece”- 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.
Quando a moderação remove uma evidência
Seção intitulada “Quando a moderação remove uma evidência”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.
Anexo técnico — implementação dos rastros
Seção intitulada “Anexo técnico — implementação dos rastros”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óriomvp-api,web/= repositóriomvp-web.
1. Conceitos
Seção intitulada “1. Conceitos”1.1 Redação na gravação do log de eventos
Seção intitulada “1.1 Redação na gravação do log de eventos”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).
1.2 Anonimização
Seção intitulada “1.2 Anonimização”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:
- 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). - Nulo em colunas que identificam a pessoa (ex.:
nome,emailegoogle_subemeliminarCidadao,titular.repository.ts:299). - 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). - Arredondamento de coordenadas para 2 casas decimais (aproximação de ~1 km), função compartilhada com a redação do log.
- Exclusão física do objeto de mídia no armazenamento (
ArmazenamentoTitularService.removerObjetos,armazenamento-titular.service.ts:36, que chamaremoveObjectpor 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.
1.3 Pseudonimização
Seção intitulada “1.3 Pseudonimização”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.
1.4 Hash (impressão digital)
Seção intitulada “1.4 Hash (impressão digital)”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).
1.5 Tombstone
Seção intitulada “1.5 Tombstone”A decisão de moderação é a exceção ao log append-only. O tombstone tem três partes:
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_ideremovido_em. A idempotência do consumidor é essa tabela (remocao-conteudo.service.ts:58).- Reescrita dos payloads de
core.event_log(tombstonarEventLog,titular.repository.ts:874): todo evento comcorrelacao_idoupayload.demanda_idigual à demanda afetada passa porlimparPayloadDeConteudo(remocao-payload.ts:60), que troca por marcador as chaves de texto deCHAVES_CONTEUDO_TEXTO(:3-20) e osobject_keydo conjunto removido (:42-48). O restante do payload é preservado. - Linha do anexo:
statusemoderacao_statusviramremovido, com moderador e data;object_keyehash_sha256permanecem (anexo.repository.ts:152). O download do conteúdo removido responde 410 (acesso-anexo.service.ts:63).
1.6 O identificador do aparelho
Seção intitulada “1.6 O identificador do aparelho”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.
2. Caminho 1 — Eliminação a pedido do titular
Seção intitulada “2. Caminho 1 — Eliminação a pedido do titular”2.1 Entrada, autenticação e auditoria
Seção intitulada “2.1 Entrada, autenticação e auditoria”- 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) exigeAuthorization: Bearer <jwt>eauth_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; tabelaprisma/schema.prisma:108) comtipo(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.mddo repositório.
2.2 Ordem de execução
Seção intitulada “2.2 Ordem de execução”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. |
2.3 O que permanece depois da eliminação
Seção intitulada “2.3 O que permanece depois da eliminação”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_keyehash_sha256, com o objeto ausente do bucket. Downloads respondem 410.d12,l1,l3,e1ed6a: pseudônimos e contagens.d1d.d1d_decisoes_moderacao: a decisão, o motivo e a data da trilha permanecem; omoderador_idvira o placeholder anônimo, em paridade com a fila.- A projeção pública da D-7 tem
titulo,descricaoetexto_ultima_atualizacaodo 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_cicloed7.demanda_snapshots.resumo_agregado,resumo_cicloecaminho_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 emd12ed6b, o dossiê público emd7.caminhose 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).
3. Caminho 2 — Retenção automática
Seção intitulada “3. Caminho 2 — Retenção automática”3.1 Job principal
Seção intitulada “3.1 Job principal”- Agenda: todo dia às 04:00 (
@Cron('0 4 * * *'),retencao.service.ts:24). - Corte: agora menos
RETENCAO_CONTEUDO_MESESmeses (:27-28; padrão 60,titular.constants.ts:3). - Seleção: demandas com
data_conclusaoanterior ao corte na projeçãod7.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_recebidasrecebetexto_bruto=[conteúdo anonimizado por retenção],tituloedescricao= null;d1b.normalizacoesrecebedescricao_limpa= marcador e entidades = null;d7.demanda_snapshotstemtitulo,descricao,texto_ultima_atualizacao,resumo_agregado,resumo_cicloecaminho_dossie= null, comdescricoes_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_caminhostemdossie,documentosegargalosvazios no par;d1c.anexosrecebestatus=removidoemetadados_exif= null; por fim os objetos são apagados do bucket (retencao.service.ts:36).
3.2 Jobs satélites do mesmo módulo
Seção intitulada “3.2 Jobs satélites do mesmo módulo”| 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. |
3.3 Diferenças em relação à eliminação
Seção intitulada “3.3 Diferenças em relação à 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.
3.4 Parâmetros públicos
Seção intitulada “3.4 Parâmetros públicos”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.
4. Caminho 3 — Remoção por moderação
Seção intitulada “4. Caminho 3 — Remoção por moderação”4.1 A decisão humana (D-1d)
Seção intitulada “4.1 A decisão humana (D-1d)”- Rotas fora do prefixo
api:GET /admin/moderacao/fila,GET /admin/moderacao/fila/:item_id/historico,POST /admin/moderacao/fila/:item_id/decidirePOST /admin/moderacao/fila/:item_id/reverter(moderacao.controller.ts:36), 60/min (:41,:73,:103e:137), sobPapelModeradorGuard. - A remoção exige motivo e é irreversível; a reversão aceita apenas
aprovadoebloqueado. O item do tiporelatoaceita apenasaprovadoebloqueado:removidoresponde 400. remover_texto_demandasó é aceito em item do tipoanexo.- 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 oevento_idpublicado. A eliminação do titular anonimiza o moderador nessa trilha. - A decisão publica
moderacao.decidida1.1.0 comitem_id,tipo(texto/anexo/relato),referencia_id,decisao,moderador_id,demanda_id,motivo,remover_texto_demandaereabertoquando aplicáveis.
4.2 D-1c — remoção da mídia
Seção intitulada “4.2 D-1c — remoção da mídia”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):
removerCopiasDoHash(:366) busca todas as linhas ded1c.anexoscom o mesmohash_sha256(buscarPorHash,anexo.repository.ts:125), em qualquer demanda.- Apaga do bucket cada
object_keypermanente e cadaobject_key_tempdas cópias ativas. - Publica
anexo.removido1.0.0 por cópia, comanexo_id,demanda_idehash_sha256(:412-432). marcarRemovidos(anexo.repository.ts:152) marcastatusemoderacao_statuscomoremovido, grava moderador e data e zeraobject_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):
- Resolve a demanda do payload (
resolverDemandaId,:139). - Define
limparTexto = tipo === 'texto' || remover_texto_demanda === true(:77). - 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). - 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. |
- Zera a evidência de confirmações e conclusões da demanda (
limparEvidenciasDuplicidade→evidencia = [],titular.repository.ts:858). - Tombstone do log (
tombstonarEventLog,:874), conforme a seção 1.5. - Grava
core.remocoes_conteudocomevento_idúnico, hash, motivo truncado e moderador (registrarRemocaoModeracao,:765).
4.4 D-7 — vitrine pública
Seção intitulada “4.4 D-7 — vitrine pública”processarModeracaoDecidida (d7.service.ts:1265) só age em tipo === 'texto':
removido:limparConteudoRemovido(:1294) troca adescricaodas entradas de timeline da demanda porConteúdo removido por decisão de moderação humana.(DESCRICAO_CONTEUDO_REMOVIDO,d7.constants.ts:77), apaga os campostituloedescricaodosdados_relevantes(d7.repository.ts:461) e zeratitulo,descricao,texto_ultima_atualizacaoedescricoes_midiado snapshot (:447).aprovado:liberarConteudoSuspensopublica as entradas internas e restaura título e descrição (d7.service.ts:583).bloqueadocomreaberto:ocultarConteudoDaVitrineesconde 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).
4.5 O que permanece
Seção intitulada “4.5 O que permanece”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.
5. Redação do log por tipo de evento
Seção intitulada “5. Redação do log por tipo de evento”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.
6. Algoritmos e constantes
Seção intitulada “6. Algoritmos e constantes”| 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 |
7. Mapa do texto canônico para a implementação
Seção intitulada “7. Mapa do texto canônico para a implementação”| 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 |
8. Limites conhecidos do MVP
Seção intitulada “8. Limites conhecidos do MVP”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.
- 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_cicloecaminho_dossieemd7.demanda_snapshotse zeramdescricoes_midia; a moderação usalimparConteudoTimeline/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. Asdescricoes_midia, preservadas em claro na redação, voltam pelo replay de forma intencional. d1b.normalizacoes.titulonão é sobrescrito na eliminação (titular.repository.ts:342) nem na retenção (:681); só a moderação o substitui (:807).- O log de eventos mantém
cidadao_id(pseudônimo) em vários tipos e mantém otitulodedemanda.normalizada; a eliminação não aplica tombstone de texto — só a moderação. d1a.demandas_recebidas.midia_urlsnã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.d1c.anexos.object_key_tempnão é zerado na eliminação; o objeto temporário correspondente é removido pelo job de 24h (seção 3.2).- 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,e1nem 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. - A D-7 só limpa texto em decisão de tipo
texto(d7.service.ts:1265); a remoção de anexo comremover_texto_demandalimpa os schemas de origem via N-0d, mas não o snapshot da D-7. - 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. - 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_cicloecaminho_dossiee emd7.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émd24.caminhos_casos.resumo,d24.caminhos_casos.documentos,d24.caminhos_casos.gargalosed24.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.