🌐 यह पृष्ठ अभी तक हिंदी में अनुवादित नहीं किया गया है। पुर्तगाली (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
1. Cliente clica "Assinar Cloud Pro" em /pricing
│
▼
2. Stripe Checkout (Brasil) processa cartão/Pix
│
▼
3. Stripe webhook → Cloud Function `provisionServer`
│
▼
4. Cloud Function chama Hetzner API (cria VM CX32)
+ gera token efêmero (TTL 30 min)
│
▼
5. VM boota com cloud-init que executa
`curl https://roqueos.com.br/install.sh | bash`
passando --customer-id, --tier, --provisioning-token,
--provisioning-callback
│
▼
6. install.sh roda dentro da VM:
- Pulla images Docker
- Cria volumes persistentes
- Sobe roqueos-server + sidecars (guacd, cloudflared)
- Aguarda /health responder 200
- POST /admin/setup local pra gerar admin key
- POSTa callback pro Firebase Functions com
{customerUid, token, apiKey, apiSecret}
│
▼
7. Cloud Function `provisioningCallback` valida o token,
marca subscription `active`, salva creds (TTL 24h),
dispara welcome email via SMTP
│
▼
8. Cliente recebe email com URL + credenciais
Login no /app já configurado pro server novoComponentes 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.
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)