07 — Segurança e Observabilidade
Documentação do boilerplate — o que já está no monorepo vs o que o fork configura em produção. Detalhe de deploy e health checks: 05-deploy-infrastructure.
Segurança
Secrets
| Onde | O quê |
|---|---|
| Local | .env por app (apps/*/.env) + .env na raiz (SSH, DB prod) — templates em .env.example |
| Produção | Painel Dokploy por Application — ver cloud/dokploy/environments/*.production.env.example |
| Git | Nunca commitar .env ou valores reais — só .env.example com placeholders |
Boilerplate já exige (service): JWT_SECRET, HASURA_GRAPHQL_ADMIN_SECRET, POSTGRES_URL, STRIPE_*, S3_*, GOOGLE_*, BACKOFFICE_ADMIN_EMAILS.
Rotação (definir no fork):
| Secret | Frequência sugerida | Como |
|---|---|---|
JWT_SECRET / Hasura JWT | 90–180 dias ou pós-incidente | Gerar novo valor; atualizar service + Hasura (HASURA_GRAPHQL_JWT_SECRET); invalida sessões ativas |
HASURA_GRAPHQL_ADMIN_SECRET | 90 dias | Dokploy → hasura + service; smoke test GraphQL |
| Stripe webhook secret | Quando rotacionar no Stripe Dashboard | Atualizar STRIPE_WEBHOOK_SECRET no service |
| SSH / DB | Anual ou pós-exposição | DO + .env raiz |
Acesso prod: SSH por chave default (~/.ssh); scripts carregam .env da raiz — confirmar com usuário antes de ações destrutivas (TRUNCATE, DROP, restart em massa). Ver .cursor/rules/server-ssh.mdc.
Autenticação
Modelo JWT Bearer emitido pelo apps/service; clients (app, backoffice) enviam Authorization: Bearer <access_token> em REST e GraphQL.
| Camada | Mecanismo |
|---|---|
| REST (service) | JwtAuthGuard + @Public() em rotas abertas (/auth/*, webhooks) |
| GraphQL (Hasura) | Mesmo JWT; claims https://hasura.io/jwt/claims com x-hasura-user-id, x-hasura-role, x-hasura-allowed-roles |
| Refresh | POST /auth/refresh-token; access token default JWT_EXPIRES_IN=7d |
| OAuth | Google (GOOGLE_CLIENT_ID / secret); callback no service |
Roles Hasura (metadata):
| Role | Uso |
|---|---|
user | Cliente logado — row-level filters (user_id, company_id) |
anonymous | Leitura pública limitada (ex.: plans) |
| Admin secret | Service e migrations — nunca expor ao browser |
Backoffice (plataforma): não é role Hasura separada — lista BACKOFFICE_ADMIN_EMAILS no service; login backoffice exige e-mail na lista (assertPlatformAdmin). Rotas admin REST usam PlatformAdminGuard.
Princípio do menor privilégio: regras de negócio sensíveis no service; Hasura só expõe o mínimo com permissions por tabela. Operações admin (users, companies globais) só via service + guard.
Contrato completo: 04-api-integrations · spec 002-auth-module.
Dados
PII no boilerplate: e-mail, nome, avatar, dados de billing (Stripe), membros de empresa, uploads em storage. No fork, documentar campos adicionais (ex.: domínios analisados) e base legal LGPD/GDPR.
| Prática | Detalhe |
|---|---|
| Isolamento multi-tenant | Row-level security Hasura por company_id / user_id |
| Logs de aplicação | Tabela error_logs — não incluir senhas, tokens completos, API keys |
ErrorLoggingInterceptor | Sanitiza contexto de request; códigos categorizados (AUTH_001–AUTH_006, etc.) |
| Exclusão de conta | Fluxo user-facing em spec 002 — garantir cascade ou anonimização conforme política do fork |
| Backups | Postgres em volume Docker — definir frequência e teste de restore no fork (05-deploy runbooks) |
Dependências
pnpm audit # revisão periódica (mensal sugerida)
pnpm lint && pnpm build # gate antes de merge em main
- Priorizar patches de NestJS, React, Hasura engine e dependências com CVE alto/critical
- Atualizar deps major com plano: lint + build + smoke manual nos fluxos auth/billing afetados
- O boilerplate não roda suite automatizada no CI — aceite manual após bump relevante (06-testing-quality)
Observabilidade
Estado shipped vs opcional / fork:
| Sinal | Tool (boilerplate) | Retenção | Notas |
|---|---|---|---|
| Logs app/service | pnpm server:docker -- logs --tail N framework-<app> | Docker log driver (default) | Sem agregador central incluso |
| Errors (service) | ErrorLoggingService → Postgres error_logs | Indefinida — definir política de purge no fork | Sempre ativo — DB + stdout (docker logs) |
| Uptime / health | Endpoints HTTP + pnpm dokploy:health | Manual / externo | Configurar UptimeRobot ou similar no fork |
| DB / infra | docker stats, queries admin | — | Postgres :5439, PgBouncer :6432 prod |
| APM / traces | Não incluso | — | Adicionar no fork se necessário (Datadog, etc.) |
| Produto (funil) | GA4, GTM, Hotjar | Conforme vendor | Ver 06-tracking-attribution |
Health checks (produção)
| App | Path | Comando local |
|---|---|---|
| service | /health | curl -sf localhost:3002/health |
| website | /healthz | curl -sf localhost:3000/healthz |
| hasura | /healthz | curl -sf localhost:8080/healthz |
| app / backoffice | / | resposta 200 na raiz |
Verificação remota após deploy:
pnpm dokploy:health
./scripts/prod/server-ssh.sh 'docker ps --format "table {{.Names}}\t{{.Status}}"'
Fluxo de erro (service)
Exception → ErrorLoggingInterceptor → ErrorLoggingService
├─ 1. INSERT error_logs (Postgres via Hasura admin)
└─ 2. Logger NestJS (stdout → docker logs)
Consultar erros recentes (prod, não interativo):
pnpm db:query -- "SELECT error_code, severity, error_message, created_at FROM error_logs ORDER BY created_at DESC LIMIT 20"
Usuário autenticado vê apenas próprios logs via Hasura (user_id = X-Hasura-User-Id).
Alertas (produção)
Boilerplate não inclui pager automático — configurar monitor externo no fork. Condições mínimas sugeridas:
| Condição | Severidade | Ação |
|---|---|---|
service /health fail 2× consecutivas | P1 | Logs: pnpm server:docker -- logs --tail 100 framework-service; checar postgres/redis; rollback Dokploy se deploy recente |
Hasura /healthz fail | P1 | Logs framework-hasura; verificar PgBouncer e postgres |
| app ou backoffice down | P1 | Logs do container; verificar build/env vars |
| website down | P2 | Marketing offline — prioridade abaixo de API |
| Disk > 85% no droplet | P2 | df -h via SSH; limpar logs/images Docker; expandir volume se recorrente |
Spike error_logs severity ≥ high | P2 | Query acima; correlacionar com deploy |
| Stripe webhook failures | P2 | Stripe Dashboard → Webhooks; logs service em /webhooks/stripe |
| Redis unreachable | P2 | Impacto em cache/filas; restart framework-redis após confirmar |
Canal interno: definir no fork (Slack, e-mail, WhatsApp founder).
Incident response (leve)
Playbook mínimo para equipe pequena:
- Detectar — alerta externo, cliente, ou
pnpm dokploy:healthfalhando - Triagem (5 min) — qual app, desde quando, deploy recente? (
docker ps, health URLs) - Comunicar — status interno; status page pública se configurada no fork
- Mitigar — restart container, rollback Dokploy para imagem anterior, revert commit + redeploy — confirmar se destrutivo ou afeta dados
- Resolver — fix forward ou rollback; validar health + login smoke test
- Post-mortem breve — causa, timeline, ação preventiva; entrada user-facing em 08-changelog se impacto visível
Nunca sem confirmação explícita: TRUNCATE, DROP, delete de volumes, restart em massa de postgres.
Acesso remoto
Scripts carregam .env da raiz (load-root-env.sh → ssh-env.sh → SSH_TARGET):
./scripts/prod/server-ssh.sh 'comando'
pnpm server:docker -- ps
pnpm server:docker -- logs --tail 100 framework-service
pnpm db:query -- "SELECT 1"
pnpm db:psql # interativo prod via SSH
Ver .cursor/rules/server-ssh.mdc e scripts/README.md.
Checklist pré-público (fork)
Itens operacionais antes de abrir tráfego real — complementa PLAYBOOK Fase 5–6:
- Secrets prod preenchidos no Dokploy (sem placeholders
REPLACE_*) -
pnpm dokploy:healthverde nos 5 endpoints - Processo de revisão periódica de
error_logs(query acima ou backoffice) - Monitor de uptime apontando para
/healthe/healthz - Backup Postgres documentado e testado uma vez
- Política de retenção/purge de
error_logse logs Docker -
BACKOFFICE_ADMIN_EMAILSrestrito a contas reais da equipe