🌐 यह पृष्ठ अभी तक हिंदी में अनुवादित नहीं किया गया है। पुर्तगाली (BR) संदर्भ दिखाया जा रहा है। अनुवाद में मदद करें।
Provisioning automatizado (Cloud)
Este documento descreve como uma VM RoqueOS Cloud é criada do zero em ~3-5 min após o cliente assinar. Você não precisa entender nada disso pra usar — é referência técnica pra debug, suporte, ou pra quem quer adaptar o fluxo pro próprio negócio.
Diagrama do fluxo
⚠️ O token dos passos 4 e 7 é o que impede qualquer um de registrar um servidor em nome de outro cliente. Ele vale 30 minutos: uma VM que demore mais que isso para chamar de volta cai no ramo da direita e não conclui.
Componentes envolvidos
Frontend (roqueos-front)
src/pages/PricingPage.vue— botão "Assinar" leva pro Stripe Checkoutfunctions/index.js— Cloud FunctionsprovisionServereprovisioningCallbackpublic/install.sh— mirror do instalador canonical doroqueos-server
Backend (roqueos-server)
install/install.shv1.1.0-cloud-callback — instalador one-liner com suporte a callbacksrc/modules/admin/admin.controller.ts— endpointPOST /admin/setupidempotente (gera admin key)
Infra externa
- Hetzner Cloud API (
https://api.hetzner.cloud/v1) — criação de servers - Cloudflare API — criação de subdomain
<id>.cloud.roqueos.com.br(planejado v1.2) - Firebase Secret Manager — guarda
HCLOUD_TOKEN,PROVISIONER_SSH_KEY_ID,STANDARD_FIREWALL_ID - Stripe Brasil — checkout BRL via cartão/Pix
Tier → spec Hetzner
| Tier RoqueOS | Hetzner SKU | vCPU | RAM | Disco SSD | COGS €/mês |
|---|---|---|---|---|---|
| Starter | cx22 | 2 shared | 4 GB | 40 GB | 4.51 |
| Pro | cx32 | 4 shared | 8 GB | 80 GB | 7.55 |
| Power | ccx23 | 4 dedicated | 16 GB | 160 GB | 31.20 |
| Enterprise | ad-hoc | varia | varia | varia | varia |
Localização: Falkenstein, Alemanha (fsn1). Imagem: ubuntu-22.04.
Endpoints Cloud Function
provisionServer (admin onCall)
Trigger: Stripe webhook customer.subscription.created. Internamente é uma https.onCall que requer context.auth.token.admin === true.
Input:
{
"customerUid": "abc123",
"tier": "pro"
}Output:
{
"ok": true,
"vmId": "12345678",
"vmIp": "65.108.x.x",
"provisioningToken": "<64 hex chars>",
"callbackUrl": "https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback",
"tokenExpiresAt": 1716800000000
}O provisioningToken é mostrado uma única vez pro admin (não persiste em log). O hash SHA-256 dele fica em subscriptions/{uid}.provisioning.tokenHash no Firestore, com TTL 30 min.
provisioningCallback (HTTP público)
Trigger: VM recém-criada chama POST desse endpoint via install.sh.
URL pública: https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback
Por que público sem auth Firebase: a VM novinha não tem credenciais Firebase. Autenticação é feita pelo token efêmero (one-time, SHA-256 hash comparado).
Input (POST JSON):
{
"customerUid": "abc123",
"token": "<64 hex chars>",
"apiKey": "ros_xxxx",
"apiSecret": "ros_secret_xxxx"
}Validações:
- Shape do payload (regex em token + apiKey + apiSecret)
tokenHashconfere com o gravado no Firestore- Token não expirado
- Subscription ainda em
provisioning(não foi completada antes)
Output ok: 200 { "ok": true }Output erro: HTTP 400/401/404/409/410 + { "error": "<code>" }
Erros possíveis:
400 missing_fields— payload incompleto400 invalid_*— shape inválido401 token_mismatch— token errado (potencial ataque, loga IP)404 subscription_not_found— customerUid não existe409 already_completed— callback já foi chamado com sucesso (dedup)409 no_pending_provisioning— subscription não está em estado provisioning410 token_expired— passou dos 30 min
install.sh flags do cloud-init
A Cloud Function provisionServer gera um user_data (cloud-init) que executa:
#!/bin/bash
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y curl ca-certificates
curl -fsSL https://roqueos.com.br/install.sh | bash -s -- \
--customer-id "${CUSTOMER_UID}" \
--tier "${TIER}" \
--provisioning-token "${TOKEN}" \
--provisioning-callback "${CALLBACK_URL}" \
--skip-promptsAs 4 flags --customer-id, --tier, --provisioning-token, --provisioning-callback foram adicionadas no install.sh v1.1.0-cloud-callback (INSTALLER_VERSION no topo do script). Quando todas as quatro estão presentes, o instalador dispara do_provisioning_callback() após wait_for_server retornar healthy.
O que o cliente vê quando entra

Acompanhar um provisionamento, passo a passo
Quando um cliente assina e o servidor não chega, é esta a ordem para conferir — cada passo elimina uma etapa do fluxo acima:
- Stripe — o pagamento consta como concluído e o webhook saiu com 200? Se o webhook falhou, nada depois disso aconteceu.
- Logs da
provisionServer— ela recebeu o evento e chamou a Hetzner? O log traz o id da VM criada. - Console da Hetzner — a VM existe e está ligada? VM criada e parada é falha de cloud-init, não de pagamento.
/healthda VM — responde 200? Enquanto não responder, oinstall.shainda está subindo os containers lá dentro.- Logs da
provisioningCallback— o callback chegou e o token era válido? Token expirado (30 min) é o motivo mais comum de VM de pé sem assinatura ativa. - Firestore — a assinatura virou
activee as credenciais foram gravadas? - E-mail — o SMTP entregou? Se tudo acima está certo e o cliente não recebeu, o problema é só o e-mail, e as credenciais já existem para reenvio.
Validar do lado do operador
Testar callback manualmente (substituindo customerUid/token fakes):
curl -sS -X POST \
"https://southamerica-east1-roqueos.cloudfunctions.net/provisioningCallback" \
-H 'Content-Type: application/json' \
-d '{
"customerUid":"smoke-test",
"token":"0000000000000000000000000000000000000000000000000000000000000000",
"apiKey":"ros_smoketestkey",
"apiSecret":"ros_secret_smoketest_secret_xyz"
}'
# Esperado: HTTP 404 {"error":"subscription_not_found"}Troubleshooting
| Sintoma | Causa provável | Fix |
|---|---|---|
| Email de welcome não chegou | SMTP_HOST/USER/PASS faltando no Secret Manager | gcloud secrets list --project=roqueos | grep SMTP + reconfigurar |
VM criada mas subscription ficou provisioning | install.sh falhou DENTRO da VM (não chamou callback) | SSH na VM: journalctl -u cloud-init -f + docker logs roqueos-server |
Callback retorna token_expired | Provisioning levou >30 min (VM lenta) | Rodar provisionServer de novo pra regenerar token + cloud-init |
Callback retorna token_mismatch | Bug ou tentativa de ataque | Conferir IP origem no log da Function; se suspeito, rotacionar e alertar |
vmIp: null no doc Firestore | Hetzner API timeout ou resposta sem public_net | Retentar provisionServer; logs em gcloud functions logs read provisionServer |
Não documentado aqui (mas relevante)
- Renovação automática de assinatura: Stripe lida sozinho via
customer.subscription.updatedwebhook. VM continua viva. - Cancelamento: webhook
customer.subscription.deleted→ futura funçãodecommissionServerque para VM + snapshot final + email "seus dados estão preservados por 30 dias". - Upgrade de tier (Pro → Power): planejado v1.2 — provavelmente migration via
hcloud server change-type. - Cloudflare Tunnel per-customer: planejado — hoje cada VM expõe via IP público + portas 80/443. Tunnel adiciona TLS via edge SP (latência mais baixa pra BR).
Referências cruzadas
- RoqueOS Cloud overview — planos, preços, racional
- Instalação self-host — mesmo
install.shque roda dentro do cloud-init - Server Admin Panel — pra gerenciar o servidor recém-provisionado
- Memory técnica:
hetzner_architecture_decision.mdno repo (interna)