voltar

// projeto em produção · U4 · GPU

Projeto em produção

O MyNook - mynook.social - nasceu de uma convicção simples: as redes sociais perderam o rumo. Quis construir uma alternativa onde a privacidade não é uma definição escondida, mas o ponto de partida, e onde és tu a decidir quem vê cada coisa.

MyNook

Em produção

Plataforma social full-stack · mynook.social

Uma rede social construída de raiz, com foco em privacidade, segurança e experiência de utilizador. Os utilizadores podem publicar, partilhar fotos, gerir amizades por categorias (amigos, família) e personalizar o seu perfil com temas únicos.

MyNook, captura 1screenshots/mynook-1.png
MyNook, captura 2screenshots/mynook-2.png
MyNook, captura 3screenshots/mynook-3.png
  • Feed de posts com fotos (até 4 por publicação) e visibilidade configurável por categoria de amizade
  • Notificações em tempo real via SignalR (WebSocket)
  • Autenticação JWT com refresh token rotation e 2FA TOTP (Google Authenticator)
  • Perfis personalizáveis: temas de fundo, fontes, bordas e elementos animados
  • Painel de administração com moderação de conteúdo e gestão de denúncias
  • Conformidade RGPD: exportação de dados e eliminação de conta com anonimização faseada
  • Interface disponível em PT, EN, ES e FR
ASP.NET Core 10 C# 13 Next.js 16 React 19 TypeScript PostgreSQL Entity Framework Core SignalR Tailwind CSS Docker Cloudinary Resend + React Email

A ideia por trás

Quando me perguntei o que é que mudava hoje, a primeira resposta foi imediata: as redes sociais. Começaram por ser boas, mas tornaram-se cada vez mais manipuladoras, até virarem um ninho de desinformação e ódio. Depois de ver o documentário The Great Hack, que reflete bastante o que penso, deixei de publicar fotos em 2019.

Foi daí que veio o mynook.social (Meu Recanto): uma rede social com a privacidade no centro. Os perfis públicos mostram apenas o nome e a foto de perfil; um perfil privado fica completamente oculto, nem aparece na barra de pesquisa.

A maior diferença está nas amizades. Ao adicionar alguém, classificamo-lo como família, amigo ou ambos, e é isso que define quem vê cada publicação. Em vez de privatizar foto a foto, a restrição vive na própria relação. Todos temos fotos que preferíamos que a família não visse, e outras que só faz sentido partilhar com ela. No MyNook, isso resolve-se à partida.

Por dentro do MyNook

Processo & design

Antes de escrever a primeira linha de código, comecei por desenhar diagramas de domínio para mapear as entidades necessárias e as relações entre elas. Só depois de ter o modelo completo é que parti para a implementação.

Arquitetura & backend

Sistema monolítico, com o backend organizado por camadas e responsabilidades bem separadas:

  • Controllers finos, que recebem o pedido HTTP, validam e delegam, sem lógica de negócio.
  • Service layer que concentra toda a lógica de negócio.
  • Factories responsáveis pela criação das entidades.
  • Global Exception Handler que trata todas as exceções num único ponto, devolvendo sempre o mesmo formato de resposta.

Todas as camadas seguem o princípio da responsabilidade única (SRP), com injeção de dependências por interface (DIP): os controllers dependem das interfaces dos serviços, e estes das interfaces dos repositórios e factories. Apliquei OCP sempre que fez sentido (evitando over-engineering em casos simples, como a conversão de enums), e Liskov e ISP na medida do possível.

A camada de serviços está coberta por testes com mocks, garantindo que a lógica de negócio se comporta como esperado de forma isolada.

Segurança

A segurança foi uma prioridade desde o início:

  • JWT (HS256): access token de 15 min, com refresh token rotation (7 dias, tokens opacos de 64 bytes).
  • 2FA TOTP via Otp.NET, compatível com Google Authenticator e Authy.
  • Passwords protegidas com bcrypt (BCrypt.Net-Next).
  • Bloqueio de conta após 5 tentativas falhadas (15 min).
  • Verificação de email com validação de registos MX (filtra domínios inválidos).
  • RBAC com roles User e Admin.
  • Rate limiting por política: 5 req/min no login, 3 req/5 min no forgot-password, 100 req/min global.
  • Security headers em todas as respostas (CSP, HSTS, X-Frame-Options, etc.).
  • Audit log de todas as ações relevantes, com retenção configurável.
  • Background service diário que limpa tokens expirados e logs antigos, e anonimiza ou elimina contas agendadas para remoção.
Frontend
  • Sessão gerida inteiramente no servidor: os tokens nunca chegam ao JavaScript do browser.
  • Cookies httpOnly para access token, refresh token e dados de sessão (nunca localStorage).
  • Todas as mutações passam por Server Actions (Next.js), com o padrão useActionState do React 19.
  • Middleware de proteção de rotas que verifica sessão e role antes de renderizar qualquer página.
  • Camada de API separada por domínio, com um HTTP client base que trata erros, rate limit (429) e injeção do Bearer token.
  • Painel de administração com proteção dupla: middleware mais verificação server-side no layout.
  • Fluxo de 2FA com cookie temporário (pending_2fa_token, 5 min) entre o login e a verificação TOTP, evitando expor o token intermédio ao cliente.
  • Internacionalização (i18n) em três línguas.
Infraestrutura & deploy
  • Alojado num servidor cloud Hetzner (2 vCPU, 4 GB RAM, 40 GB).
  • Imagens guardadas na Cloudinary (limite de 10 MB por ficheiro, sem vídeos), com folga até 25 GB.
  • Dockerfile para o backend e para o frontend, orquestrados por docker-compose.
  • Painel de administração para moderação: visualização de fotos denunciadas, com possibilidade de remover uma foto ou bloquear uma conta.
Privacidade & RGPD

Implementei a conformidade com o RGPD de forma deliberada. Os únicos dados pessoais a que tenho acesso são os emails e as fotos dos utilizadores, estas mantidas de forma privada na Cloudinary. A aplicação inclui exportação de dados e eliminação de conta com anonimização faseada.

O código-fonte é mantido privado, por se tratar de um projeto em produção. Disponível para apresentação em contexto privado.

// live project · U4 · GPU

Live project

MyNook - mynook.social - was born from a simple conviction: social media has lost its way. I wanted to build an alternative where privacy isn't a hidden setting but the starting point, and where you decide who sees what.

MyNook

Live

Full-stack social platform · mynook.social

A social network built from scratch, focused on privacy, security and user experience. Users can post, share photos, manage friendships by category (friends, family) and personalise their profile with unique themes.

MyNook, screenshot 1screenshots/mynook-1.png
MyNook, screenshot 2screenshots/mynook-2.png
MyNook, screenshot 3screenshots/mynook-3.png
  • Post feed with photos (up to 4 per post) and visibility configurable by friendship category
  • Real-time notifications via SignalR (WebSocket)
  • JWT authentication with refresh token rotation and TOTP 2FA (Google Authenticator)
  • Customisable profiles: background themes, fonts, borders and animated elements
  • Admin panel with content moderation and report management
  • GDPR compliance: data export and account deletion with phased anonymisation
  • Interface available in PT, EN, ES and FR
ASP.NET Core 10 C# 13 Next.js 16 React 19 TypeScript PostgreSQL Entity Framework Core SignalR Tailwind CSS Docker Cloudinary Resend + React Email

The idea behind it

When I asked myself what I would change today, the answer was immediate: social media. It started out good, but became increasingly manipulative, to the point of turning into a breeding ground for misinformation and hate. After watching the documentary The Great Hack, which closely reflects what I think, I stopped posting photos in 2019.

That's where mynook.social (My Nook) came from: a social network with privacy at its core. Public profiles show only the name and profile picture; a private profile stays completely hidden, not even appearing in search.

The biggest difference is in friendships. When you add someone, you classify them as family, friend, or both, and that's what decides who sees each post. Instead of privatising photo by photo, the restriction lives in the relationship itself. We all have photos we'd rather family didn't see, and others that only make sense to share with them. On MyNook, that's handled from the start.

Under the hood

Process & design

Before writing a single line of code, I started by drawing domain diagrams to map out the entities I'd need and the relationships between them. Only once the model was complete did I move on to implementation.

Architecture & backend

A monolithic system, with the backend organised in layers with clearly separated responsibilities:

  • Thin controllers that receive the HTTP request, validate and delegate, with no business logic.
  • Service layer that holds all the business logic.
  • Factories responsible for creating entities.
  • Global Exception Handler that handles all exceptions in one place, always returning the same response format.

Every layer follows the single-responsibility principle (SRP), with interface-based dependency injection (DIP): controllers depend on service interfaces, and these on repository and factory interfaces. I applied OCP wherever it made sense (avoiding over-engineering in simple cases, like enum conversion), and Liskov and ISP as far as practical.

The service layer is covered by tests with mocks, ensuring the business logic behaves as expected in isolation.

Security

Security was a priority from the start:

  • JWT (HS256): 15-min access token, with refresh token rotation (7 days, opaque 64-byte tokens).
  • TOTP 2FA via Otp.NET, compatible with Google Authenticator and Authy.
  • Passwords protected with bcrypt (BCrypt.Net-Next).
  • Account lockout after 5 failed attempts (15 min).
  • Email verification with MX record validation (filters invalid domains).
  • RBAC with User and Admin roles.
  • Rate limiting per policy: 5 req/min on login, 3 req/5 min on forgot-password, 100 req/min global.
  • Security headers on every response (CSP, HSTS, X-Frame-Options, etc.).
  • Audit log of all relevant actions, with configurable retention.
  • Daily background service that clears expired tokens and old logs, and anonymises or deletes accounts scheduled for removal.
Frontend
  • Session managed entirely server-side: tokens never reach the browser's JavaScript.
  • httpOnly cookies for access token, refresh token and session data (never localStorage).
  • All mutations go through Server Actions (Next.js), using React 19's useActionState pattern.
  • Route-protection middleware that checks session and role before rendering any page.
  • Domain-separated API layer, with a base HTTP client that handles errors, rate limiting (429) and Bearer token injection.
  • Admin panel with double protection: middleware plus a server-side check in the layout.
  • 2FA flow using a temporary cookie (pending_2fa_token, 5 min) between login and TOTP verification, so the intermediate token is never exposed to the client.
  • Internationalisation (i18n) in three languages.
Infrastructure & deploy
  • Hosted on a Hetzner cloud server (2 vCPU, 4 GB RAM, 40 GB).
  • Images stored on Cloudinary (10 MB per-file limit, no videos), with headroom up to 25 GB.
  • Dockerfile for the backend and the frontend, orchestrated with docker-compose.
  • Admin panel for moderation: viewing reported photos, with the ability to remove a photo or block an account.
Privacy & GDPR

I implemented GDPR compliance deliberately. The only personal data I have access to are users' emails and photos, the latter kept privately on Cloudinary. The app includes data export and account deletion with phased anonymisation.

The source code is kept private, as this is a project in production. Available to present in a private setting.