Contas e acesso
Desde 14 de outubro de 2026, todas as rotas da aplicação exigem uma sessão. Continuam públicas a página inicial (/, /en, /pt), esta documentação, os avisos legais (/privacidad, /terminos) e a página de vagas. Não há senhas: a conta é criada na primeira vez que a pessoa entra.
Como se entra
Uma só tela, /login:
- Escreve o seu e-mail. Enviamos um código de 6 dígitos e um link na mesma mensagem; qualquer um serve e ambos expiram em 5 minutos. A resposta é idêntica exista ou não a conta.
- Digita o código (ou abre o link). Se o e-mail não tinha conta, ela é criada nesse momento. Três tentativas falhadas invalidam o código; peça outro.
O acesso é só por e-mail (código ou link), enviado de acceso@futuros.xyz via AgentMail. Não há início de sessão com Google: o botão e o vínculo em /cuenta ficam desligados, a menos que quem implanta os ative (VITE_AUTH_GOOGLE=1 mais credenciais do Google).
Depois de entrar volta à página que pedia (?next=), validada como rota do próprio site; qualquer outro valor leva a /atlas.
Primeiro acesso
Um passo, opcional: nome visível, idioma, e uma nota sobre o que guardamos e como pedir a exclusão. Pode ser pulado e não volta a aparecer nesse dispositivo.
O que guardamos
| Dado | Onde | Para quê |
|---|---|---|
| E-mail, nome visível, idioma, foto de perfil (só se o Google for ativado) | Neon Postgres, AWS São Paulo (sa-east-1) | Identificá-lo e enviar o código |
| Sessões ativas (navegador, IP, data) | Mesma base | Encerrá-las; expiram após 30 dias sem atividade |
| Códigos e tokens de link | Mesma base, como hash, uso único | Verificar o acesso |
| Eventos de acesso (início, encerramento, códigos falhados, limites) com o IP em hash | Mesma base | Segurança; guardados 12 meses |
| Preferências, itens guardados (lista de acompanhamento, vistas, bandejas de comparação, portfólios) e atos cívicos (posições, emendas, comentários, respostas de consenso, votos em pilotos) | Mesma base | Acompanhar você entre dispositivos; participação identificada |
auth_login, auth_signup, auth_onboarding, preferences_migrated, saved_item_created, civic_action, account_export_requested, account_deleted | PostHog | Medir o funil; sem e-mail |
O que não guardamos: senhas (não existem), o e-mail em logs ou analítica, o código em claro.
O detalhe legal está no aviso de privacidade (§2, §5, §8, §9).
Seu espaço de trabalho
Com a sessão aberta, suas preferências, itens guardados e atos cívicos vivem na sua conta e acompanham você entre dispositivos:
| O quê | Onde |
|---|---|
| Idioma, tour, modelo de chat, ajustes de interface, aviso de instalação | user_preference (lista fechada de chaves; cada valor é validado e reescrito no servidor) |
Lista de acompanhamento de /mercados, vistas de /explorar, bandejas de comparação de pilotos e personas, portfólios de pilotos | saved_item (até 200 por tipo) |
Posições e emendas de propostas, respostas de /consenso, comentários, votos em pilotos | civic_action (ver Participação identificada) |
Migração única do que você já tinha no navegador. A primeira conta que entra em um navegador envia o que havia no localStorage (lista de acompanhamento, vistas, bandejas, portfólios e preferências) e depois o remove ou o renomeia para <chave>.migrated. É sempre "inserir se não existir": um dispositivo novo, vazio ou desatualizado nunca sobrescreve o que a conta já tem. Uma segunda conta no mesmo navegador não herda nada. As chaves recusadas pelo servidor (valores alterados ou grandes demais) são descartadas; uma falha transitória de rede é repetida na próxima inicialização. Os votos, emendas e respostas anônimas de antes não podem ser atribuídos e não são migrados; as chaves com e-mails em claro (futuros_comments_v1, futuros_comment_gate_v1) são apagadas. Seguem locais, de propósito: o idioma, o modelo de chat e as marcas do tour como cache de inicialização (lidos antes de a sessão carregar), o feedback do chat e as filas de demonstração.
Sessões, exportação e exclusão
Tudo fica em /cuenta; em qualquer página, o avatar do cabeçalho abre o menu com Sua conta e Sair (em telas estreitas, no topo do menu ≡).
Sessões. A página lista cada navegador com sessão aberta (navegador e sistema inferidos do User-Agent, última atividade). Cada sessão pode ser fechada separadamente; Sair em todos os dispositivos apaga todas as linhas de session do usuário, inclusive a atual. As sessões duram 30 dias e se renovam com o uso. Se quem implanta ativar o Google, ele pode ser vinculado e desvinculado na mesma página (a entrada por código continua disponível).
Exportação. Pedir exportação (POST /api/account/export) monta um arquivo JSON com perfil, conversas e mensagens (com as citações tal como exibidas), preferências, itens guardados, atos cívicos e eventos de auditoria, e envia ao e-mail da conta um link assinado válido por 24 horas. Para baixar são necessárias as duas coisas: o link sem alterações e a sessão aberta da mesma conta para a qual foi emitido (com outra conta, ou sem sessão, nada é entregue). Acima de 4 MB o arquivo é dividido em partes (a primeira com tudo menos as conversas; as seguintes, conversas inteiras) e o e-mail traz um link por parte. Uma exportação por hora por pessoa.
Exclusão. Excluir minha conta (POST /api/account/delete) exige digitar o e-mail da conta (maiúsculas e espaços são ignorados). Se você é a única pessoa proprietária de uma organização que tem outros integrantes, o pedido é recusado (409) até que você transfira a propriedade. Ao confirmar:
| Momento | O que acontece |
|---|---|
| Imediato | deleted_at é fixado; todas as sessões são fechadas; as chaves de API são desativadas e os tokens de embed revogados; qualquer tentativa de login é recusada; o evento account_deleted é registrado na analítica com um pseudônimo, nunca com o e-mail. |
7 dias depois (cron diário /api/account-purge, protegido por CRON_SECRET) | Perfil, sessões, conta Google vinculada, preferências, atos cívicos, conversas privadas com suas mensagens e itens guardados privados são apagados. Conversas e itens compartilhados com uma organização ficam nela com dono vazio ("ex-integrante"). Os eventos de auditoria ficam com ator vazio e o identificador substituído por um hash. |
O mesmo cron apaga os eventos de auditoria com mais de 12 meses. Não há "desfazer": durante os 7 dias a conta está fechada, não suspensa. Os recibos do fundo fiduciário de dados não são apagados (são o registro de proveniência da própria pessoa, já pseudônimo).
Organizações
Uma organização reúne uma equipe: compartilha conversas e itens salvos entre seus integrantes. Gerencia-se em /cuenta/organizacion; o seletor do cabeçalho (ou a folha do menu ≡ em telas estreitas) define o espaço ativo, e Pessoal é o espaço sem organização.
| Papel | Pode |
|---|---|
| Proprietário | Tudo: mudar papéis, transferir a propriedade, excluir a organização, além do que o administrador pode. |
| Administrador | Editar nome e identificador, convidar, cancelar convites, remover integrantes (não administradores nem proprietários) e editar ou apagar o que foi compartilhado, inclusive o de ex-integrantes. |
| Integrante | Ver o que foi compartilhado, criar o próprio e compartilhá-lo. |
- Criar. Qualquer pessoa; fica como proprietária. O identificador (a parte da URL) pode ser mudado uma única vez. Limites: 5 organizações criadas por pessoa por dia e 20 por conta.
- Convidar. Proprietário ou administrador informa um e-mail e um papel (administrador ou integrante). O convite chega por e-mail (no idioma da pessoa convidada se ela já tem conta, ou no de quem convida) com um link para
/invite/<id>, expira em 7 dias e só pode ser aceito pela conta com esse mesmo e-mail. No máximo 50 pendentes por organização e, contra abusos, 20 convites por hora por pessoa e 30 por hora por organização. Se o e-mail não puder ser enviado, o convite é cancelado e você é avisado. - Compartilhar. Cada conversa ou item salvo é privado até que seu autor o compartilhe com a organização ativa (chave em
/cuenta/organizacion, ou na barra lateral de/chatpara conversas). Só se compartilha com uma organização à qual você pertence. Autor, proprietários e administradores podem editá-lo ou apagá-lo; os demais só o veem. - Sair ou ser removido. O que você compartilhou fica na organização, sem dono, e aparece como de um ex-integrante; o privado continua seu. O último proprietário não pode sair nem mudar o próprio papel: primeiro transfira a propriedade (a regra roda dentro de uma única instrução SQL, então dois proprietários saindo ao mesmo tempo não deixam a organização sem dono).
- Excluir a organização (só proprietário, digitando o identificador). A organização, seus integrantes e convites são excluídos. As conversas e os itens que cada integrante compartilhou voltam a ser privados do autor; os de ex-integrantes são excluídos com a organização. Não há como desfazer.
- Auditoria. Criação, edições, convites (criação, cancelamento, aceite), mudanças de papel, transferências, saídas e remoções ficam no registro de auditoria, sem e-mails.
Chaves de API e tokens de embed
Fora do navegador não há cookie, então usam-se credenciais que você mesmo emite:
- Chaves de API (
ft_live_<48 hex>, /cuenta/claves): para a API pública/api/v1/*(e/data.json,/feeds/*), o servidor MCP/api/mcpe o chat via API/api/chat. Mostradas uma única vez e guardadas com hash. Teto de 600 solicitações por minuto por chave (cobrado em cada solicitação) e 500 mensagens de chat por dia por conta. O chat por chave é guardado no seu histórico, em uma conversa intitulada pelo cliente ou "API". No máximo 20 chaves ativas. Guias: API pública, MCP. - Tokens de embed (
ft_embed_…, /cuenta/embeds): para iframes/embed/stat,/embed/charte/embed/boardem outros sites. Você escolhe as rotas permitidas e a validade (365 dias por padrão, máximo 730); abrem só essas rotas e os arquivos de dados que esses widgets leem. No máximo 25 ativos. Guia: Embeds.
Revogar uma chave ou um token vale em menos de um minuto. Se você excluir sua conta, suas chaves são desativadas e seus tokens revogados na hora.
O muro de acesso (para operadores)
A fronteira de segurança é o Routing Middleware da Vercel (middleware.ts, lógica em server/auth/wall.ts), não a interface. Para cada pedido fora da lista pública:
- Lê o cookie de sessão e valida-o com uma chamada interna ao
/api/auth/get-sessiondo mesmo deploy. Um resultado positivo é lembrado 60 s em memória (chave: hash do token); um negativo nunca. - Sem sessão válida: uma navegação de documento recebe
302 /login?next=<rota>; qualquerfetch, API, embed ou MCP recebe401 {"error":"auth_required"}comWWW-Authenticate: Bearer. - Se a validação falha por erro (tempo limite de 3 s, 500, JSON inválido), fecha: 302/401 e uma linha
[auth]nos logs. Nunca deixa passar nem devolve um 500. - Credenciais de máquina: as chaves de API (
Authorization: Bearer ft_live_…ouX-Api-Key) são aceitas em/api/v1/*,/data.json,/feeds/*,/api/mcpe/api/chat; os tokens de embed (?et=ouX-Embed-Token) em/embed/*e nos arquivos de dados que esses widgets leem. Uma credencial na superfície errada é recusada sem consulta. O muro as verifica com uma chamada interna a/api/machine-verifydo mesmo deploy (origem da plataforma, nunca oHostda requisição), guarda o veredito positivo por 60 s e nunca um negativo. O teto por chave é cobrado em cada solicitação, inclusive com veredito guardado. Se a verificação não for possível (sem segredo interno, banco fora do ar), fecha:503comRetry-After, nunca uma passagem. /api/machine-verifyestá na lista pública apenas para que o muro possa chamá-lo; a própria função responde401a qualquer requisição que não tragaAUTH_INTERNAL_SECRET(comparação em tempo constante). Não usa o cabeçalho de isenção do observador de scraping, que é derivável quandoSCRAPE_INTERNAL_TOKENnão está definido.
Lista pública exata: /, /en, /pt, /login, /invite/*, /privacidad, /terminos, /roles*, /api/auth/*, /api/health, /manifest.webmanifest, /sw.js, /robots.txt, /sitemap*.xml, /favicon*, /assets/*, /fonts/*, /og/*, /.well-known/*, /icons/*, o deck de investidores (/deck, /deck.html, /api/deck-access, /api/deck-slide), o salto de outreach /o/* (/api/o), e os endpoints com segredo próprio: /api/alerts-digest (cron), /api/access-webhook (Stripe), /api/questions e /api/chat-turns (token de operador). Pedidos OPTIONS passam.
Variáveis de ambiente:
| Variável | Efeito |
|---|---|
AUTH_WALL_DISABLED=1 | Interruptor de emergência: desativa o muro por completo. Registado nos logs em cada arranque a frio. Deve estar ausente em produção. |
AUTH_INTERNAL_SECRET | Obrigatória para chaves de API e tokens de embed (16 ou mais caracteres; produção e preview). Sem ela, toda credencial de máquina recebe 503. |
EMBED_TOKEN_PEPPER | Pimenta do hash dos tokens de embed. Sem ela não se criam nem se verificam tokens (503). |
API_KEY_MAX_PER_MINUTE, CHAT_USER_DAILY_MAX | Tetos ajustáveis: 600 por chave por minuto, 500 mensagens de chat por conta por dia. |
AUTH_INTERNAL_ORIGIN | Origem que o muro chama para validar a sessão; por padrão, a do próprio deploy. |
VERCEL_AUTOMATION_BYPASS_SECRET | Se definido, o muro reenvia-o para que uma preview protegida possa validar-se a si mesma. |
AUTH_SIGNUP_DISABLED, AUTH_GOOGLE_DISABLED | Alavancas do servidor de autenticação; /login mostra o erro correspondente. |
Estado
| Capacidade | Estado |
|---|---|
Muro de acesso, /login por código e link (e-mail via AgentMail), onboarding | Ao vivo |
| Histórico de chat por conta | Ao vivo |
Preferências e itens guardados por conta, participação identificada, painel /cuenta (perfil, sessões, exportar, excluir) | Ao vivo |
| Organizações (papéis, convites, compartilhamento), chaves de API, tokens de embed | No ar |