Ir para o conteúdo

RIPD — Relatório de Impacto à Proteção de Dados Pessoais

Documento de conformidade com a Lei Geral de Proteção de Dados (LGPD, Lei 13.709/2018), especialmente Art. 38 (RIPD para tratamentos que envolvam dados sensíveis ou alto risco).

Campo Valor
Sistema Anotae — auxiliar de digitação para profissionais SUS
Versão coberta 0.12.x (e versões posteriores até nova revisão)
Última atualização 2026-05-16
Responsável Carlos E. F. de Ávila / Sciereli HDC
DPO/Encarregado (a designar para v1.0)
Contato suporte@anot.ae

1. Contexto e finalidade do tratamento

O Anotae é uma ferramenta desktop 100% local/offline desenhada para profissionais de saúde do SUS escreverem o atendimento uma única vez em um editor com template estruturado e injetarem os dados em sistemas oficiais (e-SUS APS PEC, SISREG III, SEI/DF) via extensão de navegador.

A partir de v0.10 a ferramenta passou a oferecer também: anexos clínicos por encounter (imagens/PDFs), busca semântica em prontuário próprio (RAG retrieval-only), sumarização/redação assistida por LLM local opcional, transcrição de áudio por ASR local opcional, e canal mobile HTTPS+QR para envio de fotos do celular para o desktop dentro da mesma rede.

Finalidade legítima (LGPD Art. 7º, III): redução do tempo de digitação dupla pelo profissional, mitigando burnout e erros por re-digitação. O profissional continua sendo o controlador dos dados que digita; o Anotae é ferramenta auxiliar sob seu controle direto.

Não é: prontuário eletrônico oficial (PEP), dispositivo médico (SaMD), serviço cloud, agente autônomo ou sistema de decisão clínica automatizada.


2. Categorias de dados pessoais tratados

2.1 Sobre o profissional (controlador)

Armazenados na tabela professionals do vault local:

Campo Categoria Cifrado? Justificativa
Nome completo Pessoal Sim (field-level) Composição de relatórios oficiais
CNS Pessoal Não (necessário para UNIQUE no schema; vault inteiro cifrado em repouso) Identificação no e-SUS, SEI e demais sistemas SUS
CPF Pessoal sensível Sim (field-level) Pode ser exigido em formulários oficiais
Função (MFC, GO, ENF...) Profissional Não (categórico, não-PII) Personalização do template
Registro profissional (CRM, COREN...) Profissional Não Composição de relatórios
CNES da unidade Categórico institucional Não Identificação da unidade

2.2 Sobre o paciente

Armazenados na tabela patients do vault local:

Campo Categoria Cifrado? Justificativa
Handle (PAC-001) Identificador local Não Indexação para busca rápida; não é nome real
Nome completo Pessoal sensível Sim Composição de relatórios oficiais
Data de nascimento Pessoal sensível Sim Verificação de identidade do paciente
Nome da mãe Pessoal sensível Sim Critério SUS de desambiguação
Número de prontuário Identificador institucional Sim Vínculo com prontuário físico/PEP
CNS do paciente Pessoal sensível Sim Identificação no e-SUS
Observações livres Pode conter sensível Sim Campo livre — pode conter informação clínica

2.3 Sobre o atendimento (encounters)

Armazenados na tabela encounters do vault local. A partir de v0.11 (Migration 010, P0.1), os 7 campos clínicos são cifrados por campo com ChaCha20-Poly1305 + HKDF subkeys + AAD binding ao encounter.id, em adição à cifragem de página do vault em repouso:

Campo Categoria Cifrado field-level?
soap_subjective Dado de saúde — sensível (LGPD Art. 11) Sim (Migration 010)
soap_objective Dado de saúde sensível Sim (Migration 010)
soap_assessment Dado de saúde sensível Sim (Migration 010)
soap_plan Dado de saúde sensível Sim (Migration 010)
ciap2_codes Dado de saúde Sim (Migration 010)
cid10_codes Dado de saúde Sim (Migration 010)
medications Dado de saúde Sim (Migration 010)
procedures Dado de saúde (códigos SIGTAP) Não (códigos categóricos, baixo risco)
professional_cpf Pessoal sensível Sim (field-level legacy)
hash_sha256 Integridade Não (não-PII; verificado na leitura, P0.5)

Decisão de design: em v0.9 o conteúdo SOAP ficava em texto plano dentro do SQLite cifrado em repouso. Em v0.11 (2026-05-15) introduziu-se a cifragem de campo dos 7 campos clínicos via Migration 010, com upgrade automático de rows legadas (upgrade_vault_encryption()). Ver docs/CRYPTO-DESIGN.md e docs/reviews/anotae-hardening-backlog.md (P0.1).

2.4 Anexos clínicos (attachments — v0.10, Migration 005)

Armazenados na tabela attachments vinculada a encounters:

Campo Categoria Cifrado? Tratamento
Binário do anexo (JPEG/PNG/PDF) Dado de saúde sensível Sim (field-level via data_encrypted) EXIF removido antes de persistir (imagens)
Nome do arquivo original Pode conter PII Sim
MIME type Categórico Não Validado contra allowlist (image/jpeg, image/png, application/pdf)
Tamanho em bytes Métrica Não Limite hard MAX_UPLOAD_BYTES
SHA-256 Integridade Não Anti-duplicação

EXIF stripping (anotae/upload_bridge/exif.py + attachment_repository._strip_exif_if_image) remove GPS, modelo de câmera, datetime, thumbnail e qualquer metadado da imagem antes de cifrar e persistir. Auditado via evento attachment.exif_stripped.

2.5 Índice RAG retrieval-only (v0.11, Migrations 006/008)

A partir de v0.11 o usuário pode opt-in (RAGConsentDialog) em indexar seu próprio prontuário para busca semântica local. Armazenado na tabela rag_chunks:

Campo Categoria Cifrado? Notas
Texto do chunk SOAP (~512 chars) Dado de saúde sensível Sim (field-level, P0.6 Migration 008) ChaCha20-Poly1305 + HKDF subkey + AAD com chunk.id
Embedding vetorial (384 floats) Derivado de PII Sim (field-level, Migration 008) Modelo multilingual-e5-small ONNX local
patient_id_local Identificador local Não Indexação para escopo por paciente
model_id (string) Métrica Não Versionamento do modelo de embedding

O modelo de embedding é executado localmente via ONNXRuntime; nenhum vetor sai da máquina. O índice é descartado quando o vault é fechado/excluído.

2.6 Modelos LLM locais (v0.11)

Modelos GGUF baixados sob demanda via ModelManager (llama.cpp embutido). Armazenamento em disco fora do vault (~/.anotae/models/), não cifrado porque são artefatos públicos baixados de HuggingFace, não contêm PII.

Item Categoria Cifrado? Notas
Arquivo .gguf (~4 GB típico) Artefato público Não SHA-256 verificado na primeira carga
Manifesto local de modelos Catálogo Não JSON com URLs, hashes, recomendações
Cache de inferência (KV-cache em RAM) Derivado de PII Sim (somente em RAM, nunca persistido) Liberado ao descarregar modelo
Saída de inferência Pode conter sensível Tratada como parte do encounter (cifrada via Migration 010 ao salvar)

Nenhuma decisão clínica é automatizada: a saída do LLM é apresentada ao profissional para revisão antes de inserir em campos SOAP. Cada uso é registrado em metadata.ai_assisted=true no encounter exportado (CFM 2.454/2026).

2.7 Áudio ASR (v0.12-4, opt-in)

Quando o usuário ativa gravação no ASRRecordWidget:

Item Categoria Cifrado? Tratamento
Áudio bruto WAV em RAM Dado de saúde sensível (voz do paciente) Não (efêmero) Nunca persistido em disco; descartado após transcrição
Texto transcrito Dado de saúde sensível Sim ao salvar (via Migration 010) Inserido no campo SOAP correspondente após revisão do profissional
Modelo whisper.cpp (ggml-small) Artefato público Não SHA-256 verificado

A janela de gravação informa explicitamente que o áudio não sai da máquina e não é persistido. A transcrição acontece em CPU/GPU local via pywhispercpp.

2.8 Canal mobile HTTPS+QR (v0.11, upload_bridge)

Canal opcional para enviar fotos do celular para o desktop dentro da mesma rede Wi-Fi:

Aspecto Tratamento
Transporte HTTPS local com certificado self-signed efêmero (regenerado a cada sessão)
Bind 127.0.0.1 + IP LAN detectado (não exposto à internet)
Autenticação Token aleatório de 256 bits transmitido apenas via QR code físico; TTL único; single-use após upload bem-sucedido
Origin/Host check Validado em todo POST (P0.2)
Conteúdo Imagens JPEG/PNG/HEIC com EXIF stripping antes de persistir como attachment
Logs Sem PII; apenas upload.success/upload.failure no audit log

Mensagens de erro nunca expõem o token nem o conteúdo. O servidor encerra automaticamente após o primeiro upload ou timeout.

2.9 RAG de protocolos clínicos públicos (v0.11, Migration 009)

Documentos públicos (PCDT/MS, SBMFC) podem ser indexados separadamente para consulta. Não contém PII — são publicações governamentais/científicas. Tabela protocol_chunks armazena os chunks em texto plano + embedding (ambos não-PII, mas cifrados field-level junto do restante por uniformidade operacional).

Manifest e controles de vigência (v0.12, RFC-007 planejado para v0.13):

O manifest de protocolos (vocabularies/protocolos/manifest.json) registra metadados de cada documento curado. O schema v2 (RFC-007) adicionará campos de proveniência e vigência que constituem controles adicionais de qualidade:

Campo manifest v2 Controle Ameaça mitigada
sha256 Integridade do PDF baixado Adulteração de fonte ou download corrompido
effective_date Data de vigor da edição citada Citar diretriz vencida como vigente (modo de falha #1)
superseded_by Cadeia de versões Profissional não sabe que existe versão mais recente
verified_at Última confirmação manual pelo mantenedor Degradação silenciosa de vigência ao longo do tempo
review_required_by Prazo de revisão obrigatória (18 meses) Manifest envelhecendo sem alerta ativo
source_authority Entidade emissora (MS-CONITEC, WHO, etc.) Confundir fonte secundária com diretriz oficial
license Licença verificada por entry Redistribuição inadvertida de conteúdo com licença restritiva

O que a query do usuário NÃO contém: a busca semântica em Ctrl+K usa texto livre digitado pelo profissional — nunca campos clínicos do encontro aberto, identificadores de paciente, ou conteúdo do SOAP. A query é processada localmente pelo embedder ONNX; nenhum dado trafega para fora do dispositivo.

Ausência de PII confirmada: o índice RAG de protocolos (protocol_chunks) contém exclusivamente texto extraído de PDFs públicos e os embeddings correspondentes. Não há cruzamento com dados de pacientes. A tabela é cifrada field-level por uniformidade operacional, não por necessidade de sigilo de PII.

2.10 Dados que o Anotae NUNCA coleta

  • ❌ Telemetria de uso (sem opt-in explícito; nenhum opt-in disponível ainda)
  • ❌ Crash reports automáticos
  • ❌ Localização geográfica
  • ❌ Dados biométricos (apesar de processar voz no ASR, áudio é efêmero — não-persistido)
  • ❌ Conteúdo de outras abas do navegador
  • ❌ Identificadores publicitários
  • ❌ Padrão de uso do LLM (qual prompt foi enviado, qual saída foi aceita)

3. Bases legais

Tratamento Base legal LGPD Justificativa
Dados do profissional (próprio usuário) Art. 7º, V — execução de contrato/serviço solicitado pelo titular O profissional usa a ferramenta voluntariamente
Dados de paciente (anotações clínicas, anexos, RAG, transcrição ASR) Art. 11º, II, alínea "f" — proteção da saúde, exclusivamente, em procedimento realizado por profissional de saúde Profissional é o controlador efetivo; todos os tratamentos são executados localmente sob seu comando
Anonimização para pesquisa/ensino Art. 12 — dados anonimizados não são considerados pessoais Função anonymize_* + k-anonymity opcional (k≥1) removem identificadores
Manifesto remoto de modelos LLM (v0.12-2, opt-in) Art. 7º, IX — interesse legítimo Conexão a HuggingFace baixa apenas metadados públicos (catálogo); nenhuma PII é enviada

4. Princípios LGPD aplicados

Finalidade (Art. 6º, I)

Cada dado coletado tem propósito específico documentado nesta tabela. Campos opcionais ficam vazios sem prejuízo. RAG, LLM e ASR são opt-in com diálogo de consentimento explícito (RAGConsentDialog, master switch IA na SettingsDialog).

Adequação (Art. 6º, II)

Coletamos só o necessário para compor relatórios SUS. Sem campos "extras só para colecionar". Embeddings RAG são derivados de texto que já existe no vault — não há nova coleta.

Necessidade (Art. 6º, III)

Handle do paciente em claro é justificável (busca rápida, sem PII). PII forte cifrada por campo. Áudio ASR não persiste em disco (descartado após transcrição).

Livre acesso (Art. 6º, IV)

Profissional acessa todos seus dados via UI a qualquer momento. Vault é arquivo .db portável.

Qualidade dos dados (Art. 6º, V)

Validators (CNS algoritmo DATASUS, CPF módulo 11, CIAP-2 e CID-10 sintáticos) bloqueiam dados malformados. Hash SHA-256 verificado na leitura de encounters (P0.5).

Transparência (Art. 6º, VI)

Documento aberto + código aberto (AGPL-3.0). Disclaimers in-app no editor SOAP e em todo painel de IA (AIAssistantPanel). Política de Uso de IA Anotae em docs/AI-USAGE-POLICY.md.

Segurança (Art. 6º, VII)

  • Argon2id KDF com parâmetros adaptativos
  • ChaCha20-Poly1305 AEAD para cifra de campo (encounters, attachments, rag_chunks)
  • HKDF para subkeys por escopo (field_key, attachment_key, rag_key)
  • AAD binding ao ID da linha para impedir swap de ciphertext
  • SQLite Multiple Ciphers para cifra de toda a base
  • Hash SHA-256 de integridade por encounter (verificado na leitura)
  • Audit log append-only com hash chain linear (P0.4, Migration 007)
  • Origin/Host check + token TTL single-use no canal mobile (P0.2)
  • TLS local com certificado self-signed efêmero no canal mobile
  • EXIF stripping automático em imagens anexadas

Prevenção (Art. 6º, VIII)

  • Bandit + Semgrep + pip-audit em CI
  • mypy --strict em CI
  • Testes unitários: 2252 testes / 100% das linhas alcançáveis (gate v1.0 ≥80% largamente superado)
  • Ausência de eval/exec, parâmetros SQL parametrizados
  • Hooks pre-commit (ruff check + ruff format + bandit)

Não-discriminação (Art. 6º, IX)

Sem decisões automatizadas baseadas em perfil. Sem auto-diagnóstico, auto-prescrição ou IA generativa em produção sem revisão humana (CFM 2.454/2026). LLM atua apenas em modo assistente com saída revisável pelo profissional.

Responsabilização (Art. 6º, X)

Audit log por encounter (campo audit_log JSON) + audit log global append-only com hash chain (audit_events). Cada mudança fica registrada com timestamp. Eventos cobertos: encounter.created/updated/deleted, attachment.created/exif_stripped, rag.indexed, llm.inference, export.consent_recorded, vault.opened/closed, upload.success/failure.


5. Riscos identificados e mitigações

Risco Severidade Probabilidade Mitigação
Vault .db copiado por terceiro com acesso à máquina Alta Média Senha-mestra Argon2id + cifra ChaCha20-Poly1305 + field-level encryption em campos sensíveis. Ataque de força bruta é caro
Profissional digita PII real em campo de feedback Média Baixa Aviso explícito no FeedbackDialog; texto limitado a 10k chars; nada é enviado automaticamente
Screenshot da janela "Pacientes" expondo nomes Média Média Lista mostra apenas handle + iniciais (PAC-001 (JS)); nome completo só ao editar
Código/RFC futura introduzir vazamento via log Alta Baixa CLAUDE.md §2.1 proíbe explicitamente logar PII; revisões de PR cobrem; testes em tests/docs/test_no_unfulfilled_security_claims.py
Build PyInstaller incluir vault.db de teste por engano Alta Baixa .gitignore cobre .anotae/, *.db; betatests/screenshots/ ignorado
Anonimização determinística reidentificável via correlação Média Baixa Salt aleatório por export (LGPD Art. 12); k-anonymity opcional (k≥5 recomendado para pesquisa)
Áudio ASR persistir em swap/temp ao quebrar Alta Baixa Áudio bruto só em RAM (bytearray); descartado em finally; whisper.cpp opera em memória
Token do canal mobile interceptado Média Baixa Token aleatório 256 bits em QR físico; TTL único; single-use; Origin/Host check; servidor encerra após 1 upload ou timeout
Servidor mobile exposto à internet por engano Alta Baixa Bind explícito apenas em 127.0.0.1 + IP LAN; TLS self-signed só aceita conexão da mesma sessão; firewall do SO normalmente bloqueia
LLM alucina e profissional aceita sem revisar Alta Média Disclaimer em AIAssistantPanel; saída sempre apresentada para revisão; metadata ai_assisted=true registrada; Política de Uso de IA cobre
Embeddings RAG reversíveis se chave comprometida Alta Baixa Chave field-encryption derivada da senha-mestra via HKDF; mesma chave do vault — comprometimento simultâneo
Manifesto remoto HF expor padrão de uso Baixa Baixa Opt-in explícito (SettingsDialog); apenas baixa catálogo público; nenhuma PII é enviada; sem identificador de usuário
Modelo LLM/ASR malicioso baixado de HF Alta Baixa SHA-256 verificado na primeira carga; manifesto curado pela maintainer; gate v1.0 inclui auditoria de licenciamento
Cache do LLM (KV) persistido em swap do SO Média Baixa RAM-only; descarregamento chama gc.collect() para liberar imediatamente; idle-unload automático após timeout
Attachment com EXIF/GPS persistir por engano Alta Baixa EXIF stripping em _strip_exif_if_image antes de persistir; auditado via attachment.exif_stripped; testes em tests/unit/test_attachment_repository_exif.py

6. Direitos dos titulares (Art. 18)

Como o vault é local na máquina do profissional, ele tem acesso direto a:

  • Confirmação da existência de tratamento (Art. 18, I): UI mostra todos os encounters/pacientes/anexos
  • Acesso aos dados (Art. 18, II): janelas Vault → Pacientes/Atendimentos
  • Correção (Art. 18, III): Editar paciente/atendimento
  • Anonimização (Art. 18, IV): exportar XLSX com flag "Anonimizar" + k-anonymity opcional
  • Eliminação (Art. 18, VI): botão Remover; deletar vault.db apaga tudo (inclui attachments cifrados e índice RAG)
  • Portabilidade (Art. 18, V): exportação XLSX disponível

Pacientes que queiram exercer direitos sobre seus dados precisam contatar o profissional/instituição que os atendeu — o Anotae é ferramenta sob controle do profissional, não independente.


7. Anonimização (Art. 12)

Funções dedicadas em anotae/core/anonymizer.py:

  • anonymize_encounter(encounter, *, generalize_dates=False, salt=None): substitui CPF por vazio, CNS e patient_id_local por hash truncado com salt; opcionalmente generaliza data para 1º do mês.
  • anonymize_patient(patient, *, generalize_dates=False, salt=None): handle → ANON-{hash}; PII forte → vazio; birth_date → 1º do trimestre se generalize_dates.
  • anonymize_professional(professional, *, salt=None): cns → ANON-{hash}; PII forte → vazio; categóricos (role, CNES) preservados.
  • generate_anon_salt(): salt aleatório por export para impedir correlação cross-dataset.
  • apply_k_anonymity(encounters, k): suprime encounters cuja combinação (data, CIAP, CID) aparece <k vezes. Aplicado após anonimização. K=5 é recomendação clássica em pesquisa.

Output da anonimização é considerado dado anonimizado sob LGPD Art. 12 e pode ser usado para pesquisa/ensino sem necessidade de consentimento individual.


8. Retenção e eliminação

  • Vault local (encounters, pacientes, anexos, audit, índice RAG): retenção indefinida sob controle do profissional. Backup automático cifrado em ~/.anotae/backups/ com retenção configurável (default 7 dias).
  • Áudio ASR bruto: ZERO retenção. Apenas em RAM, descartado imediatamente após transcrição em finally.
  • Cache de modelos LLM/ASR (~/.anotae/models/): persistente fora do vault, sem PII. Pode ser deletado a qualquer momento sem afetar dados clínicos.
  • Cache de inferência LLM (KV-cache): RAM-only; liberado ao descarregar modelo (ModelManager.unload()) ou no auto-unload por idle.
  • Logs de aplicação: ZERO PII. Logs operacionais não persistem por padrão.
  • Logs de feedback: arquivos JSON em ~/.anotae/feedback/ sob controle direto do usuário; nunca enviados automaticamente.
  • Certificado TLS do canal mobile: efêmero, regenerado a cada sessão; descartado ao fechar servidor.
  • Eliminação: deletar vault.db ou usar UI para deletar registros individuais. Sem soft-delete — eliminação é definitiva. Attachments e chunks RAG vinculados são eliminados em cascata.

9. Compartilhamento e transferências

Destino O que é compartilhado Quando Base legal
e-SUS APS PEC Texto SOAP do encounter (via injeção pela extensão) Sob ação explícita do profissional Profissional é controlador efetivo
SISREG III Justificativa clínica idem Idem Idem
SEI/DF Texto de relatório renderizado Sob ação explícita + confirmação dialog Idem
Celular do profissional (canal mobile) Foto local enviada via QR code → HTTPS local Sob ação explícita; QR efêmero single-use Profissional é controlador (mesma pessoa nas 2 pontas)
HuggingFace (catálogo de modelos, opt-in) Apenas requisição GET a manifesto público; sem identificador de usuário Quando profissional clica em "Verificar atualizações HuggingFace" Art. 7º, IX — interesse legítimo (modelos LLM/ASR são software público)
Nuvem / servidores com dados clínicos NADA NUNCA CLAUDE.md §2.1 proíbe

Transferência internacional de dados pessoais: ZERO. Anotae não envia dados clínicos para fora da máquina do usuário em nenhum cenário. Conexões a HuggingFace transferem apenas metadados públicos do catálogo de modelos.


10. Atualizações deste RIPD

Este documento é versionado junto com o código. Mudanças significativas em coleta/tratamento de PII (ex: nova migração que adiciona campos, mudança em política de cifra, novo componente que processa dados clínicos) exigem revisão deste documento no mesmo PR via RFC pública (CLAUDE.md §4.1).

Data Versão Mudanças
2026-05-07 0.9.0-beta3 Versão inicial após RFC-003 (cadastros estruturados de Patient e Professional)
2026-05-16 0.12.x Cobertura ampliada para componentes v0.10–v0.12: attachments (§2.4, Migration 005), field-level encryption de encounters (§2.3, P0.1 Migration 010), índice RAG retrieval-only (§2.5, P0.6 Migration 008), modelos LLM locais (§2.6), áudio ASR (§2.7), canal mobile HTTPS+QR (§2.8), RAG de protocolos públicos (§2.9), manifesto remoto HF opt-in (§3, §9). Riscos novos incorporados (§5): áudio em swap, token mobile, exposição LAN, alucinação LLM, EXIF, cache KV. Cobertura k-anonymity em §7.

11. Contato e relato de incidentes

  • Suporte geral: suporte@anot.ae
  • Incidente de segurança / vazamento de PII: ver SECURITY.md para divulgação coordenada
  • DPO/Encarregado (a designar para v1.0): será publicado neste documento; gate de publicização inclui designação formal

Nenhum incidente registrado até a data desta versão.