ECOA

- ESTÚDIO
- MILWEB
- TIPO
- PRODUTO DIGITAL
- STATUS
- NO AR
02 / A IDEIA
Rede social anônima · produto completo com IA
Pessoas querem desabafar e ser ouvidas sem expor o rosto, e as redes tradicionais punem a vulnerabilidade.
01 / 01
revoke select + grant em lista explícita de colunas: nem a API consegue ligar conta a post.

02 / 02
Regex determinístico sempre ativo mais Claude Haiku opcional. Indisponibilidade da IA nunca bloqueia a publicação.

03 / 03
Frase de 6 palavras com hash bcrypt reatribui a conta via RPC, sem precisar de nenhum dado pessoal.

04 / 04
Sem hashtag livre, sem DM e com 13 tópicos curados: estrutura pensada contra campanhas coordenadas.

01
02
03
04
04 / POR BAIXO DO CAPÔ
01O stack em produção é Next.js 16 (App Router, Turbopack) sobre Supabase Postgres na região sa-east-1, escolhida por residência de dados no B…+
O stack em produção é Next.js 16 (App Router, Turbopack) sobre Supabase Postgres na região sa-east-1, escolhida por residência de dados no Brasil, pensando em LGPD. É deliberadamente mais simples que a arquitetura-alvo documentada no blueprint do projeto (NestJS, Redis, NATS JetStream, Meilisearch, React Native/Expo): o MVP trocou tudo isso por um atalho de validação rápida. Como o produto é só texto e não tem grafo social (sem seguir, sem seguidores), o problema mais caro de uma rede social, que é o fan-out de timeline, simplesmente não existe. Cada post pesa poucos KB e a query do feed é uma view Postgres com paginação por keyset, sem fila nem cache dedicado.
02Anonimato aqui não é 'sem cadastro', é uma garantia de banco de dados. Cada pessoa entra via signInAnonymously() do Supabase e recebe um pub…+
Anonimato aqui não é 'sem cadastro', é uma garantia de banco de dados. Cada pessoa entra via signInAnonymously() do Supabase e recebe um public_number gerado por uma função Postgres (sorteio com checagem de unicidade), exibido como "Usuário #48291". O UUID interno (profiles.id) nunca sai do banco: um revoke select remove o acesso padrão das tabelas posts, comments e profiles para o papel authenticated, e um grant select seguinte devolve acesso só a uma lista explícita de colunas, sem user_id. Na prática, mesmo uma query client-side maliciosa não consegue ligar conta a conteúdo, porque o privilégio pra isso não existe no papel do banco. Não é uma regra de aplicação que dá pra contornar. Também não existe página de perfil navegável: tocar num "#48291" não abre histórico nenhum, decisão deliberada anti-stalking.
03A moderação roda em dois estágios, ambos em lib/moderation.ts. O estágio 0 é determinístico e instantâneo: normalização Unicode NFKC (contra…+
A moderação roda em dois estágios, ambos em lib/moderation.ts. O estágio 0 é determinístico e instantâneo: normalização Unicode NFKC (contra bypass por homóglifos), bloqueio de links, regex de PII (e-mail e telefone) que só sinaliza, e um regex de sinal de crise ("quero morrer", "me machucar") que nunca bloqueia a publicação, porque só existe pra acionar apoio. O estágio 2 é opcional: uma chamada à Claude Haiku com saída JSON estruturada, cujo prompt de sistema instrui explicitamente "na dúvida, não sinalize", porque um falso positivo silenciando alguém em sofrimento é tratado como pior que um falso negativo. O design é fail-open por princípio: se a API cair ou estourar o timeout de 8s, o conteúdo passa. Hoje, sem crédito de API configurado em produção, só o estágio 0 está realmente ativo. Denúncias da comunidade viram auto-remoção via trigger Postgres a partir de 3 denunciantes distintos, sem fila de moderação dedicada. O painel do Supabase faz esse papel no MVP.
04Algumas decisões deixam claro o tipo de produto que é. Nunca vai ter mensagem privada, tratada no projeto como "vetor de assédio e aliciamen…+
Algumas decisões deixam claro o tipo de produto que é. Nunca vai ter mensagem privada, tratada no projeto como "vetor de assédio e aliciamento". Nunca vai ter hashtag livre: só uma taxonomia curada de 13 tópicos, justamente pra impedir campanhas coordenadas. E o feed é cronológico, sem scroll infinito, com "marcos de respiro" a cada ~50 posts, que é decisão estrutural anti-vício e não só de UX. A recuperação de conta sem e-mail usa uma frase de 6 palavras (lista de 240 termos, ~47 bits de entropia) com hash bcrypt, reatribuída via RPC com ON UPDATE CASCADE propagando pra posts, comentários e reações. Os limites atuais estão documentados com honestidade: o contador de tentativas de recuperação é anulado pelo rollback da mesma transação que gera o erro de autenticação. É um detalhe reconhecido e aceito, porque a entropia da frase e o custo do bcrypt já seguram a maior parte do risco. Uma suíte e2e de 19 cenários roda contra o banco real, incluindo checagem de que a privacidade por coluna realmente impede o vazamento de user_id.
CONSTRUÍDO COM — Next.js · TypeScript · Supabase · PostgreSQL · Claude API
06 / RESULTADO



Loja de iPhone
