Skip to content

Por que Staging? — Motivações, Decisões e Trade-offs ​

Documento de decisão arquitetural (ADR) do ambiente de staging. Para setup técnico, ver STAGING_SETUP.md. Para valores de referência, ver STAGING_REFERENCE.md.

Última atualização: 2026-04-05


1. Motivação — Por que precisamos de staging ​

O staging não foi criado por "boas práticas" genéricas. Nasceu de problemas reais que bloqueavam o desenvolvimento seguro da plataforma:

1.1 Impossibilidade de testar permissões em produção ​

O sistema de permissões OLP tem 3 camadas interdependentes:

  • usuarios_escola_permissoes — permissões CRUD por escola
  • Feature Flags — flags globais que habilitam/desabilitam áreas inteiras
  • Canary Releases — permissões temporárias liberadas para grupos de teste

Bugs nesse ecossistema causam dois cenários igualmente graves:

  1. Perda de acesso: coordenador perde permissão de agenda → escola não consegue operar
  2. Acesso indevido: gestor escola consegue alterar permissões canary-protegidas → violação do princípio de menor privilégio

Testar essas interações com escolas reais é inaceitável. Um erro no CRUD de permissões afeta diretamente a operação diária da escola.

Exemplo real: A auditoria de abril/2026 detectou que gestao-usuarios-escola (endpoint da escola) não protegia permissões canary durante update e update_permissoes. Um gestor escola poderia, ao salvar a matriz de permissões, deletar silenciosamente permissões canary-protegidas. Esse bug foi detectado em staging antes de afetar qualquer escola real.

O fluxo de autenticação OLP é não-trivial:

Frontend (olp.digital)
  → Cloudflare Worker (gateway.olp.digital)
    → Supabase Edge Function (*.supabase.co)
      → JWT assinado com OLP_JWT_SECRET
        → Cookie HttpOnly olp_auth (Domain=.olp.digital)

Esse fluxo envolve:

  • 3 origens diferentes (frontend, gateway, Supabase)
  • Reescrita de cookies no Worker (Set-Cookie com Domain correto)
  • CORS com credentials (nunca Access-Control-Allow-Origin: *)
  • Geolocalização injetada via headers Cloudflare

É impossível validar esse fluxo completo com mocks ou testes unitários. A única forma de garantir que cookies são setados, propagados e validados corretamente é um ambiente end-to-end real com as mesmas camadas de produção.

1.3 Pipeline de CI/CD bloqueado ​

Antes do staging, o pipeline de CI era limitado a:

lint → build → ✅ (fim)

Faltavam os estágios críticos:

  • Testes de contrato: validar que as 52+ Edge Functions respondem corretamente (CORS, auth, payloads)
  • Testes de segurança: validar isolamento cross-escola (IDOR), que um gestor da Escola A não acessa dados da Escola B
  • Testes E2E: validar fluxos completos (login → navegação → ação → logout) via Playwright

Sem um ambiente real, esses testes simplesmente não tinham onde rodar.

1.4 Isolamento de secrets ​

A plataforma utiliza secrets sensíveis que impactam diretamente segurança e integridade:

SecretImpacto se comprometido
OLP_JWT_SECRETForjar sessões de qualquer usuário
OLP_PEPPER_SECRETComprometer hashes de senha
CRON_SECRETExecutar jobs administrativos
E2E_TEST_PASSWORDSenha dos usuários de teste para login direto sem OTP (só existe em staging)

Testar com os secrets de produção significaria que qualquer vazamento no CI (logs, artefatos) comprometeria o ambiente real. Staging usa secrets exclusivos e independentes — um vazamento em staging não afeta produção.

1.5 Validação de migrations antes de produção ​

Migrations SQL críticas — como o seed de permissões para os 20 usuários órfãos detectados na auditoria — precisam ser validadas antes de aplicar em produção. Em staging, uma migration mal escrita causa rollback local; em produção, causa downtime real.


2. Por que Cloudflare (e não Vercel, Netlify, etc.) ​

A escolha de Cloudflare não foi preferência — foi necessidade técnica de paridade com produção.

2.1 Paridade de infraestrutura ​

ComponenteProduçãoStagingPor que importa
Gatewaygateway.olp.digital (Worker)gateway-staging.olp.digital (Worker)Cookie rewrite + CORS + geo headers idênticos
FrontendLovable hosting (olp.digital)Cloudflare Pages (staging.olp.digital)Build Vite idêntico
DNSCloudflare DNSCloudflare DNSSubdomínios sem mexer no registrador (GoDaddy)

Se usássemos Vercel ou Netlify para o frontend de staging, o fluxo de cookies não funcionaria — o Worker precisa estar no mesmo domínio (.olp.digital) para reescrever cookies com Domain=.olp.digital.

2.2 DNS já no Cloudflare ​

O domínio olp.digital já tem DNS gerenciado pela Cloudflare. Criar subdomínios (staging.olp.digital, gateway-staging.olp.digital) é uma operação de 30 segundos no painel — sem tocar no registrador externo (GoDaddy), sem propagação DNS demorada.

2.3 Cloudflare Pages — simplicidade ​

Cloudflare Pages integra-se ao mesmo painel onde o Worker já existe:

  • Build: bun install && bun run build → output dist/
  • Deploy automático via GitHub (branch staging)
  • Preview deploys por branch
  • Zero configuração de servidor

2.4 Custo zero ​

RecursoFree TierUso estimado staging
Workers100.000 req/dia< 5.000 req/dia
Pages500 builds/mês< 50 builds/mês
DNSIlimitado2 subdomínios

O staging inteiro opera no free tier da Cloudflare sem custo adicional.

2.5 Alternativas descartadas ​

AlternativaPor que descartada
VercelNão resolve cookies cross-origin (sem Worker no mesmo domínio)
NetlifyMesmo problema de cookies; sem Worker nativo
Lovable hostingNão suporta múltiplos ambientes (produção e staging simultâneos)
Docker localNão replica Cloudflare Worker; sem CI integrado
Supabase local (supabase start)Não replica Edge Functions em Deno Deploy; sem CORS real

3. Trade-offs conscientes ​

Cada decisão abaixo foi tomada com plena consciência dos custos e benefícios:

3.1 Frontend em Cloudflare Pages vs Lovable hosting ​

Cloudflare PagesLovable hosting
VantagemBranch deploys, CI integrado, mesmo domínio do WorkerZero config, preview automático
DesvantagemBuild manual (GitHub Actions)Apenas 1 ambiente por projeto
Decisão✅ Pages para staging✅ Lovable para produção

Justificativa: Lovable hosting não suporta múltiplos ambientes. Para manter produção em olp.digital via Lovable e ter staging em staging.olp.digital, Cloudflare Pages é a única opção que mantém paridade de domínio.

3.2 Supabase em projeto separado ​

Projeto separadoMesmo projeto (schemas diferentes)
VantagemIsolamento total de secrets, RLS, dadosSem custo extra, schema unificado
DesvantagemGestão de 2 projetos, deploy duplicado de Edge FunctionsRisco de cross-contamination de dados e secrets
Decisão✅ Projeto separado

Justificativa: O OLP_JWT_SECRET deve ser exclusivo por ambiente. Se staging e produção compartilharem o mesmo projeto, um JWT gerado em staging poderia ser válido em produção — violação crítica de segurança.

3.3 Integrações externas — Wasender e MercadoPago ​

IntegraçãoStatusDecisão
Wasender (WhatsApp)✅ ConfiguradoCredenciais de staging independentes — OTP WhatsApp funcional
MercadoPago❌ Não configuradoPagamentos não são alvo dos testes CI

Justificativa Wasender: Configurado com credenciais de staging para que o fluxo de OTP via WhatsApp funcione quando necessário (ex: testes manuais do mural). O CI usa E2E_TEST_PASSWORD para login direto sem OTP.

Justificativa MercadoPago: Pagamentos não são alvo dos testes automatizados atuais. Quando necessário, será configurado com credenciais de sandbox.

3.4 Worker com código duplicado (não versionado) ​

Worker no repositórioWorker no painel Cloudflare
VantagemVersionado, deploy automatizadoEdição rápida, sem build pipeline
DesvantagemPipeline adicional, complexidadeDuplicação manual entre prod e staging
Decisão❌ Painel Cloudflare (dívida técnica aceita)

Justificativa: O Worker tem < 200 linhas e muda raramente (última alteração significativa: fev/2026). O custo de montar um pipeline de deploy para Worker não se justifica pelo ritmo atual de mudanças. Quando o Worker crescer em complexidade, migraremos para Wrangler + GitHub Actions.

3.5 Dados de seed com UUIDs fixos ​

UUIDs fixosUUIDs gerados
VantagemReferência cruzada direta nos testesIsolamento entre runs
DesvantagemColisão se aplicar seed 2x sem limparTestes precisam de lookup
Decisão✅ UUIDs fixos

Justificativa: Staging é ambiente isolado — UUIDs fixos simplificam enormemente os testes (assertions diretas, sem queries prévias). O seed é idempotente (usa ON CONFLICT ou verifica existência antes de inserir).

3.6 Login direto para testes (sem OTP WhatsApp) ​

Login direto via secretWhatsApp sandbox
VantagemZero custo, zero latência, CI-friendlyTeste real do fluxo WhatsApp
DesvantagemNão testa envio WhatsApp realCusto, rate limits, latência
Decisão✅ Login direto via secret

Justificativa: O E2E_TEST_PASSWORD é a senha real atribuída aos usuários de teste do staging. A Edge Function e2e-login valida o header X-E2E-Key contra esse secret — é uma autenticação legítima, apenas sem o passo de OTP WhatsApp (que é funcional em staging via Wasender, mas desnecessário para CI). Em produção, o secret não existe, tornando o endpoint inacessível. Zero risco de escalação.

3.7 Portal (mural) sem seed data ​

Configurar portal em stagingNão configurar
VantagemTestes E2E do portal aluno/responsávelMenos complexidade
DesvantagemMais seed data (mural, liberações)Fluxo portal não testado em CI
Decisão❌ Dívida técnica aceita (P3)

Justificativa: O portal do aluno tem fluxo de autenticação separado (cookie olp_mural) e dados específicos (snapshots, liberações). O JWT do portal usa o mesmo OLP_JWT_SECRET (unificado por exigência do PostgREST/RLS — ver AUTHENTICATION.md), então não há secret pendente. O que falta é seed data (mural_dados_publicados, mural_liberacoes, escola_mural_config com slugs). Será implementado quando os testes E2E do portal forem priorizados.


4. O que tivemos que fazer — Setup completo ​

Cada passo do setup teve uma razão técnica específica. Para comandos detalhados, ver STAGING_SETUP.md.

4.1 Criar projeto Supabase separado ​

Por quê: Isolamento de OLP_JWT_SECRET. Se staging e produção compartilharem o mesmo secret, um token de staging poderia autenticar em produção.

O que fizemos: Novo projeto no Supabase Dashboard → região South America (São Paulo) para latência consistente com produção.

Resultado: Project Ref nrgcajnhpjmwilfcmqyb — completamente independente de produção (mjvuzsizjlcalyfmbquy).

4.2 Clonar schema + sanitizar dados ​

Por quê: O schema precisa ser idêntico à produção (tabelas, RLS policies, triggers, enums). Mas dados reais violam LGPD — nunca copiar dados de alunos, telefones ou CPFs.

O que fizemos:

  1. pg_dump --schema-only de produção
  2. Aplicar no staging via psql
  3. Seed com dados fictícios (7 usuários de teste, 3 escolas)

4.3 Regenerar todos os secrets ​

Por quê: Cada ambiente deve ter secrets exclusivos. Um secret compartilhado entre ambientes anula o isolamento.

O que fizemos: Gerar novos valores para todos os 14 secrets:

SecretMétodo de geração
OLP_JWT_SECRETopenssl rand -base64 64
OLP_PEPPER_SECRETopenssl rand -base64 32
CRON_SECRETopenssl rand -hex 32
E2E_TEST_PASSWORDopenssl rand -hex 16
SUPABASE_SERVICE_ROLE_KEYGerado pelo Supabase (automático)

4.4 Replicar Storage Buckets ​

Por quê: Edge Functions que fazem upload (banners, thumbnails) falham se o bucket não existir.

O que fizemos: Criar os 4 buckets públicos no staging:

  • banners-login
  • curso-thumbnails
  • tutoriais-thumbnails
  • mural-imagens

4.5 Deployar 52+ Edge Functions ​

Por quê: Testes de contrato validam cada Edge Function individualmente. Todas precisam estar deployadas.

O que fizemos: supabase functions deploy --project-ref nrgcajnhpjmwilfcmqyb para cada função. No CI, automatizado via GitHub Actions no estágio deploy-staging.

4.6 Clonar Cloudflare Worker Gateway ​

Por quê: O frontend staging precisa de um gateway no mesmo domínio (.olp.digital) para que cookies Domain=.olp.digital funcionem cross-origin.

O que fizemos:

  1. Criar Worker olp-staging-gateway no painel Cloudflare
  2. Copiar código do Worker de produção
  3. Alterar COOKIE_DOMAIN de .olp.digital para .staging.olp.digital (subdomínio isolado para evitar vazamento de cookies para produção)
  4. Alterar SUPABASE_URL para apontar ao projeto staging
  5. Rota: gateway-staging.olp.digital/*

4.7 Atualizar CORS ​

Por quê: Edge Functions validam Origin contra uma allowlist. Staging tem origens novas que precisam ser permitidas.

O que fizemos: Adicionar em cors-helpers.ts:

typescript
'https://staging.olp.digital',
'https://gateway-staging.olp.digital',

As regras dinâmicas (*.olp.digital) já cobriam, mas origens explícitas garantem clareza.

4.8 Configurar Cloudflare Pages ​

Por quê: Frontend staging precisa de hosting com deploy automatizado e variáveis de ambiente do staging.

O que fizemos:

  1. Criar projeto Pages conectado ao repo GitHub
  2. Branch de produção: staging (ou main com variáveis de staging)
  3. Build command: bun install && bun run build
  4. Output directory: dist
  5. Variáveis de ambiente:
VariávelValor
VITE_SUPABASE_URLhttps://nrgcajnhpjmwilfcmqyb.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEYAnon key do staging
VITE_SUPABASE_PROJECT_IDnrgcajnhpjmwilfcmqyb
VITE_USE_WORKERtrue
VITE_WORKER_URLhttps://gateway-staging.olp.digital

4.9 Pipeline CI/CD — 5 estágios ​

Por quê: Garantir que mudanças só chegam a produção após validação completa em staging.

O que fizemos: GitHub Actions com 5 estágios sequenciais:

┌─────────────────┐
│ 1. lint-and-build│ ← Validação de código (ESLint + TypeScript + Vite build)
└────────┬────────┘
         │ ✅
┌────────▼────────┐
│ 2. deploy-staging│ ← supabase db push + functions deploy (Staging Ref)
└────────┬────────┘
         │ ✅
┌────────▼────────┐
│ 3. contract-tests│ ← Vitest: smoke test sequencial de cada Edge Function
└────────┬────────┘
         │ ✅
┌────────▼────────┐
│ 4. security-tests│ ← Vitest: IDOR cross-escola, isolamento de dados
└────────┬────────┘
         │ ✅
┌────────▼────────┐
│ 5. e2e           │ ← Playwright: fluxos completos (login → ação → logout)
└─────────────────┘

Cada estágio só executa se o anterior passou. Um teste de segurança falhando bloqueia o E2E e, consequentemente, o merge.

4.10 Seed de usuários de teste ​

Por quê: Testes precisam de usuários com papéis e permissões pré-definidos para validar RBAC.

O que fizemos: 7 usuários Dev cobrindo 11 papéis:

UsuárioCódigo (CPF/CNPJ)Papel(is)Escola
Dev Admin42970698064administrador— (global)
Dev Especialista63100416066especialista— (global)
Dev Coordenador99407464075coordenadorMonteiro Lobato
Dev Diretor61626069026diretorMonteiro Lobato
Dev Escola77457094000157 (CNPJ)escolaMonteiro Lobato
Dev Testes Multi Role40750810017admin, espec, coord×3, dir, escola, pedag, profTodas as 3 escolas
Dev Coordenador da Nova Era47691226080coordenadorNova Era

Todos compartilham o secret E2E_TEST_PASSWORD para login via e2e-login. Para detalhes completos dos vínculos, ver seed_test_users.sql.


5. Resultados alcançados ​

O staging transformou o pipeline de CI de superficial para bloqueador de bugs reais:

5.1 Bugs detectados proativamente ​

BugSeveridadeDetectado emImpacto evitado
20 usuários sem permissões (órfãos)CríticoAuditoria stagingCoordenadores sem acesso à sidebar
gestao-usuarios-escola não protege canary no updateCríticoAuditoria stagingPermissões canary deletadas silenciosamente
gestao-usuarios-escola não protege canary no update_permissoesCríticoAuditoria stagingPermissões canary deletadas em bulk
features_em_manutencao ausente no list da escolaMédioAuditoria stagingFrontend escola sem badges de manutenção
password-history-helper.test.ts regex desatualizadoBaixoCI stagingTeste falhando silenciosamente

5.2 Cobertura de testes ​

EstágioCenáriosO que valida
Contract~52 funçõesCORS, auth, payloads, status codes
Security~15 cenáriosIDOR, cross-escola, role escalation
E2E~10 fluxosLogin, navegação, CRUD, logout
Total~77 cenários—

5.3 Tempo de feedback ​

Antes (sem staging)Depois (com staging)
Bug detectado em produção pelo clienteBug detectado no CI antes do merge
Rollback manual + comunicação com escolaFix no PR, re-run do CI
~2h de impacto mínimo~0 impacto em produção

6. Diagrama de arquitetura — Staging vs Produção ​

                    ┌─────────────────────────────────────────────────────────┐
                    │                    PRODUÇÃO                             │
                    │                                                         │
  Usuário Real      │  ┌──────────────┐    ┌──────────────────┐              │
  ───────────────►  │  │  olp.digital  │───►│ gateway.olp.digital│             │
                    │  │ (Lovable Host)│    │ (CF Worker)       │             │
                    │  └──────────────┘    └────────┬─────────┘              │
                    │                               │                        │
                    │                    ┌──────────▼──────────┐              │
                    │                    │ mjvuzsizjlcalyfmbquy │             │
                    │                    │ (Supabase Produção)   │             │
                    │                    │ OLP_JWT_SECRET = A    │             │
                    │                    └─────────────────────┘              │
                    └─────────────────────────────────────────────────────────┘

                    ┌─────────────────────────────────────────────────────────┐
                    │                    STAGING                              │
                    │                                                         │
  CI / Dev          │  ┌────────────────────┐  ┌─────────────────────────┐   │
  ───────────────►  │  │ staging.olp.digital │──►│gateway-staging.olp.digital│ │
                    │  │ (CF Pages)          │  │(CF Worker)               │  │
                    │  └────────────────────┘  └───────────┬──────────────┘  │
                    │                                      │                 │
                    │                           ┌──────────▼──────────┐      │
                    │                           │ nrgcajnhpjmwilfcmqyb │     │
                    │                           │ (Supabase Staging)    │     │
                    │                           │ OLP_JWT_SECRET = B    │     │
                    │                           └─────────────────────┘      │
                    └─────────────────────────────────────────────────────────┘

  Pontos-chave:
  ┌────────────────────────────────────────────────────────────────────────┐
  │ • Secrets A ≠ B — JWT de staging NUNCA funciona em produção           │
  │ • Mesmo domínio (.olp.digital) — cookies funcionam cross-origin      │
  │ • Worker staging = clone do Worker produção (apenas URLs diferentes)  │
  │ • CF Pages staging ≠ Lovable hosting produção (necessidade técnica)   │
  │ • E2E_TEST_PASSWORD só existe em staging — login direto impossível em prod │
  └────────────────────────────────────────────────────────────────────────┘

7. Referências ​