Visão geral
Anotações rápidas acabam em ficheiros soltos, conversas de chat e aplicações de notas em que não se confia, sem proteção de conta e sem nada que possa ser auditado. O Bridopen é um único lugar, auto-hospedado, para escrever, etiquetar, anexar ficheiros e arquivar notas, pensado para pessoas e equipas pequenas que preferem manter a própria instância a entregar o conteúdo a terceiros. É um projeto pessoal de engenharia, publicado em código aberto para que as decisões por trás dele possam ser inspecionadas, e não apenas afirmadas.
A minha contribuição
Sou o autor único do repositório. Desenhei as camadas e escrevi os handlers HTTP, os serviços, os modelos de domínio e os repositórios, as migrations SQL, os templates HTML e o JavaScript do browser, os middlewares de segurança e a suíte de testes. Também escrevi as configurações de Docker Compose, os alvos do Makefile e a documentação.
O projeto existe para praticar decisões de engenharia de ponta a ponta — fronteiras entre camadas, tratamento de sessão e autorização, segurança de uploads, migrations e versionamento de releases — num código que controlo por completo e que, por isso, posso publicar na íntegra.
Arquitetura
Um monolito modular em camadas. O cmd/http é a composition root: carrega a configuração,
abre o pool do banco de dados, liga o gestor de sessões, o mailer, o storage, o scanner, os repositórios,
os serviços e os handlers, e então regista as rotas. Os módulos de negócio ficam em
internal/handlers (notes, users, legal); a
infraestrutura transversal fica em internal/platform.
cmd/http/routes.go,
cmd/http/security.go e internal/platform/middleware.
Regras das camadas
- Os handlers tratam apenas de HTTP: sessão, leitura do pedido, resposta, renderização e JSON.
- Os serviços concentram casos de uso, validação de fluxo e orquestração.
- Os modelos guardam entidades, estados, constantes e regras puras de domínio.
- Os repositórios isolam SQL, leitura de linhas, transações e mapeamento de persistência.
- Os templates recebem DTOs e page models, nunca regras de negócio.
Stack
- Backend: Go 1.27 com o router padrão do
net/http; sem framework web. - Dados: PostgreSQL via
pgx/v5; esquema gerido pelogolang-migrate. - Sessões:
alexedwards/scscom store em PostgreSQL. - Views:
html/templaterenderizado no servidor, Tailwind CSS e JavaScript puro dividido por responsabilidade. - Entrega: Docker Compose para desenvolvimento e produção, Caddy à frente e tags Git como fonte oficial da versão.
Decisões de engenharia
Três decisões que moldaram o código, cada uma com a alternativa considerada e o que custa.
1. HTTP da biblioteca padrão em vez de um framework web
- Decisão:
- o roteamento, os middlewares e o tratamento de pedidos assentam apenas no
net/http, usando os padrões de método e caminho disponíveis noServeMuxpadrão. - Alternativa:
- Gin, Echo ou Chi, que trariam grupos de rotas, binding e uma pilha de middlewares prontos.
- Motivo:
- superfície de dependências pequena, sem idiomas de framework a infiltrarem-se nos handlers, e fronteiras entre camadas que têm de ser declaradas em vez de herdadas.
- Limitação:
- a cadeia de middlewares, o mapeamento de erros e os helpers de pedido são escritos à mão, portanto há mais código meu para manter e testar do que um framework exigiria.
2. Renderização no servidor em vez de uma SPA
- Decisão:
- as páginas são renderizadas com
html/templatee enriquecidas progressivamente com JavaScript puro; não há framework de cliente nem bundler. - Alternativa:
- uma API JSON com front end em React ou Vue, implantado em separado.
- Motivo:
- um único artefacto para implantar, estado mantido no servidor junto da sessão, e nenhuma esteira de build para manter sincronizada com o backend.
- Limitação:
- interações de cliente mais ricas têm de ser escritas à mão. O Tailwind é hoje carregado de um CDN em tempo de execução, o que obriga a
script-src 'unsafe-inline'na Content-Security-Policy; compilar a folha de estilos no build e apertar essa diretiva continua em aberto.
3. Sessões no banco de dados em vez de tokens sem estado
- Decisão:
- as sessões ficam em PostgreSQL através do
scscom o store pgx, e uma tabela separada regista as sessões ativas do utilizador. - Alternativa:
- um token assinado e sem estado (JWT) em cookie, validado sem tocar no banco de dados.
- Motivo:
- o utilizador autenticado pode listar as sessões ativas e revogá-las na página da conta, e terminar sessão invalida-a mesmo no servidor, em vez de esperar que um token expire.
- Limitação:
- todos os pedidos autenticados fazem uma consulta de sessão ao banco de dados, colocando-o no caminho crítico de todo o tráfego autenticado.
Qualidade e segurança
Tudo o que está abaixo existe no repositório público e pode ser conferido no código. Nada aqui é uma afirmação sobre quão segura é uma instância em produção — isso depende de como for configurada e operada.
Testes e CI
- 26 ficheiros de teste cobrindo handlers, serviços, repositórios, modelos, DTOs e os pacotes de plataforma.
- Escritos com
testify; os testes de repositório usampgxmock/v4, exercitando SQL e leitura de linhas sem um banco real. - O GitHub Actions executa
go vet ./...ego test ./...em cada push paramaine em cada pull request. - Reproduza localmente com
go test -count=1 ./....
Controlos de acesso
- Palavras-passe com hash bcrypt.
- Verificação em duas etapas por TOTP (6 dígitos, período de 30 segundos, janela de validação de um período) com registo por QR e códigos de recuperação; o segredo partilhado fica cifrado em repouso.
- Tokens CSRF via
gorilla/csrf, cookies com flags de segurança e captcha nos formulários de autenticação. - Limite de pedidos por IP e cabeçalhos de segurança —
Content-Security-Policy,X-Frame-Options: DENY,X-Content-Type-Options,Referrer-Policy,Permissions-Policy.
Dados e operação
- Sete migrations SQL versionadas em
db/migrations, aplicadas comgolang-migrate. - Eventos de negócio registados num esquema de auditoria dedicado.
- Anexos gravados num volume fora do banco de dados; quando há um endereço ClamAV configurado, os uploads são verificados antes de serem persistidos.
- Exportação e exclusão de conta disponíveis ao utilizador autenticado na página de perfil.
- Releases marcadas com
vMAJOR.MINOR.PATCH; a build em execução informa versão, commit e data de compilação emGET /version.
Resultados e limitações
O que existe hoje é uma aplicação a funcionar: registo com confirmação por e-mail, entrada com verificação em
duas etapas opcional, recuperação de palavra-passe, notas com etiquetas, anexos, arquivo e lixeira com
exclusão reversível, tema claro e escuro, exportação e exclusão de conta e páginas legais públicas. Corre a
partir de um único docker compose, com as migrations aplicadas por um alvo do Make.
Limitações declaradas
- Nenhum benchmark de performance foi executado. Não tenho números medidos de latência, débito ou disponibilidade para este projeto, por isso nenhum é alegado.
- Não há instância oficial hospedada. Não existe demo público para ligar. Quem implanta o Bridopen é responsável pelo serviço, pelos dados dos utilizadores e pelos textos legais das páginas públicas.
-
A verificação de anexos é opcional. Quando
BRIDOPEN_CLAMAV_ADDRnão está configurado, a aplicação recorre a um scanner que não faz nada e os uploads são guardados sem verificação. - As páginas legais são modelos genéricos. Precisam de revisão jurídica antes de qualquer implantação real.
- A Content-Security-Policy não é tão restritiva quanto podia. Carregar o Tailwind de um CDN em tempo de execução obriga a permitir scripts inline.
- Não há dados de uso real. Este é um projeto pessoal; não alego utilizadores, adoção ou tráfego em produção.
Explorar
O repositório é público e inclui o README com instruções de instalação, a referência das variáveis de
ambiente, os alvos do Make e a documentação de apoio em docs/.
O README do repositório está em português. Não existe instância de demonstração hospedada, por opção.