Política de Privacidade — ChainLife
Última atualização: 2026-07-29 · Versão: 1.0
ChainLife é um dashboard pessoal de manutenção de bikes e tracking de pedaladas em estágio beta, operado individualmente. Esta política descreve quais dados pessoais são coletados, com que finalidade, com quem são compartilhados e quais são os direitos do titular sob a Lei Geral de Proteção de Dados (Lei nº 13.709/2018 — LGPD).
A linguagem é direta e factual. Cada afirmação aqui é verificável no código-fonte público do projeto (referências cruzadas em docs/legal/data-inventory.md).
1. Quem somos (Controlador dos dados)
- Controlador: Lucas Trindade (pessoa física)
- Encarregado pelo Tratamento de Dados Pessoais (DPO): Lucas Trindade, conforme LGPD Art. 41
- Contato: [email protected]
- Jurisdição: Brasil — Lei Geral de Proteção de Dados (Lei nº 13.709/2018)
- Infraestrutura de produção: Raspberry Pi 5 hospedando FastAPI + PostgreSQL, exposto via Cloudflare Tunnel no domínio
chainlifeapp.com - Natureza do serviço: dashboard pessoal de manutenção de bicicletas operado individualmente em estágio beta aberto (cadastro não exige código de convite). A conexão com a Strava está sujeita a um limite de 10 atletas conectados simultaneamente (restrição do tier da API da Strava, não do ChainLife) — acima do limite, o app funciona normalmente, mas a conexão com a Strava fica em espera até a Strava autorizar mais vagas
2. Quais dados coletamos
O ChainLife coleta apenas os dados estritamente necessários para entregar as funcionalidades solicitadas pelo titular. O mapa abaixo é exaustivo e baseado em auditoria de código (docs/legal/data-inventory.md).
Dados de cadastro e autenticação
| Dado | Fonte | Armazenamento |
|---|---|---|
| signup / login | Supabase Auth (auth.users) |
|
| Senha (hash bcrypt) | signup | Supabase Auth |
user_id (UUID) |
gerado no signup | Supabase Auth + Postgres Pi5 (chave estrangeira em todas as tabelas do titular) |
| JWT access token | login + refresh | localStorage do navegador do titular |
| Refresh token | login + refresh | localStorage do navegador do titular |
| Código de convite (8 caracteres alfanuméricos) — histórico; não é mais exigido no cadastro (beta aberto desde 2026-07-29) | signup gating (quando reativado) | Postgres Pi5 (invite_codes) |
Dados de integração com plataformas terceiras (opt-in por OAuth ou credenciais)
| Plataforma | Dados coletados |
|---|---|
| Strava | OAuth access + refresh tokens, athlete_id Strava, escopo autorizado (read,activity:read_all,profile:read_all), atividades sincronizadas (nome, distância, tempo, tipo, data, equipamento, velocidade média/máxima, elevação) |
| Polar | OAuth access token (permanente), polar_user_id, atividades de ciclismo (id, esporte, distância, duração, ascensão, data) |
| Garmin Connect | Email + senha do Garmin Connect (necessários porque a API Garmin não oferece OAuth público), session blob da biblioteca garth, atividades de ciclismo (id, nome, tipo, distância, duração, data) |
Dados gerados pelo titular dentro do ChainLife
Todos armazenados em PostgreSQL no Pi5. Exceto o catálogo de peças (ver abaixo), protegidos por Row-Level Security (RLS) por user_id:
- Bicicletas cadastradas (nome, marca, modelo, tipo, quilometragem e horas acumuladas)
- Componentes (tipo, marca, modelo, datas, ciclos de manutenção, anotações livres)
- Log de manutenção (data, tipo, descrição, km, horas, custo, anotações)
- Ciclos de manutenção recorrentes e respectivo histórico
- Atribuições histórico install/remove de componentes
- Preferências de configuração (chave-valor)
- Catálogo de peças customizado (quando o titular adiciona itens próprios) — catálogo global e compartilhado: itens e notas ficam visíveis para todos os titulares do ChainLife, sem RLS por
user_id. Não insira dados pessoais em notas de catálogo
Dados técnicos e de uso
| Dado | Finalidade | Armazenamento |
|---|---|---|
endpoint Web Push, p256dh, auth_key |
dispatch de notificações push opt-in | Postgres Pi5 (push_subscriptions) |
| Email destinatário de alertas transacionais | envio de alertas de manutenção | passado em tempo de envio para Resend (não persistido localmente) |
Tracebacks de exceções, URL da request, user_id (apenas UUID) |
monitoramento de erros (opt-in via variável de ambiente SENTRY_DSN) |
Sentry / GlitchTip |
| Screenshots de notas fiscais (opcional) | OCR para preencher cadastro de componente | enviado pro Tesseract local; em fallback, base64 da imagem enviada à API Anthropic Claude Vision |
Dados em localStorage do navegador
Para autenticação client-side da SPA, o navegador armazena: JWT access token, refresh token, user_id (UUID) e email do titular. Esse armazenamento é necessário para evitar relogin a cada visita.
3. Para que usamos
As finalidades de tratamento são restritas e específicas:
- Autenticação e manutenção de conta — permitir acesso ao dashboard e proteger dados do titular
- Sincronização de atividades — ler atividades de ciclismo de Strava, Polar e/ou Garmin para calcular quilometragem acumulada em componentes e disparar alertas de manutenção
- Cálculo de manutenção — comparar quilometragem/tempo de uso de componentes contra ciclos configurados pelo titular
- Notificações push opt-in — avisar sobre componentes próximos do fim de vida útil
- Email transacional — enviar alertas de manutenção para o email cadastrado
- OCR de notas fiscais opcional — extrair texto de comprovantes para acelerar cadastro de componentes
- Monitoramento de erros (opt-in) — quando habilitado via variável de ambiente, capturar tracebacks para diagnóstico de falhas, com
send_default_pii=Falsee contexto de usuário restrito ao UUID
4. Bases legais (LGPD Art. 7)
| Finalidade | Base legal | Artigo LGPD |
|---|---|---|
| Manutenção de conta, autenticação, sync core, email transacional de alertas | Execução de contrato | Art. 7, V |
| Strava OAuth, Polar OAuth, credenciais Garmin, push notifications, OCR via Claude Vision | Consentimento explícito do titular | Art. 7, I |
| Sentry error monitoring (quando habilitado) | Legítimo interesse | Art. 7, IX |
Para finalidades que dependem de consentimento, este é coletado de forma específica e granular no momento da ativação (tela de OAuth da própria plataforma, prompt do navegador para push, formulário de credenciais para Garmin, ação explícita de upload para OCR). O titular pode revogar o consentimento a qualquer momento (ver §7).
5. Compartilhamento com terceiros
O ChainLife não revende dados pessoais e não compartilha informações com terceiros para fins de marketing ou perfilamento publicitário. Os compartilhamentos abaixo são exclusivamente técnicos e necessários à operação do serviço:
| Terceiro | Função | Localização | Privacy Policy |
|---|---|---|---|
| Supabase Inc. | Autenticação (signup, login, JWT) | Estados Unidos | https://supabase.com/privacy |
| Strava Inc. | OAuth + leitura de atividades | Estados Unidos | https://www.strava.com/legal/privacy |
| Polar Electro Oy | AccessLink API (leitura de exercícios) | Finlândia (União Europeia) | https://www.polar.com/en/legal/privacy-notice |
| Garmin Ltd. | Garmin Connect API (leitura de atividades) | Estados Unidos | https://www.garmin.com/en-US/privacy/ |
| Sentry / Functional Software Inc. | Monitoramento de erros (opt-in) | Estados Unidos | https://sentry.io/privacy/ |
| Cloudflare Inc. | Tunnel, CDN e DNS para chainlifeapp.com |
Edge global | https://www.cloudflare.com/privacypolicy/ |
| Resend Inc. | Email transacional (alertas de manutenção) | Estados Unidos | https://resend.com/legal/privacy-policy |
| Anthropic PBC | Claude Vision API (fallback OCR opt-in) | Estados Unidos | https://www.anthropic.com/privacy |
| Web Push providers (FCM, Mozilla, Microsoft etc.) | Dispatch de notificações push | Global, varia conforme o navegador do titular | Política do provedor escolhido pelo navegador |
Strava — declaração específica
O ChainLife integra a Strava sob autorização OAuth explícita do titular. ChainLife não acessa dados de outros atletas sem permissão explícita do dono da conta autorizadora. Dados Strava são usados exclusivamente para o propósito declarado (sincronização de atividades para cálculo de manutenção de componentes). As páginas que exibem dados Strava trazem o logo "Powered by Strava" conforme Strava Brand Guidelines. O titular pode revogar o acesso a qualquer momento pelo dashboard do ChainLife (que chama a API de disconnect) ou diretamente no painel de aplicações autorizadas da Strava.
6. Retenção dos dados
| Sistema | Período de retenção |
|---|---|
| Postgres Pi5 (dados do titular) | Indefinido enquanto a conta estiver ativa; o titular pode apagar via DELETE /auth/account no dashboard a qualquer momento |
| Backups locais Pi5 | 7 dias (rotação automática) |
| Backups offsite (PC Windows via Tailscale) | 30 dias (rotação manual; ciclo formal em implementação) |
| Sentry events | ~30 dias (política padrão Sentry, quando habilitado) |
| Sentry issues | ~90 dias (política padrão Sentry, quando habilitado) |
| Tokens OAuth Strava, Polar e credenciais Garmin | Até o titular fazer disconnect no dashboard |
| OAuth states (CSRF protection) | 10 minutos (TTL automático) |
| Logs Cloudflare | Conforme política Cloudflare aplicável ao plano em uso |
| Logs Resend | Conforme política Resend (~30 dias por padrão) |
Não há rotina automática de purge de dados antigos no banco de dados principal — o titular tem controle total sobre exclusão via dashboard.
7. Seus direitos (LGPD Art. 18)
O titular tem os seguintes direitos garantidos pela LGPD:
| Direito | Como exercer |
|---|---|
| Confirmação da existência de tratamento (Art. 18, I) | Login no dashboard confirma que há tratamento |
| Acesso aos dados (Art. 18, II) | Visualização direta no dashboard de bicicletas, componentes, manutenções e atividades sincronizadas |
| Correção de dados incompletos, inexatos ou desatualizados (Art. 18, III) | Endpoints de edição na interface (PUT em bikes, componentes, atividades, configurações) |
| Anonimização, bloqueio ou eliminação (Art. 18, IV) | Botão "Excluir conta" no dashboard chama DELETE /auth/account e remove os dados do Postgres + Supabase Auth |
| Portabilidade dos dados (Art. 18, V) | Botão "Baixar meus dados" em Configurações (GET /api/me/export), que entrega um arquivo JSON com seus dados; credenciais e tokens de conexão não são incluídos |
| Eliminação de dados tratados com base em consentimento (Art. 18, VI) | Disconnect Strava via dashboard: o titular escolhe manter os dados sincronizados, apagar as atividades Strava (desgaste recalculado) ou excluir a conta — a desconexão em si sempre revoga o acesso do app na Strava, mas só elimina dados se essa opção for escolhida; desautorizar diretamente no painel da Strava também apaga as atividades automaticamente. Disconnect Polar via dashboard; remoção de credenciais Garmin via tela de configurações; revogação no painel do próprio provedor também é eficaz |
| Informação sobre compartilhamento com terceiros (Art. 18, VII) | Esta política, §5 |
| Informação sobre a possibilidade de não fornecer consentimento e suas consequências (Art. 18, VIII) | Recusar consentimento OAuth bloqueia a sincronização da plataforma específica; o serviço base (cadastro manual de bikes, componentes e manutenções) continua plenamente funcional |
| Revogação do consentimento (Art. 18, IX) | Disconnect pela UI do ChainLife; revogação adicional pelo painel do provedor; push notifications podem ser desabilitadas nas configurações do navegador |
Para exercer qualquer direito, contato direto via DPO: [email protected]. Resposta em até 15 dias úteis.
8. Segurança técnica
Medidas técnicas implementadas e verificáveis em código:
- HTTPS obrigatório via Cloudflare Tunnel com certificado TLS gerenciado pela Cloudflare
- JWT ES256 com validação JWKS contra Supabase e checagem de audience (
authenticated) - Row-Level Security (RLS) no PostgreSQL por
user_idem todas as tabelas de propriedade do titular - Password hashing delegado ao Supabase Auth (bcrypt internamente)
- Encryption-at-rest em credenciais sensíveis (AES-256-GCM):
- Credenciais Garmin (email, senha, session blob) armazenadas em
config.value_encryptedBYTEA, criptografadas com AES-256-GCM e dado autenticado adicional (AAD =user_id|chave) que previne troca de tag entre titulares ou entre chaves - Tokens OAuth Strava e Polar armazenados em
platform_tokens.access_token_encryptedeplatform_tokens.refresh_token_encryptedBYTEA, com AAD =user_id|plataforma|access|refreshque previne troca cross-titular, cross-plataforma ou access↔refresh - Chave de criptografia (32 bytes) mantida em variável de ambiente
FIELD_ENCRYPTION_KEY, fora do código-fonte - Formato de payload com version byte (
0x01 | nonce(12B) | ciphertext+tag(16B)) habilita rotação futura zero-downtime - OAuth state com TTL de 10 minutos para proteção contra CSRF no fluxo OAuth
- Rate limiting (slowapi) nos endpoints de autenticação (signup, login, refresh, validate_invite), com chave por IP real (
CF-Connecting-IP, não falsificável pelo cliente) - Sentry
send_default_pii=Falsepor design, e contexto de usuário restrito ao UUID (sem email no payload Sentry) - Cadastro aberto, sem código de convite — o Supabase Auth pode exigir confirmação de email antes do primeiro login (conforme configuração do projeto), funcionando junto ao rate limiting acima como camada residual anti-abuso
- Secrets fora do repositório —
.envnão é commitado; arquivo.env.exampledocumenta as variáveis necessárias
9. Cookies e armazenamento local
- O ChainLife não emite cookies de sessão server-side — não há
SessionMiddlewareconfigurado no FastAPI - O navegador do titular utiliza
localStoragepara armazenar JWT access token, refresh token,user_id(UUID) e email — todos necessários para autenticação client-side da SPA e para evitar relogin a cada visita - Cookies de terceiros podem ser definidos por Cloudflare (anti-bot, SSL) e, quando habilitado, pelo SDK frontend do Sentry. Esses cookies seguem as políticas dos respectivos provedores
10. Menores de idade
O ChainLife é destinado a pessoas com 18 anos ou mais. Não coletamos deliberadamente dados pessoais de menores de idade. Caso seja identificado cadastro de menor, a conta será removida e os dados associados apagados. Pais ou responsáveis que identifiquem cadastro de menor podem entrar em contato com o DPO para remoção imediata.
11. Alterações nesta política
Versão atual: 1.0 (2026-05-26). Alterações materiais serão notificadas por email ao titular cadastrado com pelo menos 30 dias de antecedência. Versões anteriores ficam acessíveis via histórico Git do repositório público do projeto (audit trail aberto para transparência). Alterações que reduzirem direitos do titular exigirão novo consentimento.
Correção factual 2026-07-29: ajuste de precisão em dois pontos, sem mudança de direitos ou finalidades de tratamento — (1) §2 esclarece que o catálogo de peças é global/compartilhado entre titulares, não protegido por RLS por user_id; (2) §7 esclarece que o disconnect da Strava oferece escolha ao titular (manter dados, apagar atividades ou excluir conta), não elimina dados automaticamente.
Atualização de produto 2026-07-29: o beta deixou de exigir código de convite no cadastro — qualquer pessoa pode criar conta e usar o ChainLife (bikes, componentes, manutenção, atividades manuais). A conexão com a Strava permanece sujeita a um limite de 10 atletas conectados simultaneamente (tier Standard da API da Strava); acima do limite, a conexão fica indisponível até a Strava autorizar mais vagas, sem impacto no restante do serviço. O código de convite deixa de ser exigido no fluxo padrão, mas a tabela invite_codes é mantida com o histórico de quem entrou via convite antes desta mudança.
12. Limitações conhecidas (transparência)
O ChainLife está em estágio beta, com escala individual (cadastro aberto, sem código de convite; conexão com a Strava limitada a 10 atletas conectados pelo tier da API). Em nome da transparência, declaramos limitações conhecidas:
- Sentry
LoggingIntegration: quando o monitoramento está habilitado, mensagens de log interpoladas (logger.error("user %s failed ...", email)) podem inadvertidamente capturar PII. Hardening cravado no backlog técnico - Backups offsite de longo prazo: o ciclo de retenção atual é de 30 dias em PC Windows via Tailscale + scp. Não há retenção offsite de longo prazo formalizada além desse ciclo
- Encryption at rest do disco do Postgres Pi5: o sistema de arquivos do Pi5 não está cifrado no nível do disco (sem LUKS) na versão atual. Os campos sensíveis individuais (credenciais Garmin, tokens OAuth) estão cifrados em nível de aplicação via AES-256-GCM (§8), mas backup físico do dispositivo poderia expor dados não-sensíveis (bikes, manutenções, atividades). Migração para volume cifrado está cravada em backlog
13. Contato
Lucas Trindade — Encarregado pelo Tratamento de Dados Pessoais (DPO) Email: [email protected] Jurisdição: Brasil — Lei Geral de Proteção de Dados (Lei nº 13.709/2018)
Para exercer qualquer direito previsto na LGPD, esclarecer dúvidas sobre o tratamento de dados pessoais, reportar incidentes de segurança ou solicitar a remoção de conta, o contato deve ser feito via email acima. Resposta em até 15 dias úteis.
Powered by Strava — ChainLife exibe dados Strava conforme as Strava Brand Guidelines.