Esta página é o complemento operacional de LGPD — aqui o foco é o caminho
técnico real do dado, não o enquadramento legal.
Entrada — de onde o dado vem
Onde é processado e armazenado
Cada projeto é Postgres com Row Level Security habilitado nas tabelas que guardam dado de usuário
— não existe uma cópia “de leitura geral” fora do RLS para uso interno (ex: um data warehouse
espelhado sem controle de acesso próprio). Onde uma função
security definer precisa contornar o
RLS para uma operação legítima (ex: export_my_data(), delete_my_account()), ela é escrita para
filtrar explicitamente pelo próprio auth.uid()/family_id do chamador — nunca por um parâmetro
livre.
Quem tem acesso interno
Hoje o controle de acesso interno é o modelo de membros de projeto do próprio Supabase (quem tem login no dashboard do projeto) — não existe ainda uma segmentação formal de papéis internos (editorial, engenharia, suporte) com permissões diferenciadas por função dentro da plataforma:
Essa segmentação formal (RBAC interno por função, não só por produto) é um item real de roadmap de
maturidade organizacional — citamos aqui sem inflar: hoje o time é pequeno o suficiente para que o
acesso “de fato” seja mais amplo do que o princípio de menor privilégio recomendaria em regime
permanente, e achamos mais honesto declarar isso do que descrever uma segmentação que ainda não
existe.
Como um pedido de exclusão se propaga
A exclusão de conta usa duas camadas:on delete cascade no schema (para o caso simples) e uma
função explícita para os casos onde apagar em cascata ingenuamente afetaria outra pessoa.
Caso simples — NovelAI/NovelAudio (conta individual, sem conceito de família): a função
delete_my_account() apaga a linha em auth.users, e isso cascateia automaticamente para
profiles, subscriptions, favorites, reading_progress, listening_progress, tts_usage,
audio_access_log, email_log, free_time_grants e referral_codes — todas com
on delete cascade apontando para auth.users(id). Uma tabela exigiu ajuste antes: promo_codes
tinha created_by sem regra de exclusão (NO ACTION), o que impediria um administrador de excluir
a própria conta sem quebrar os códigos promocionais que ele criou — trocado para on delete set null, preservando o código para quem já o resgatou.
Caso com família — HistorinhAI: apagar em cascata direto pela conta seria perigoso, porque uma
família pode ter mais de um adulto (family_members). A função delete_my_account() primeiro
verifica se a pessoa é dona de uma família com outros membros — se sim, bloqueia com
family_has_other_members até que os outros membros sejam removidos primeiro (fluxo já disponível
no app). Registros que a pessoa criou mas que pertencem a uma família da qual ela é só membro
(children.user_id, reading_sessions.user_id como proveniência, não como posse) são
reatribuídos ao dono da família antes da exclusão, para não apagar dado de uma família que ela só
visitava. Só quando a pessoa é dona sem outros membros é que a função apaga explicitamente
reading_sessions, subscriptions e children da própria família antes de apagar a família e,
por fim, a linha em auth.users — que cascateia o resto (perfil, código de indicação, etc.).
Este desenho — cascade automático onde é seguro, função explícita onde exige avaliar posse
compartilhada primeiro — é o que garante que um pedido de exclusão (Art. 18, VI da LGPD) nunca
deixa dado pessoal órfão nem, no outro extremo, remove acesso de alguém que não pediu para saber
saída da família.