Implantação no Data Clerk
Este aplicativo é executado no Data Clerk, um pequeno cluster Kubernetes gerenciado por um operador (um humano, contato em PROJECT.md). O proprietário deste repositório geralmente não é uma pessoa de infraestrutura. Ele nunca deve precisar tocar em servidores, Kubernetes ou console de nuvem: ele faz push para main, e o cluster faz o resto. Seu trabalho é manter as coisas assim: trabalhe dentro do repositório, siga o contrato abaixo e, quando algo realmente precisar do lado do cluster, escreva uma solicitação clara para o operador, em vez de improvisar.
Explique as coisas ao usuário em palavras simples ("seu site será atualizado em cerca de cinco minutos"), não em termos de Kubernetes.
Comece aqui
- Leia
deploy-data-clerk/PROJECT.md. Ele contém os valores deste projeto: URL pública, pasta de implantação, nome da imagem, porta, comando de migração, limites, quem é o operador. Tudo abaixo que diz "de PROJECT.md" significa esse arquivo. Se o nome da pasta em PROJECT.md for diferente dedeploy-data-clerk/(projetos mais antigos usamdeploy/), use o que PROJECT.md disser. - Sem
PROJECT.md, ou ele ainda tem valores__PLACEHOLDER__? O projeto ainda não foi integrado: sigareferences/new-project.md. - Escrevendo ou alterando um Dockerfile, decidindo como estruturar o aplicativo, ou a stack não é Node? Leia
references/stacks.md(qualquer linguagem, full-stack vs frontend separado + API, FastAPI, Go). - Antes da primeira tarefa de segredos em uma máquina, verifique se as ferramentas funcionam:
sops --versione um teste de descriptografia (sops decrypt <pasta de implantação>/secrets.env > /dev/null). Se algum falhar, sigareferences/setup-tools.md.
Como uma alteração chega ao site ao vivo
| O que mudou | O que acontece | Ao vivo depois de |
|---|---|---|
Código (push para main) | GitHub Actions constrói uma imagem Docker e a envia; o cluster percebe a nova tag de imagem e a distribui | ~3–7 min (tempo de construção + ~1 min) |
secrets.env (push para main) | O cluster relê a pasta de implantação a cada 5 min, descriptografa, atualiza o env do aplicativo e reinicia o aplicativo | ~5 min |
Um novo arquivo em migrations/ | Executa automaticamente antes da nova versão iniciar | com a implantação de código |
Qualquer coisa fora de main | Nada. Branches e PRs não são implantados | — |
A distribuição é cuidadosa: a nova versão inicia ao lado da antiga, as migrações são executadas primeiro, e o tráfego só é movido quando a nova versão responde à verificação de saúde. Se a construção, uma migração ou a verificação de saúde falhar, a versão antiga continua servindo e o site permanece no ar. Uma implantação falha significa "a alteração não está ao vivo", não "o site está fora do ar".
Para verificar uma implantação: gh run list --workflow "Deploy to cluster" --limit 3 (ou a aba Actions no GitHub), depois abra a URL pública. Para reimplantar sem alteração de código: gh workflow run "Deploy to cluster", que constrói uma nova tag, então o aplicativo reinicia com um ambiente fresco.
O contrato (o que mantém as implantações funcionando)
Cada regra existe porque quebrá-la falha silenciosamente ou perde dados. Siga-as e avise o usuário se uma solicitação quebrar alguma.
- Escute em
$PORT(a porta em PROJECT.md, definida pelo cluster), em0.0.0.0, nãolocalhost. - A imagem é construída a partir de
<pasta de implantação>/Dockerfileparalinux/amd64e é executada como um usuário não root. Sem root, sem portas abaixo de 1024, sem capacidades extras do Linux. - As tags de imagem são
YYYYMMDDHHMMSS-<shortsha>. O cluster implanta o número mais alto e ignora silenciosamente qualquer tag que não esteja neste formato. Nunca altere a etapa de tag em.github/workflows/deploy-cluster.yml. - O disco do contêiner é limpo em toda implantação e reinicialização. Nunca mantenha uploads de usuários, arquivos SQLite ou qualquer coisa preciosa no disco local. Coloque os dados no banco de dados; para arquivos (fotos, vídeo), peça ao operador armazenamento de objetos.
- Verificação de saúde: o cluster testa o caminho de saúde em PROJECT.md (
/healthzrecomendado). Faça-o retornar 200 rapidamente, sem chamada de banco de dados ou renderização de página, ou um banco de dados lento faz o cluster pensar que o aplicativo está morto. DATABASE_URLvem do cluster. Nunca coloque emsecrets.env(isso substituiria o real) e nunca defina no momento da construção da imagem.- Tempo de execução versus tempo de construção.
secrets.envatinge o aplicativo apenas em tempo de execução. A imagem é construída no GitHub Actions sem ele. Qualquer coisa embutida no frontend em tempo de construção (VITE_*,NEXT_PUBLIC_*,REACT_APP_*) não pode vir desecrets.env: leia esses valores no servidor em tempo de execução e passe-os para a página, ou (se não forem secretos) comprometa-os no código. - Segredos que o aplicativo gera para si mesmo quando ausentes (segredo de autenticação/sessão, sais de criptografia, chaves de push): defina-os em
secrets.env. Caso contrário, toda reinicialização cria novos, usuários são desconectados, assinaturas de push quebram, e assim por diante. - As migrações devem ser seguras para a versão antiga. Por cerca de um minuto, ambas as versões são executadas contra o novo esquema. Adicione colunas e tabelas livremente. Renomeie ou remova em duas implantações: primeiro pare de usar, depois remova. Detalhes em
references/database.md.
Variáveis de ambiente e segredos
Toda configuração de tempo de execução está em <pasta de implantação>/secrets.env, comprometida criptografada com sops. Apenas os valores são criptografados; os nomes das chaves permanecem legíveis. Altere-o com o próprio sops. Nunca descriptografe para um arquivo que você possa comprometer, e nunca imprima valores secretos no chat ou logs.
sops set deploy-data-clerk/secrets.env '["STRIPE_SECRET_KEY"]' '"sk_live_..."'
sops unset deploy-data-clerk/secrets.env '["OLD_KEY"]'
sops decrypt deploy-data-clerk/secrets.env | cut -d= -f1
Em seguida, commit e push para main; fica ao vivo em ~5 min. Quando o usuário tiver que fornecer o valor (uma chave de API de algum painel), peça para ele executar a linha sops set ele mesmo, ou cole o valor apenas em seu próprio terminal. O guia completo (chaves, onde elas vivem, rotação, pegadinhas) está em references/secrets.md.
Banco de dados
Postgres 18, gerenciado pelo operador. O aplicativo recebe seu próprio banco de dados e usuário via DATABASE_URL. Mudanças de esquema são arquivos de migração que se aplicam automaticamente na implantação (references/database.md).
Backups: um backup completo todas as noites às 03:00 UTC mais um log contínuo de cada alteração, armazenado fora do servidor no Google Cloud Storage. O operador pode restaurar o banco de dados para qualquer momento nos últimos 7 dias. Portanto, se dados forem excluídos ou uma migração der errado, não entre em pânico e não tente corrigir manualmente: anote a hora em que aconteceu e peça ao operador uma restauração (modelo em references/troubleshooting.md). Uma restauração retrocede todo o banco de dados, então as alterações feitas após esse momento são perdidas.
Fatos de rede que seu código pode precisar
- Caminho do tráfego: visitante → Cloudflare → Traefik (ingresso do cluster) → aplicativo. Certificados HTTPS são automáticos. O aplicativo em si fala HTTP simples em
$PORT;X-Forwarded-Proto: httpsindica que o original era HTTPS. Confie nos cabeçalhos de proxy e não redirecione para HTTPS no aplicativo (isso causaria um loop). - IP real do visitante: leia o cabeçalho
CF-Connecting-IP.X-Forwarded-Forcontém um endereço do Cloudflare, portanto, limitar a taxa com base nele coloca todos os visitantes em um único balde. - Traefik adiciona estes cabeçalhos: HSTS (2 anos, preload),
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN(o site não pode ser incorporado em um iframe em outros domínios). Cabeçalhos extras (CSP, COOP/COEP paraSharedArrayBuffer, permitindo iframes) são definidos pelo operador mediante solicitação. - O ingresso não compacta respostas. Cloudflare compacta tipos de texto comuns; para controle total, compacte no aplicativo ou forneça ativos pré-compactados.
- Conexões longas (SSE, websockets) são aceitáveis, mas o Cloudflare encerra uma conexão ociosa após ~100 s. Envie um heartbeat a cada ~30 s e reconecte no cliente.
- Saída: o aplicativo pode chamar qualquer endpoint público da internet em qualquer porta. Isso inclui APIs de LLM, Stripe, e-mail, S3 e bancos de dados gerenciados ou Redis (Neon, Supabase, Atlas, Upstash) em seus endpoints públicos. Ele não pode alcançar redes privadas (VPC peering, PrivateLink, IPs privados) ou outros projetos no cluster. O tráfego de entrada só vem através da URL pública.
- Serviços de terceiros: coloque suas chaves e URLs em
secrets.env. Se um serviço precisar de uma lista de permissões de IP, o cluster tem um IP de saída estático: peça ao operador por ele (não é publicado) e informe ao usuário que ele é compartilhado com outros projetos no cluster. O servidor está no centro dos EUA, portanto, prefira regiões US-central/us-east para bancos de dados externos (um banco de dados na UE adiciona ~100 ms por consulta). Um banco de dados externo é de propriedade do usuário: os backups do Data Clerk não o cobrem. Para chamadas lentas de LLM, transmita a resposta ou execute-as como jobs em segundo plano; uma solicitação de navegador esperando mais de ~100 s é interrompida. - Os recursos são limitados (memória/CPU em PROJECT.md). Exceder o limite de memória mata e reinicia o aplicativo. Se isso acontecer, peça ao operador mais espaço em vez de tentar resolver no código.
- Uma cópia do aplicativo é executada (uma réplica). Não confie no estado em memória para sobreviver a uma implantação e mantenha as sessões no banco de dados ou em cookies assinados.
O que apenas o operador pode fazer
O usuário não tem acesso ao cluster, e isso é por design. Escreva ao operador uma solicitação curta (veja references/troubleshooting.md para o modelo) para:
novo domínio ou subdomínio · mais memória/CPU · armazenamento de arquivos/objetos · restauração de banco de dados · logs do aplicativo ou causas de falha · cabeçalhos de segurança alterados · alteração do comando de migração ou inicialização, porta ou caminho de saúde · jobs agendados (cron) · serviços extras (Redis, filas) · um segundo aplicativo no mesmo projeto · integração de um novo repositório.
Mais detalhes
references/stacks.md: o contrato universal para qualquer linguagem; full-stack vs SPA + API vs estático; Node, Python FastAPI, Go e outras stacks.references/new-project.md: integração de um repositório (arquivos a adicionar, o que pedir ao operador, Dockerfile por stack).references/secrets.md: sops em profundidade, o arquivo de chave, rotação, variáveis de tempo de construção.references/database.md: migrações, string de conexão, backups, correções de dados.references/setup-tools.md: instalação segura de sops/git/gh no macOS, Linux e Windows. Executescripts/install-tools.shouscripts/install-tools.ps1.references/troubleshooting.md: "minha alteração não está ao vivo", "site está fora", erros comuns, o modelo de solicitação do operador.assets/: modelos de Dockerfile (Node, Python, Go) e modelos para o workflow de implantação,kustomization.yaml,.sops.yaml,.gitattributesePROJECT.md.