# Introdução URL: https://0.0.0.0:3002/docs O que é o Hydro, os conceitos principais e por onde começar. O **Hydro** é o painel e a API de hospedagem da **AcquaCloud**. Nele você publica **aplicações** (sites, APIs, bots e workers) e cria **bancos de dados** gerenciados, tudo dentro de um **workspace** com rede privada, membros e uma cobrança só. ## Conceitos [#conceitos] | Conceito | O que é | | ------------------ | ----------------------------------------------------------------------------------------------- | | **Workspace** | Onde ficam aplicações, bancos e membros. Cada workspace tem um plano e uma rede privada própria | | **Aplicação** | Seu código rodando: **web** (com endereço HTTPS) ou **worker** (sem porta, como um bot) | | **Deploy** | Uma versão da aplicação: do GitHub, de um .zip ou de uma imagem Docker pública | | **Banco de dados** | PostgreSQL, MySQL, Redis ou MongoDB gerenciado, com backup diário | | **Plano** | Define a memória, a vCPU e os membros do workspace | ## Por onde começar [#por-onde-começar] - [Primeiros passos](/docs/primeiros-passos): Da conta criada à primeira aplicação no ar. - [Aplicações](/docs/aplicacoes): Tipos, origens do código, endereço, variáveis e deploy. - [Bancos de dados](/docs/bancos-de-dados): Motores, conexão, acesso externo e backups. - [API](/docs/api): Automatize com um token: convenções e referência. --- # Primeiros passos URL: https://0.0.0.0:3002/docs/primeiros-passos Crie a conta, o workspace e coloque a primeira aplicação no ar. ## 1. Crie a conta [#1-crie-a-conta] Cadastre-se no painel e confirme o e-mail. O **primeiro workspace** que você criar entra num [teste grátis](/docs/planos-e-quotas#período-de-teste) de 14 dias, sem cartão. ## 2. Crie a aplicação [#2-crie-a-aplicação] Em **Aplicações → Criar aplicação**, escolha de onde vem o código: * **GitHub:** conecte a conta e escolha o repositório e a branch. Cada push pode virar um deploy ([Deploy pelo GitHub](/docs/deploy-github)). * **Upload de .zip:** envie o código (sem `node_modules`). O Hydro detecta a linguagem e faz o build. * **Imagem Docker:** uma imagem pública, como `ghcr.io/empresa/app:1.0`, sem build. Depois, o nome, o tipo (**Web** ou **Worker**), a porta e, se quiser, o **endereço** em `acq.lat`. Por fim, a memória: digite quantos MB a app precisa (qualquer valor a partir de 100 MB, como 231). ## 3. Configure as variáveis [#3-configure-as-variáveis] Na aba **Variáveis**, cadastre o que a aplicação precisa (ou importe um `.env`). Marque como **segredo** o que não deve aparecer de volta. ## 4. Faça o deploy [#4-faça-o-deploy] O deploy mostra o log do build ao vivo. Aplicações web entram no ar quando respondem na porta, com HTTPS no endereço escolhido. ## 5. Adicione um banco (opcional) [#5-adicione-um-banco-opcional] Em **Bancos de dados → Criar banco**, escolha o motor e digite a memória em MB (a partir do mínimo do motor). Na aba **Conexão**, revele a senha, copie a URL e cadastre-a como variável secreta da app (em **Variáveis**). Pela API, `POST …/connect` faz isso de uma vez. ## Próximos passos [#próximos-passos] * [Domínios próprios](/docs/aplicacoes#domínios-personalizados) * [Logs, métricas, terminal e arquivos](/docs/aplicacoes-runtime) * [Automatizar pela API](/docs/api) --- # Usar com IA URL: https://0.0.0.0:3002/docs/ia Leve a documentação do Hydro para o ChatGPT, o Claude, o Cursor ou o seu agente, em Markdown. As docs do Hydro foram feitas para serem lidas também por agentes de IA. ## Arquivos para agentes [#arquivos-para-agentes] | Endereço | O que tem | | ---------------------------------- | ------------------------------------------------------------------------------------------------------ | | [`/llms.txt`](/llms.txt) | Índice da documentação ([llmstxt.org](https://llmstxt.org)), com o link para o Markdown de cada página | | [`/llms-full.txt`](/llms-full.txt) | Todas as páginas em Markdown, num arquivo só | | `/docs/.mdx` | Uma página em Markdown, como [`/docs/aplicacoes.mdx`](/docs/aplicacoes.mdx) | | [`/openapi.json`](/openapi.json) | A especificação OpenAPI da API pública | ## Em cada página [#em-cada-página] No topo de cada página: * **Copiar como Markdown** copia o texto da página para colar no chat. * **Abrir em…** abre o ChatGPT ou o Claude já com a página indicada. ## Dicas [#dicas] * **Dê o token por variável de ambiente.** Crie um [token de API](/docs/api/autenticacao) só para o agente, com o escopo `read` se ele só for consultar, e defina uma validade. Revogue quando terminar. * **Comece pelo `llms.txt`.** Ele é pequeno e aponta para o resto; o `llms-full.txt` é melhor quando o agente precisa de tudo de uma vez. * **Use o OpenAPI para gerar código.** Clientes e tipos gerados do `openapi.json` erram menos que rotas escritas à mão. * **Peça confirmação para o que apaga.** Excluir apps e bancos e restaurar backups não tem volta: peça ao agente que confirme com você antes. --- # Workspaces e permissões URL: https://0.0.0.0:3002/docs/workspaces Papéis, convites, transferência de propriedade e limites. Todo recurso pertence a um **workspace**. Uma pessoa pode participar de vários workspaces, com um papel em cada. **Atalho:** **Ctrl+K** (ou **⌘K** no Mac), ou o campo **Buscar** da barra lateral, abre a busca do painel. Ela leva direto a uma aplicação, a um banco, a qualquer página do workspace ou a outro workspace. ## Papéis [#papéis] | Papel | Resumo | |---|---| | Proprietário (`owner`) | Controle total, incluindo excluir o workspace e gerenciar proprietários. | | Administrador (`admin`) | Gerencia membros, convites e configurações. Não exclui o workspace. | | Desenvolvedor (`developer`) | Opera recursos e tokens de API. Não gerencia membros. | | Leitor (`viewer`) | Somente leitura. | | Permissão | Proprietário | Administrador | Desenvolvedor | Leitor | |---|---|---|---|---| | `workspace:read` | ✓ | ✓ | ✓ | ✓ | | `workspace:update` | ✓ | ✓ | — | — | | `workspace:delete` | ✓ | — | — | — | | `members:read` | ✓ | ✓ | ✓ | ✓ | | `members:invite` | ✓ | ✓ | — | — | | `members:update` | ✓ | ✓ | — | — | | `members:remove` | ✓ | ✓ | — | — | | `api_tokens:read` | ✓ | ✓ | ✓ | — | | `api_tokens:manage` | ✓ | ✓ | ✓ | — | | `audit:read` | ✓ | ✓ | — | — | | `apps:read` | ✓ | ✓ | ✓ | ✓ | | `apps:manage` | ✓ | ✓ | ✓ | — | | `integrations:manage` | ✓ | ✓ | — | — | | `billing:read` | ✓ | ✓ | — | — | | `billing:manage` | ✓ | — | — | — | Regras de atribuição: * **Proprietários** atribuem qualquer papel. * Os demais só atribuem ou alteram papéis **abaixo** do seu. Um administrador gerencia desenvolvedores e leitores, mas não outros administradores nem proprietários. * Ninguém altera o próprio papel. Um proprietário que quer sair da função usa **Transferir propriedade**. * O workspace sempre tem **pelo menos um proprietário**. ## Convites [#convites] 1. Um administrador ou proprietário convida por e-mail, com um papel (exceto proprietário). 2. A pessoa recebe um link válido por 7 dias. 3. Para aceitar, ela entra ou cria uma conta **com o mesmo e-mail** do convite. Criando a conta pelo link do convite, com esse e-mail, ela já entra sem confirmar o e-mail (o convite chegou nele) e volta para o convite para aceitar. Com outro e-mail, o link de confirmação traz a pessoa de volta ao convite, já logada. Membros e convites pendentes contam para o limite de membros do [plano](/docs/planos-e-quotas). Convites podem ser revogados enquanto estão pendentes. ## Remoção e saída [#remoção-e-saída] Remover um membro ou sair do workspace também **revoga os tokens de API** da pessoa naquele workspace. ## Workspace suspenso [#workspace-suspenso] Um workspace suspenso pela equipe da AcquaCloud fica **somente leitura**: alterações respondem `403 workspace_suspended`. Fale com o suporte para entender o motivo. --- # Planos e quotas URL: https://0.0.0.0:3002/docs/planos-e-quotas Como funcionam o período de teste, o pool de recursos do workspace e os limites. Cada workspace tem um **plano**. O plano define a **memória** que você distribui entre aplicações e bancos, a **vCPU** do workspace e o limite de membros. **Armazenamento e domínios são ilimitados em todo plano**, inclusive no teste. Não há limite de quantidade de recursos, só um tamanho mínimo por tipo. ## CPU: garantida pela memória, dividida nos picos [#cpu-garantida-pela-memória-dividida-nos-picos] Você não escolhe a vCPU de cada aplicação ou banco: * **Garantida:** cada recurso tem **1 vCPU por GB de memória**. Assim, 256 MB valem 0,25 vCPU e 2 GB valem 2 vCPU. Aumentar a memória aumenta a CPU garantida. * **Picos:** quando sobra CPU no workspace, qualquer recurso usa mais que a garantia, até a **vCPU do plano**. Uma app sozinha pode usar toda a vCPU do plano enquanto as outras estão quietas. * **Disputa:** quando todos precisam ao mesmo tempo, a vCPU do plano é dividida na proporção das garantias. O total do workspace nunca passa da vCPU do plano. Veja o plano, o uso e os limites em **Workspace → Plano e uso**, ou pela API em `GET /v1/workspaces/{id}/plan`. ## Período de teste [#período-de-teste] * O **primeiro** workspace que você cria entra em teste. Hoje o teste dura 14 dias, com 1 vCPU, 1 GB de memória e 3 membros. * Cada pessoa tem um teste só. Os outros workspaces que você criar começam **sem plano** até contratar um. * Alguns dias antes do fim, os proprietários e administradores do workspace recebem um aviso por e-mail. * Quando o teste termina, começa uma **carência** de 7 dias: o que já existe continua rodando, mas não é possível criar nem aumentar recursos. A data do fim da carência aparece na página do plano e no e-mail. Quando a carência acaba, as [aplicações](/docs/aplicacoes) são paradas, sem apagar nada, até o workspace assinar um plano. ## Planos pagos [#planos-pagos] O catálogo está em **Plano e uso**, no site da AcquaCloud e em `GET /v1/plans` (público, sem login; preço mensal em centavos de real e `featured` no plano em destaque). A assinatura é feita em **Plano e uso**; veja [Cobrança](/docs/cobranca). Quando um plano muda de preço ou de limites, quem já assinou continua com as condições em que entrou. ## Status do plano [#status-do-plano] | Status | O que significa | | --------------- | ----------------------------------------------------------------------------- | | Em teste | Pode criar e aumentar recursos dentro dos limites do teste, até a data de fim | | Ativo | Plano pago em vigor | | Teste encerrado | Carência: recursos existentes continuam, nada novo é criado | | Sem plano | Nenhum recurso novo; só o proprietário como membro | ## Limites [#limites] A memória do plano vale para o workspace inteiro. Um pedido que não cabe responde `409 quota_exceeded`, dizendo quanto foi pedido e quanto está disponível. A vCPU não é reservada: ela é o teto dos picos, dividido entre todos. Tamanho mínimo por tipo de recurso (a vCPU é a garantida pela memória; o disco mínimo é técnico e o disco não tem limite no plano): | Tipo | Memória | vCPU garantida | Disco mínimo | | ---------- | ------- | -------------- | ------------ | | Aplicação | 100 MB | 0,1 | 0 | | PostgreSQL | 256 MB | 0,25 | 1 GB | | MySQL | 512 MB | 0,25 | 1 GB | | MariaDB | 256 MB | 0,25 | 1 GB | | MongoDB | 512 MB | 0,25 | 1 GB | | Redis | 64 MB | 0,1 | 256 MB | Abaixo do mínimo, a resposta é `422 resource_below_minimum`. Sem plano em vigor, `403 plan_required`. **Membros:** membros + convites pendentes contam para o limite. Ao atingir o limite, novos convites respondem `409 member_limit_reached`. ## Acima da quota [#acima-da-quota] Se o plano mudar para um menor que o uso atual, o workspace fica **acima da quota**. Nada é apagado nem parado: você pode reduzir e remover recursos normalmente, mas não pode criar nem aumentar até voltar a caber no plano. --- # Aplicações URL: https://0.0.0.0:3002/docs/aplicacoes Tipos, origens do código, endereço em acq.lat, deploy, variáveis de ambiente, domínios e backups. Uma **aplicação** roda dentro de um workspace e usa a memória do [plano](/docs/planos-e-quotas). A CPU vem da memória: 1 vCPU garantida por GB e, nos picos, a CPU livre do workspace até a vCPU do plano. | Tipo | Para quê | Endereço | | ---------- | --------------------------------------------------------------------------- | ---------------------------------------------------- | | **Web** | Sites e APIs: a app escuta numa porta (padrão `3000`, disponível em `PORT`) | `https://.acq.lat`, com HTTPS automático | | **Worker** | Processos sem porta: bots, filas, tarefas | Nenhum | O código vem de uma de três **origens**, escolhidas na criação: * **Upload de .zip:** você envia o código e o Hydro faz o build, detectando a linguagem. * **Imagem Docker:** uma imagem pública (ex.: `ghcr.io/empresa/bot:1.2.0`), sem build. * **GitHub:** um repositório conectado, com build no Hydro. Veja [Deploy pelo GitHub](/docs/deploy-github). ## Criar [#criar] `POST /v1/workspaces/{id}/apps` com nome, tipo, origem e memória (`memoryMb`, qualquer inteiro a partir de 100 MB). A vCPU (`cpuMillis` na resposta) é calculada pela memória. * O identificador sai do nome (`Minha API` → `minha-api`) e pode ser informado em `slug`. Ele é único no workspace. * O tamanho é **reservado no plano na hora**: se não couber, a resposta é `409 quota_exceeded`. Mínimo de 100 MB por aplicação. * Sem plano em vigor, a resposta é `403 plan_required`. * Criar não sobe nada: o código chega no primeiro deploy. ## Endereço em acq.lat [#endereço-em-acqlat] Toda aplicação web ganha um endereço `https://.acq.lat` com HTTPS. * **Escolha o subdomínio** na criação, em `subdomain` (ex.: `minha-loja` → `minha-loja.acq.lat`). Sem ele, o Hydro gera um a partir do identificador, com um sufixo. * Regras: 3 a 40 caracteres, letras minúsculas, números e hífens, sem hífen no começo, no fim ou repetido. Alguns nomes são reservados. * O subdomínio é **único em toda a plataforma**: se já estiver em uso, a resposta é `409 subdomain_taken`. * **Trocar depois:** em **Configurações → Endereço**, ou `PATCH .../apps/{appId}` com `subdomain`. O endereço novo vale na hora e o antigo **para de responder**. ## Deploy [#deploy] | Origem | Como | | ---------------- | ---------------------------------------------------------------------------------------------------------------------- | | Upload de .zip | `POST .../apps/{appId}/deployments/upload` com o arquivo no corpo (`Content-Type: application/zip`, até 200 MB) | | Imagem Docker | `POST .../apps/{appId}/deployments` com `{ "image": "..." }` (ou vazio para usar a imagem da app). Só imagens públicas | | Refazer o deploy | `POST .../apps/{appId}/deployments` vazio numa app por upload: reaproveita o build atual com variáveis e tamanho novos | * **O que vai no .zip:** só o código. A pasta `node_modules` não é aceita (`400 artifact_has_node_modules`): as dependências são instaladas no build. O .zip também é recusado (`422 artifact_invalid`) com caminhos absolutos ou com `..`, mais de 50 mil entradas, mais de 2 GB descompactados ou compressão suspeita. * Só **um deploy em andamento** por aplicação (`409 deployment_in_progress`). Um deploy na fila pode ser cancelado (`POST .../deployments/{id}/cancel`). * Estados: na fila → fazendo build → subindo → no ar. Se falhar, o deploy anterior continua no ar e o motivo aparece em `failureReason`. * **Logs de build:** `GET .../deployments/{id}/logs?after=0`; leia de novo com `after = nextAfter` até `done`. Cada deploy guarda até 5 MB de log. * **Rollback:** `POST .../deployments/{id}/rollback` sobe de novo a imagem de um deploy anterior, sem build, enquanto ela estiver disponível (`imageAvailable`). As 3 últimas imagens de cada app ficam guardadas. ## O que a aplicação encontra [#o-que-a-aplicação-encontra] * **Web:** escute em `0.0.0.0` na porta da variável `PORT`. O deploy só entra no ar quando a porta responde (até 60 s); se não responder, o deploy falha e a versão anterior continua. * **Pasta do projeto:** gravável e persistente. O que a aplicação grava ali, como um banco SQLite ou uploads, continua entre deploys e reinícios e entra nos backups. `/tmp` serve para temporários (64 MB). * **Usuário:** a aplicação roda como usuário sem privilégios, dono da pasta do projeto. * **Variáveis do Hydro:** `PORT` (web), `HYDRO_APP_ID` e `HYDRO_DEPLOYMENT_ID`. * **Troca sem queda (web):** a versão nova sobe ao lado da antiga e o endereço só muda quando ela responde. Workers param a versão antiga antes de subir a nova, para nunca rodarem dois ao mesmo tempo. * **Rede:** aplicações e bancos do mesmo workspace ficam numa rede privada; os de outros workspaces não os alcançam. Cada um tem um nome interno, como `minha-api.internal`. A saída para a internet é liberada. ## Variáveis de ambiente [#variáveis-de-ambiente] `PUT /v1/workspaces/{id}/apps/{appId}/env` substitui o conjunto inteiro (até 100 variáveis, 32 KB por valor). * Marque como **segredo** o que não deve aparecer de volta: a API e o painel nunca mostram o valor de um segredo (`value: null`). Para manter um segredo sem reenviar o valor, mande-o com valor vazio. * Tudo é guardado **cifrado** (AES-256-GCM) e nunca entra no build. * `PORT` e nomes começando com `HYDRO_` são reservados. * Mudanças valem a partir do próximo deploy (refazer o deploy aplica sem novo build). ## Parar, iniciar e reiniciar [#parar-iniciar-e-reiniciar] `POST .../apps/{appId}/stop`, `.../start` e `.../restart` (exigem um deploy no ar: `409 app_not_deployed`). Parar tira a app e o endereço do ar, mas mantém o tamanho reservado e a pasta do projeto. ## Estado [#estado] O `status` vem do servidor em que a app roda: `idle` (sem deploy), `deploying`, `starting`, `running`, `restarting`, `stopped` ou `crashed`. Em `statusDetail` ficam o código de saída, o número de reinícios e as últimas linhas quando o processo cai. * Se o processo cai, ele é reiniciado automaticamente, com espera crescente. * Depois de 5 saídas em 10 minutos, a app fica `crashed` e é parada, para não ficar em loop. ## Domínios personalizados [#domínios-personalizados] Apps web podem responder também num domínio seu, com HTTPS automático. 1. Em **Configurações → Endereço**, adicione o domínio (ex.: `loja.exemplo.com.br`). Até 20 por aplicação. 2. No DNS do domínio, crie um registro **A** para o IP mostrado no painel (e **AAAA**, se houver IPv6) e um **TXT** `_hydro.` com o valor indicado. 3. Clique em **Verificar**. Com A e TXT certos, o domínio fica **Ativo** e o certificado é emitido em seguida. * O registro A precisa apontar direto para o IP indicado. Um proxy na frente (como o da Cloudflare com a nuvem laranja) impede a emissão do certificado. * O IP é o mesmo para todos os domínios e não muda se a aplicação trocar de servidor. * Enquanto o domínio aponta para o Hydro sem estar ligado a uma aplicação, ele mostra a página "Domínio ainda não conectado". * O Hydro rechecha sozinho domínios **Aguardando DNS** por até 7 dias e os ativos a cada 6 horas. * Um domínio só pode estar em uma aplicação da plataforma (`409 domain_taken`). | Método | Caminho | | ------------- | ------------------------------------------ | | `GET`, `POST` | `/v1/workspaces/{id}/apps/{appId}/domains` | | `POST` | `…/domains/{domainId}/verify` | | `DELETE` | `…/domains/{domainId}` | ## Backups dos arquivos [#backups-dos-arquivos] A pasta do projeto tem backup diário automático, cifrado e fora do servidor. Em **Configurações → Backups**: fazer backup agora (no máximo um por hora) e restaurar, o que substitui a pasta do projeto pela do backup. Antes de restaurar, o Hydro guarda um backup do estado atual. ## Excluir [#excluir] `DELETE /v1/workspaces/{id}/apps/{appId}` libera o tamanho no plano e remove a aplicação, as imagens e a pasta do projeto. ## Permissões [#permissões] | Ação | Papéis | | --------------------------------------------------------- | ------------------------------------------ | | Ver aplicações, deploys e variáveis (segredos mascarados) | Todos, inclusive leitor | | Criar, alterar, fazer deploy, excluir | Desenvolvedor, administrador, proprietário | --- # Deploy pelo GitHub URL: https://0.0.0.0:3002/docs/deploy-github Conectar uma conta ou organização do GitHub, criar apps a partir de um repositório e usar monorepos. Aplicações com origem **GitHub** pegam o código direto de um repositório, sem upload. O Hydro usa um GitHub App da AcquaCloud: você escolhe em quais repositórios ele entra, e ele só tem **leitura** do código e permissão para marcar o **status** dos commits. ## Conectar e criar [#conectar-e-criar] 1. **Configurações do workspace → GitHub → Conectar conta do GitHub.** No GitHub, escolha a conta ou organização e os repositórios. É preciso ser proprietário ou administrador do workspace. 2. **Aplicações → Criar aplicação → GitHub.** Escolha o repositório, a branch, a pasta (num monorepo) e se cada push faz deploy. O primeiro deploy sai na criação, com o último commit da branch. 3. **Na aplicação:** "Deploy do último commit", "Deploy de outro commit" e "Refazer deploy sem build". Cada deploy mostra o commit, com link para o GitHub. Branch, pasta e deploy automático mudam em **Configurações → Repositório**. ## Opções do repositório [#opções-do-repositório] | Campo | Padrão | O que faz | | --------------- | ----------- | ------------------------------------------------------------------------------- | | `branch` | — | Branch acompanhada. Precisa existir no repositório | | `rootDir` | `""` (raiz) | Pasta da app num **monorepo**, como `apps/loja` | | `autoDeploy` | `true` | Faz deploy a cada push na branch | | `watchRootOnly` | `false` | Com `rootDir`, só faz deploy automático quando o push muda arquivos dessa pasta | O repositório é guardado pelo **ID**: renomeá-lo no GitHub não quebra a app. Branch, pasta e as duas opções mudam com `PATCH .../apps/{appId}` em `github`. ## Deploy [#deploy] | Gatilho | Como | | ---------- | ------------------------------------------------------------------------------------------------------- | | **Manual** | `POST .../apps/{appId}/deployments` com `{}` (último commit da branch) ou `{ "commitSha": "<40 hex>" }` | | **Push** | Automático com `autoDeploy`: cada push na branch faz deploy do commit novo | Durante o deploy, o commit ganha o status **pending** no GitHub, com o contexto `Hydro / `, e termina como **success** ou **failure**, com o motivo. ### Pushes seguidos [#pushes-seguidos] * Deploy ainda na fila: o push novo **substitui** o anterior, que fica cancelado. * Deploy já em build: o push novo **espera** o build terminar e faz deploy em seguida. Só o push mais recente vale. ### Quando o push não faz deploy [#quando-o-push-não-faz-deploy] * Push de tag, branch apagada ou outra branch. * `autoDeploy` desligado ou app sem acesso ao repositório. * `watchRootOnly` ligado e nenhum arquivo alterado dentro de `rootDir`. ## Acesso perdido [#acesso-perdido] Se a instalação for suspensa ou removida no GitHub, ou o repositório sair dela, as apps ficam com `github.accessLost: true`: continuam no ar, mas pushes são ignorados e o deploy manual responde `404 github_repository_not_found`. Reconectar a conta restaura o acesso. ## Limitações [#limitações] * Submódulos e arquivos do Git LFS não vêm no código baixado. * Sem previews de pull request. --- # Logs, métricas, terminal e arquivos URL: https://0.0.0.0:3002/docs/aplicacoes-runtime Acompanhar a aplicação no ar, ver o consumo, abrir um shell no container e ver ou editar o código do projeto. Com a aplicação no ar, o detalhe dela no painel ganha quatro abas de runtime. Logs, métricas, terminal e arquivos vêm do servidor onde a app roda, ao vivo. | Aba | Quem vê | O que faz | | ------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------- | | **Logs** | Todos do workspace (`apps:read`) | Console ao vivo: as últimas 1000 linhas e o que chegar | | **Métricas** | Todos (`apps:read`) | Consumo agora e histórico de até 30 dias | | **Terminal** | Owner, admin e developer (`apps:manage`) | Shell dentro do container | | **Arquivos** | Owner, admin e developer (`apps:manage`) | A pasta do projeto no servidor, ao vivo: navegar, baixar, editar, enviar, renomear e apagar | ## Logs [#logs] * `stdout` e `stderr` da aplicação, com as cores ANSI. Linhas de `stderr` têm uma marca à esquerda. * **Pausar** segura a tela sem perder o que chega; **Seguir** traz o atrasado. O filtro procura texto nas linhas já recebidas. * **Erros e avisos** ficam destacados (vermelho e amarelo), reconhecidos pelo texto da linha (`error`, `fatal`, `exception`, `warn`, `deprecated`…) ou pelo campo `level` de logs em JSON (como o pino). O filtro **Todos / Avisos / Erros** mostra só um nível, com a contagem de cada um. * O histórico é o que o servidor guarda da app (cerca de 30 MB por container, rotacionado). Depois de um deploy, o console segue o container novo. * A conexão volta sozinha se cair. ## Métricas [#métricas] * **Agora**, a cada 2 s: CPU (em vCPU, comparada ao tamanho da app), memória (com o limite), rede (entrada e saída por segundo), disco e número de processos. * **Histórico**: 1 hora (pontos de 1 minuto), 24 horas (5 minutos), 7 dias (30 minutos) e 30 dias (2 horas), com média e pico de CPU e memória, a linha do limite do tamanho, rede e disco por segundo e o tamanho da pasta do projeto. * O agente mede a cada 30 s. Intervalos sem a app rodando aparecem como buracos no gráfico. * Pela API: `GET /v1/workspaces/{id}/apps/{appId}/metrics?range=1h|24h|7d|30d`. * **Na lista de aplicações**, cada app no ar mostra a tendência da última hora de CPU e memória, em % do tamanho dela. Pela API: `GET /v1/workspaces/{id}/usage/apps` (12 pontos de 5 minutos por app; `null` onde não houve amostra). * **Tempo no ar:** o detalhe da app e do banco mostra há quanto tempo estão rodando (`runningSince` na API). A contagem recomeça num deploy novo, num reinício ou quando a app volta depois de parar ou cair. ## Terminal [#terminal] * **Abrir terminal** inicia um shell (`bash` se a imagem tiver, senão `sh`) dentro do container, com o **mesmo usuário e as mesmas restrições da aplicação**: o que a app acessa, o terminal acessa; nada além disso. * Imagens sem shell não abrem terminal (o painel avisa). * Até 2 terminais abertos por aplicação. A sessão fecha depois de 15 minutos sem digitar ou 4 horas no total. * Abertura e fim ficam na **atividade** do workspace, com duração. O que é digitado e o que aparece na tela **não são gravados**. * Só funciona pelo painel, com a sessão de uma pessoa (tokens de API não abrem terminal). ## Arquivos [#arquivos] A aba **Arquivos** mostra a **pasta do projeto no servidor, ao vivo**: o código do último deploy e tudo o que a aplicação grava enquanto roda, como um banco SQLite, uploads ou arquivos de configuração gerados. Vale para apps por upload, do GitHub e por imagem, e funciona com a aplicação parada. A pasta só existe depois do primeiro deploy. Dá para navegar, baixar, editar texto (até 1 MB), enviar arquivos (até 100 MB cada), criar pastas, renomear e apagar. Os arquivos abrem num editor de código (o mesmo motor do VS Code), com realce de sintaxe por linguagem e **Ctrl/Cmd+S** para salvar. Tudo é servido pelo próprio painel, sem CDN de terceiros, e cada arquivo mostra um ícone pelo tipo, quando ele é conhecido. O que muda aqui **vale na hora, direto no servidor**: * **Código editado só roda depois de reiniciar.** O processo que já está no ar não relê o arquivo. * **Arquivo alterado aqui é mantido nos próximos deploys**, como o que a própria aplicação altera ([pasta do projeto](/docs/aplicacoes)). O log do deploy lista os mantidos. * **Para voltar a seguir o código enviado,** apague o arquivo antes do próximo deploy: ele volta na sincronização. * **Apagar um arquivo do código** só vale até o próximo deploy, que o traz de volta. * **Mudar de vez o código de todos os deploys:** envie um .zip novo ou faça push no GitHub. Envios, edições, renomeações, novas pastas e exclusões ficam na atividade do workspace. ### Pela API [#pela-api] | Rota | O que faz | | --------------------------------------- | ----------------------------------------------------------------------------------- | | `GET .../apps/{appId}/files?path=` | Lista uma pasta | | `GET .../files/content?path=` | Baixa um arquivo | | `PUT .../files/content?path=` | Envia o corpo cru (`application/octet-stream`, com `Content-Length`) | | `POST .../files/directories` | Cria pasta (`{ "path": "uploads" }`) | | `POST .../files/rename` | Renomeia ou move (`{ "from": "a.txt", "to": "b.txt" }`); o destino não pode existir | | `DELETE .../files?path=&recursive=true` | Apaga (pasta com conteúdo exige `recursive=true`) | Os caminhos são relativos à raiz do projeto e não aceitam `..`. A pasta `.hydro/` guarda o controle da sincronização: não aparece na lista e responde `404`. | Erro | Quando | | ---------------------------------------------- | ------------------------------------------------ | | `404 file_not_found` | O caminho não existe (ou é `.hydro/`) | | `409 file_exists` | O destino de uma criação ou renomeação já existe | | `409 directory_not_empty` | Pasta com conteúdo, sem `recursive=true` | | `413 file_too_large` | Arquivo maior que o limite | | `409 app_not_deployed` / `409 app_not_running` | A pasta ainda não existe (sem deploy) | | `503 node_offline` | Servidor fora do ar | ## Como a conexão funciona [#como-a-conexão-funciona] Logs, consumo e terminal usam um WebSocket direto com a API. O painel pede um endereço de uso único, válido por 30 segundos (`POST .../apps/{appId}/streams`), e abre a conexão nele. O endereço não carrega cookies; firewalls corporativos precisam permitir WebSocket para o endereço da API. --- # Bancos de dados URL: https://0.0.0.0:3002/docs/bancos-de-dados PostgreSQL, MySQL, Redis e MongoDB gerenciados, na rede privada do workspace e com acesso externo opcional. O Hydro cria e mantém bancos de dados ao lado das suas aplicações. Cada banco roda isolado, com volume próprio, e só é acessível pela **rede privada do workspace**, a menos que você ligue o acesso externo. | Motor | Versões | Porta | Variável ao conectar | | ---------- | ---------- | ----- | -------------------- | | PostgreSQL | 18, 17, 16 | 5432 | `DATABASE_URL` | | MySQL | 8.4 | 3306 | `DATABASE_URL` | | Redis | 8, 7.4 | 6379 | `REDIS_URL` | | MongoDB | 8, 7 | 27017 | `MONGODB_URL` | ## Criar [#criar] `POST /v1/workspaces/{id}/databases` com nome, motor, versão e memória (`memoryMb`). No painel: **Bancos de dados → Criar banco**. * A memória sai da quota do plano. A vCPU vem da memória (1 vCPU garantida por GB) e, nos picos, o banco divide a vCPU do plano com as apps. O disco não conta na quota. * A memória é qualquer número inteiro de MB a partir do mínimo do motor: PostgreSQL 256 MB, MySQL e MongoDB 512 MB, Redis 64 MB. * Mudar o tamanho (`PATCH`) reinicia o banco; os dados ficam no volume. ## Rede privada [#rede-privada] * Cada banco e cada aplicação têm um nome na rede do workspace: `` e `.internal`. Ex.: `postgres://hydro:…@meu-banco.internal:5432/hydro`. * Os nomes são únicos no workspace entre apps e bancos (`409 name_taken`). * Bancos e apps de outros workspaces não resolvem nem conectam. ## Credenciais [#credenciais] * Usuário e banco padrão: `hydro`. A senha é gerada pelo Hydro e guardada cifrada. * `GET …/credentials` devolve host, porta, usuário, banco e a URL de conexão. Cada consulta fica registrada na atividade do workspace. * **Trocar a senha** (`POST …/rotate-password`) aplica uma senha nova sem perder dados. Apps conectadas precisam ser conectadas de novo e reiniciadas. ## Conectar a uma aplicação [#conectar-a-uma-aplicação] `POST …/connect` com `{ appId, variable? }` grava a URL de conexão interna como **variável secreta** da app (a padrão da tabela acima, ou outro nome). Vale no próximo deploy ou reinício da app. ## Acesso externo [#acesso-externo] Desligado por padrão. Ao ligar (`PUT …/public` com `{ enabled: true, allowedCidrs }`): * **Qualquer IP:** deixe `allowedCidrs` vazio. Conecta de qualquer lugar, com a senha do banco. * **Só IPs específicos (mais seguro):** informe as faixas, como `["203.0.113.10/32", "198.51.100.0/24"]`. Só elas conectam. * O banco ganha uma porta própria no IP público do servidor. * A conexão é **sempre com TLS**. O certificado é autoassinado: use `sslmode=require` (PostgreSQL), `ssl-mode=REQUIRED` (MySQL), `rediss://` (Redis) ou `tls=true&tlsAllowInvalidCertificates=true` (MongoDB). Conexões sem TLS são recusadas. A URL externa aparece nas credenciais quando o acesso está ligado. ## Backups [#backups] Cada banco tem backup **diário automático**, guardado fora do servidor e cifrado. Na aba **Backups**: * **Fazer backup agora:** um backup manual, no máximo um por hora. * **Restaurar em um banco novo:** cria outro banco com os dados do backup. O original fica intacto. * **Restaurar neste banco:** substitui os dados atuais pelos do backup. Antes, o Hydro faz um backup do estado atual; se ele falhar, nada é restaurado. Guardamos os 7 diários mais recentes, os manuais por 30 dias e os feitos antes de uma restauração por 7 dias. Ao excluir o banco, os backups ainda ficam 7 dias. O espaço dos backups não conta no plano. ## Logs e consumo [#logs-e-consumo] Logs e consumo ao vivo funcionam como nas aplicações (veja [Logs, métricas, terminal e arquivos](/docs/aplicacoes-runtime)). Bancos não têm terminal nem aba de arquivos. ## API [#api] | Método | Caminho | | ------------------------ | --------------------------------------------------------- | | `GET`, `POST` | `/v1/workspaces/{id}/databases` | | `GET`, `PATCH`, `DELETE` | `/v1/workspaces/{id}/databases/{databaseId}` | | `POST` | `…/{databaseId}/start`, `stop`, `restart` | | `GET` | `…/{databaseId}/credentials` | | `POST` | `…/{databaseId}/rotate-password` | | `POST` | `…/{databaseId}/connect` com `{ appId, variable? }` | | `PUT` | `…/{databaseId}/public` com `{ enabled, allowedCidrs }` | | `GET`, `POST` | `…/{databaseId}/backups` e `…/backups/{backupId}/restore` | Erros específicos: `name_taken`, `workspace_node_full` (sem espaço para o banco), `database_not_ready` (banco ainda sendo criado, ou parado ao trocar a senha) e `public_port_exhausted`. --- # Cobrança URL: https://0.0.0.0:3002/docs/cobranca Assinar um plano, pagar com Pix, boleto ou cartão, mudar de plano e o que acontece quando uma fatura atrasa. O Hydro cobra por assinatura mensal, processada pelo **Asaas**. Cada fatura tem uma página de pagamento onde você escolhe **Pix, boleto ou cartão**. ## Assinar [#assinar] Em **Plano e uso**, o owner do workspace escolhe um plano e clica em **Assinar**. 1. Na primeira vez, informe os dados de cobrança: nome ou razão social, CPF ou CNPJ e e-mail. Eles vão para o Asaas, que emite as cobranças. 2. A página de pagamento da primeira fatura abre numa aba nova. 3. **O plano passa a valer quando o pagamento é confirmado.** Até lá, o workspace continua como estava (em teste, por exemplo). Cartão e Pix confirmam em instantes; boleto, em até 3 dias úteis. As próximas faturas chegam todo mês, no mesmo dia, por e-mail do Asaas. ## Mudar de plano [#mudar-de-plano] * **Para um plano maior:** os limites novos valem na hora. A diferença dos dias que faltam no período vem numa cobrança à parte, com vencimento em 3 dias (abaixo de R$ 5,00 não há cobrança). As próximas faturas já vêm com o valor novo. * **Para um plano menor:** vale no próximo ciclo. Só é possível se o que está em uso couber no plano novo (`409 plan_usage_exceeds`, com o que reduzir). ## Cancelar [#cancelar] O cancelamento vale no **fim do período pago**, e até lá dá para desfazer. Depois, o workspace entra na mesma carência do fim do teste: os recursos continuam rodando por alguns dias, sem criar nem aumentar nada, e depois param. Nada é apagado. ## Fatura em atraso [#fatura-em-atraso] 1. No vencimento sem pagamento, o workspace fica **em atraso**, com aviso no painel e por e-mail. 2. Durante a **carência de 7 dias**, tudo continua rodando e os deploys funcionam, mas não dá para criar recursos nem aumentar o tamanho (`403 billing_past_due`). 3. No fim da carência, **apps e bancos param**. Nada é apagado. 4. **Pagou, volta:** o que parou por falta de pagamento religa sozinho assim que o pagamento é confirmado. O mesmo vale para o fim do período de teste: apps e bancos param no fim da carência e religam quando um plano é assinado. ## Quem pode [#quem-pode] | Ação | Papéis | | --------------------------------- | ------------- | | Ver plano, assinatura e faturas | Owner e admin | | Assinar, mudar de plano, cancelar | Só o owner | ## API [#api] | Método | Caminho | | ------ | ------------------------------------------------ | | `GET` | `/v1/workspaces/{id}/billing` | | `POST` | `…/billing/subscribe` com `{ planId, profile? }` | | `POST` | `…/billing/change-plan` com `{ planId }` | | `POST` | `…/billing/cancel` e `…/billing/cancel/revert` | Erros específicos: `billing_not_configured` (cobrança desligada na plataforma), `billing_gateway_error` (o Asaas não respondeu), `billing_active`, `billing_pending_payment`, `billing_profile_required`, `billing_past_due`, `plan_usage_exceeds`, `plan_not_purchasable` e `same_plan`. --- # Hospedar um bot do Discord URL: https://0.0.0.0:3002/docs/guias/bot-discord Coloque um bot feito com discord.js ou discord.py online 24/7 no Hydro, com o token como segredo. Um bot do Discord abre uma conexão com o gateway do Discord e fica ouvindo eventos. Ele não recebe conexões, então roda como **worker**: um processo contínuo, sem porta pública, que o Hydro reinicia se cair. ## Antes de começar [#antes-de-começar] * O token do bot, em **Discord Developer Portal → Applications → seu app → Bot → Reset Token**. * Os **Privileged Gateway Intents** que o bot usa ligados na mesma página (por exemplo, *Message Content Intent* para ler mensagens). * O código num repositório do GitHub ou num `.zip` (sem `node_modules` nem `.venv`). ## 1. Leia o token da variável de ambiente [#1-leia-o-token-da-variável-de-ambiente] Nunca deixe o token no código. Leia de `DISCORD_TOKEN`: ```js title="index.js (discord.js)" import { Client, Events, GatewayIntentBits } from 'discord.js'; const client = new Client({ intents: [GatewayIntentBits.Guilds] }); client.once(Events.ClientReady, (ready) => console.log(`Online como ${ready.user.tag}`)); client.login(process.env.DISCORD_TOKEN); ``` ```python title="main.py (discord.py)" import os import discord client = discord.Client(intents=discord.Intents.default()) @client.event async def on_ready(): print(f"Online como {client.user}") client.run(os.environ["DISCORD_TOKEN"]) ``` ## 2. Garanta o comando de início [#2-garanta-o-comando-de-início] * **Node.js:** um script `start` no `package.json`, como `"start": "node index.js"`. O gerenciador (npm, pnpm, yarn ou bun) vem do lockfile. * **Python:** um `main.py` na raiz e as dependências no `requirements.txt` (ou `pyproject.toml` com Poetry ou uv). ## 3. Crie o worker [#3-crie-o-worker] Em **Aplicações → Criar aplicação**, escolha a origem (GitHub, `.zip` ou imagem Docker), o tipo **Worker** e a memória. Bots pequenos costumam rodar bem com 128 a 256 MB; acompanhe o consumo em **Métricas** e ajuste. ## 4. Cadastre o token e faça o deploy [#4-cadastre-o-token-e-faça-o-deploy] Na aba **Variáveis**, crie `DISCORD_TOKEN` marcada como **segredo** e faça o deploy. Nos **Logs**, aparece a linha `Online como …` quando o bot conecta. ## Erros comuns [#erros-comuns] | Sintoma | Causa | | -------------------------------- | ------------------------------------------------------------------------------ | | `Used disallowed intents` | O código pede um intent privilegiado que não está ligado no Developer Portal | | `An invalid token was provided` | Token errado ou com espaço sobrando na variável. Gere outro e atualize | | O bot conecta e não lê mensagens | Falta o *Message Content Intent*, no código e no portal | | A app fica `crashed` | O processo caiu 5 vezes em 10 minutos. As últimas linhas do log mostram o erro | ## Dados do bot [#dados-do-bot] Arquivos que o bot grava, como um banco SQLite, ficam na **pasta do projeto**, que continua entre deploys e entra no backup diário ([detalhes](/docs/aplicacoes)). Para muitos acessos simultâneos, use um [banco gerenciado](/docs/bancos-de-dados). --- # Hospedar um bot do WhatsApp URL: https://0.0.0.0:3002/docs/guias/bot-whatsapp Rode um bot com Baileys 24/7 no Hydro sem perder a sessão a cada deploy. O ponto delicado de um bot do WhatsApp fora do computador é a **sessão**: se ela se perde, o bot pede o QR code de novo. No Hydro, grave a sessão na **pasta do projeto**, que continua entre deploys e reinícios e entra no backup diário. ## 1. Grave a sessão numa pasta do projeto [#1-grave-a-sessão-numa-pasta-do-projeto] Com o Baileys, use um caminho relativo, dentro do projeto: ```js title="index.js" import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys'; const { state, saveCreds } = await useMultiFileAuthState('./auth'); const sock = makeWASocket({ auth: state, printQRInTerminal: true }); sock.ev.on('creds.update', saveCreds); ``` Não versione a pasta `auth` no Git: ela é criada no servidor no primeiro acesso. ## 2. Crie o worker [#2-crie-o-worker] Em **Aplicações → Criar aplicação**, escolha a origem, o tipo **Worker** e a memória. O comando de início é o script `start` do `package.json`. ## 3. Faça o deploy e escaneie o QR code [#3-faça-o-deploy-e-escaneie-o-qr-code] Faça o deploy e abra os **Logs**: o QR code impresso pela biblioteca aparece ali. No celular do número do bot, abra **Aparelhos conectados → Conectar um aparelho** e escaneie. Nos próximos deploys e reinícios, o bot reaproveita a sessão da pasta `auth`. ## whatsapp-web.js [#whatsapp-webjs] O whatsapp-web.js controla um Chromium. Publique uma **imagem Docker** com o navegador e as bibliotecas dele (origem *Imagem Docker*) e reserve mais memória. Guarde a pasta `.wwebjs_auth` dentro do projeto do mesmo jeito. ## Boas práticas [#boas-práticas] * Bibliotecas não oficiais podem levar a bloqueios do número se o uso violar os termos do WhatsApp. Para uso comercial em escala, prefira a API oficial (Cloud API), que recebe webhooks: rode como aplicação **Web**, com HTTPS automático. * O celular principal precisa entrar na conta de tempos em tempos para manter os aparelhos conectados. * Chaves de outros serviços (OpenAI, banco) vão como [variáveis secretas](/docs/guias/variaveis-e-tokens). --- # Hospedar um bot do Telegram URL: https://0.0.0.0:3002/docs/guias/bot-telegram Long polling como worker ou webhook com HTTPS automático, com Telegraf, grammY, python-telegram-bot ou aiogram. O Telegram entrega as mensagens de dois jeitos, e o Hydro tem um tipo de aplicação para cada um: | Modo | Tipo da aplicação | Quando usar | | ---------------- | ----------------- | ------------------------------------------------------------------------ | | **Long polling** | Worker | O mais simples: o bot busca mensagens novas o tempo todo, sem endereço | | **Webhook** | Web | Bots muito usados: o Telegram chama o seu endereço HTTPS a cada mensagem | ## Long polling (worker) [#long-polling-worker] ```python title="main.py (python-telegram-bot)" import os from telegram.ext import Application, CommandHandler async def start(update, context): await update.message.reply_text("Olá!") app = Application.builder().token(os.environ["TELEGRAM_BOT_TOKEN"]).build() app.add_handler(CommandHandler("start", start)) app.run_polling() ``` Crie uma aplicação **Worker**, cadastre `TELEGRAM_BOT_TOKEN` como segredo em **Variáveis** e faça o deploy. Workers rodam uma cópia por vez, inclusive no deploy, então não aparece o erro `Conflict: terminated by other getUpdates request`. Se ele aparecer, o bot está rodando também em outro lugar (como o seu computador). ## Webhook (web) [#webhook-web] 1. Crie uma aplicação **Web**. Ela ganha um endereço `https://.acq.lat` com HTTPS automático (ou use um [domínio próprio](/docs/aplicacoes#domínios-personalizados)). 2. A aplicação precisa escutar em `0.0.0.0`, na porta da variável `PORT`. 3. Depois do deploy, registre o endereço no Telegram: ```bash curl "https://api.telegram.org/bot/setWebhook?url=https://.acq.lat/webhook" ``` Use um caminho difícil de adivinhar ou o parâmetro `secret_token` do `setWebhook` para conferir que a chamada veio do Telegram. ## Bibliotecas [#bibliotecas] * **Node.js:** Telegraf ou grammY, iniciados pelo script `start` do `package.json`. * **Python:** python-telegram-bot ou aiogram, com um `main.py` na raiz e as dependências no `requirements.txt`. --- # Variáveis de ambiente e tokens URL: https://0.0.0.0:3002/docs/guias/variaveis-e-tokens Como guardar o token do bot, senhas e chaves de API com segurança no Hydro. Token de bot, senha de banco e chave de API nunca devem ficar no código nem no repositório. No Hydro, eles vão como **variáveis de ambiente** da aplicação. ## Cadastrar [#cadastrar] Na aba **Variáveis** da aplicação: * crie cada variável com nome e valor, ou **importe um `.env`** inteiro; * marque como **segredo** o que não deve aparecer de novo: o valor fica cifrado com AES-256-GCM e não é mostrado de volta, nem no painel nem na API. As variáveis chegam à aplicação como variáveis de ambiente comuns. Elas não entram no build, só no processo em execução. ## Ler no código [#ler-no-código] | Linguagem | Como ler | | --------- | ----------------------------- | | Node.js | `process.env.DISCORD_TOKEN` | | Python | `os.environ["DISCORD_TOKEN"]` | | Go | `os.Getenv("DISCORD_TOKEN")` | ## Trocar um token vazado [#trocar-um-token-vazado] 1. Gere um token novo no serviço (Discord Developer Portal, BotFather, painel da API). 2. Atualize a variável no Hydro. 3. Reinicie a aplicação ou faça um deploy para o processo ler o valor novo. ## O que não fazer [#o-que-não-fazer] * Não versione o `.env`: coloque-o no `.gitignore`. * Não imprima o token nos logs. * Não compartilhe o mesmo token entre bots diferentes. --- # Convenções da API URL: https://0.0.0.0:3002/docs/api Endereço, versionamento, formato, erros, paginação, idempotência e limites. Base URL: `https://api.acquacloud.com.br`. Tudo o que está documentado aqui funciona com um [token de API](/docs/api/autenticacao). A lista completa de rotas está na [referência](/docs/api/referencia). ## Versionamento [#versionamento] Toda rota começa com a versão: `/v1/...`. Mudanças incompatíveis criam `/v2/...`, e a versão anterior continua disponível até ser descontinuada com aviso prévio. Campos novos em respostas não contam como mudança incompatível: ignore campos desconhecidos. ## Formato [#formato] * JSON em UTF-8. Corpo de até 100 KB (uploads de .zip e de arquivos têm limites próprios). * Datas em ISO 8601 com fuso (`2026-10-06T22:00:00.000Z`). * IDs são UUIDv7. * Toda resposta traz `X-Request-Id`. Você pode enviar o seu (8 a 64 caracteres `[A-Za-z0-9_-]`) para correlacionar com os seus logs. ## Erros [#erros] Erros seguem a [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457), com `Content-Type: application/problem+json`: ```json { "title": "Alguns campos estão inválidos.", "status": 400, "code": "validation_failed", "requestId": "0b9f…", "errors": [{ "path": "memoryMb", "message": "Mínimo: 100 MB", "code": "custom" }] } ``` Baseie a lógica no campo **`code`**, que é estável. `title` e `detail` são textos para pessoas e podem mudar. Veja os [códigos de erro](/docs/api/erros). Um recurso de outro workspace responde **404**, não 403: a API não revela a existência de recursos aos quais você não tem acesso. ## Paginação [#paginação] Listas grandes usam cursor: `?limit=25&cursor=…`, com a resposta `{ "data": [...], "nextCursor": "…" }`. `limit` vai de 1 a 100 (padrão 25). Quando `nextCursor` vier `null`, não há mais páginas. ## Idempotência [#idempotência] Requisições `POST` aceitam o header `Idempotency-Key` (8 a 200 caracteres) para retentativas seguras: | Situação | Resposta | | -------------------------------------- | ---------------------------------------------------- | | Mesma chave e mesmo corpo | A resposta original, com `Idempotent-Replayed: true` | | Mesma chave e corpo diferente | `422 idempotency_conflict` | | Primeira requisição ainda em andamento | `409 idempotency_in_progress` | | Erro na primeira tentativa | A chave é liberada para nova tentativa | As chaves valem por 24 horas, por token. ## Limites de requisição [#limites-de-requisição] * 300 requisições por minuto por IP, com os headers `X-RateLimit-Limit`, `X-RateLimit-Remaining` e `X-RateLimit-Reset`. * Ao exceder, a API responde `429 rate_limited` com `Retry-After`. --- # Autenticação URL: https://0.0.0.0:3002/docs/api/autenticacao Tokens de API por workspace, escopos e boas práticas. A API pública usa **tokens de API**. Crie um em **Workspace → Tokens de API** no painel. O valor completo (`hydro_pat_…`) aparece **uma única vez**. Envie-o no header `Authorization`: ```bash curl https://api.acquacloud.com.br/v1/workspaces \ -H "Authorization: Bearer hydro_pat_XXXXXXXX" ``` ## Escopo e permissões [#escopo-e-permissões] * **Um workspace:** o token só enxerga o workspace em que foi criado. Os outros respondem 404. * **Escopos:** `read` (consultas) e `write` (alterações). * **Permissão efetiva:** o escopo do token limitado ao papel **atual** de quem o criou. Se a pessoa for rebaixada, o token perde o acesso na hora; se ela sair do workspace, o token é revogado. * **Validade:** opcional, de 1 a 365 dias. O painel mostra o último uso. ## Boas práticas [#boas-práticas] * Guarde o token num gerenciador de segredos, nunca no código. * Use `read` para integrações que só consultam. * Um token vazado deve ser revogado no painel: a revogação vale na hora. Login, conta, membros, convites, cobrança e a criação de tokens são feitos pelo painel e não fazem parte da API pública. --- # Códigos de erro URL: https://0.0.0.0:3002/docs/api/erros Os códigos estáveis do campo code nos erros da API do Hydro, com a mensagem padrão de cada um e os status HTTP mais comuns. O campo `code` dos erros é estável e deve guiar a lógica do seu cliente. A tabela é gerada a partir do código-fonte e está sempre atualizada. | code | Mensagem padrão | |---|---| | `validation_failed` | Alguns campos estão inválidos. | | `bad_request` | Requisição inválida. | | `unauthenticated` | Faça login para continuar. | | `forbidden` | Você não tem permissão para esta ação. | | `not_found` | Recurso não encontrado. | | `conflict` | O recurso foi alterado por outra operação. Tente novamente. | | `rate_limited` | Muitas tentativas. Aguarde um pouco e tente novamente. | | `internal_error` | Erro interno. Tente novamente em instantes. | | `invalid_token` | Link inválido ou expirado. | | `workspace_suspended` | Este workspace está suspenso. | | `workspace_limit_reached` | Você atingiu o limite de workspaces. | | `slug_taken` | Este identificador já está em uso. | | `last_owner` | O workspace precisa de pelo menos um proprietário. | | `role_not_allowed` | Você não pode atribuir este papel. | | `member_limit_reached` | O workspace atingiu o limite de membros. | | `ingress_node_exists` | Já existe um ingresso central. Crie o node sem ingresso e faça a troca na página dele, depois que ele estiver no ar. | | `agent_unavailable` | O binário do agente não está disponível neste servidor. | | `payload_too_large` | Conteúdo maior que o permitido. | | `quota_exceeded` | O workspace não tem recursos disponíveis no plano para este pedido. | | `resource_below_minimum` | O tamanho pedido está abaixo do mínimo para este tipo de recurso. | | `plan_required` | O workspace precisa de um plano ativo para criar ou aumentar recursos. | | `plan_archived` | Este plano está arquivado. | | `plan_order_stale` | O catálogo mudou enquanto você reordenava. Recarregue e tente de novo. | | `app_slug_taken` | Já existe uma aplicação com este identificador no workspace. | | `deployment_in_progress` | Já há um deploy em andamento para esta aplicação. | | `artifact_too_large` | O arquivo passa do tamanho máximo permitido. | | `artifact_invalid` | Envie um arquivo .zip válido com o código da aplicação. | | `artifact_has_node_modules` | O .zip tem a pasta node_modules. Remova-a e envie de novo: as dependências são instaladas no build. | | `image_not_found` | Imagem não encontrada ou não é pública. | | `rollback_unavailable` | A imagem deste deploy não está mais disponível para rollback. | | `app_not_deployed` | A aplicação ainda não tem um deploy no ar. | | `build_failed` | O build falhou. Veja os logs do deploy. | | `workspace_has_apps` | Exclua as aplicações do workspace antes de excluí-lo. | | `apps_unavailable` | Aplicações indisponíveis: a plataforma ainda não foi configurada para elas. | | `github_installation_not_found` | Conta do GitHub não encontrada ou sem acesso neste workspace. | | `github_repository_not_found` | Repositório não encontrado ou fora do acesso do Hydro. | | `github_unavailable` | O GitHub não respondeu. Tente de novo em instantes. | | `cloudflare_token_invalid` | O token da Cloudflare é inválido ou não tem acesso à zona do domínio das aplicações. | | `cloudflare_unavailable` | A Cloudflare não respondeu. Tente de novo em instantes. | | `app_not_running` | A aplicação não está rodando. Inicie ou faça um deploy primeiro. | | `file_not_found` | Arquivo ou pasta não encontrado. | | `file_too_large` | Arquivo grande demais. | | `file_exists` | Já existe um arquivo ou pasta com esse nome. | | `path_invalid` | Caminho inválido. | | `directory_not_empty` | A pasta não está vazia. | | `name_taken` | Já existe uma aplicação ou banco com esse nome interno neste workspace. | | `workspace_node_full` | O servidor deste workspace não tem espaço para esse tamanho. Reduza o tamanho ou fale com o suporte. | | `workspace_node_unavailable` | O servidor deste workspace está indisponível agora (manutenção ou fora do ar). Tente de novo em instantes ou fale com o suporte. | | `workspace_node_no_ingress` | O servidor deste workspace não recebe tráfego web, então não roda aplicações web. Use um worker ou fale com o suporte. | | `public_port_exhausted` | Não há portas livres para acesso externo neste servidor. | | `database_not_ready` | O banco ainda não está pronto. Tente de novo em instantes. | | `domain_taken` | Esse domínio já está em uso na plataforma. | | `subdomain_taken` | Esse endereço em acq.lat já está em uso. Escolha outro. | | `domain_invalid` | Domínio inválido para uso com o Hydro. | | `domain_not_verified` | O DNS do domínio ainda não aponta para o Hydro. | | `billing_gateway_error` | O serviço de pagamentos não respondeu. Tente de novo em instantes. | | `billing_active` | Este workspace tem uma assinatura em andamento. Altere o plano pela cobrança. | | `billing_pending_payment` | A assinatura aguarda o primeiro pagamento. | | `billing_profile_required` | Informe os dados de cobrança (nome, CPF ou CNPJ e e-mail). | | `billing_no_subscription` | Este workspace não tem assinatura. | | `backup_in_progress` | Já há um backup ou uma restauração em andamento para este recurso. | | `backup_not_ready` | Este backup ainda não está pronto para restaurar. | | `backup_rate_limited` | Faça no máximo um backup manual por hora. | | `billing_past_due` | Há uma fatura em atraso. Pague para criar recursos ou religar os que pararam. | | `plan_usage_exceeds` | O uso atual não cabe neste plano. Reduza os recursos antes de mudar. | | `plan_not_purchasable` | Este plano não está disponível para contratação. | | `same_plan` | O workspace já está neste plano. | | `idempotency_conflict` | Esta Idempotency-Key já foi usada com outro conteúdo. | | `idempotency_in_progress` | Uma requisição com esta Idempotency-Key ainda está em processamento. | ## Status HTTP mais comuns [#status-http-mais-comuns] | Status | Quando | | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 400 | Corpo inválido (`validation_failed`, `domain_invalid`) ou .zip com `node_modules` (`artifact_has_node_modules`) | | 401 | Sem token válido (`unauthenticated`) | | 403 | Sem permissão (`forbidden`, `role_not_allowed`), plano exigido (`plan_required`) ou workspace suspenso | | 404 | Recurso inexistente **ou** de outro workspace | | 409 | Conflito de estado (`name_taken`, `subdomain_taken`, `domain_taken`, `quota_exceeded`, `deployment_in_progress`, `database_not_ready`, `backup_in_progress`) | | 422 | Reuso de `Idempotency-Key` com outro corpo; .zip ilegível ou inválido (`artifact_invalid`) | | 429 | Limite de requisições |