Skip to main content
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.