Pular para o conteúdo

Sugestões de persistência

A plataforma observa seus apps e, quando percebe que um deles precisa guardar dados mas não tem onde, cria uma sugestão: uma mensagem curta explicando o problema e oferecendo a solução. A plataforma nunca cria o recurso sozinha — ela sugere e espera você autorizar, seja uma pessoa no painel ou um agent pela API. É o mesmo princípio em todo lugar: a Nebular detecta, propõe e só age com sua permissão.

Dois gatilhos geram sugestões:

  1. Detecção no build. Ao preparar o deploy, a plataforma olha as dependências do app (o driver de banco ou o ORM que ele usa) e deduz o tipo de persistência que ele vai querer. Por exemplo: um app Node com better-sqlite3, ou um app Go com modernc.org/sqlite, embarca um banco SQLite e vai precisar de disco persistente.
  2. Falha no deploy (sinal mais preciso). Quando o app sobe e quebra por falta de persistência, o erro diz exatamente o que faltou:
    • activate_readonly_write: o app tentou gravar num caminho somente-leitura (por exemplo /app/data). A sugestão já vem com o caminho exato.
    • Conexão recusada a um banco (activate_app_crashed cujo log mostra “connection refused” numa porta de banco) e o serviço não tem banco ligado.
    • unresolved_reference: o deploy aponta para um serviço de banco que não existe.

A sugestão não atrapalha o deploy: ela é informativa e fica guardada até você aceitar ou descartar. Se o recurso continua faltando, o deploy segue falhando com a mesma mensagem de sempre — nada é escondido nem quebrado.

A diferença mais importante é o que o app precisa:

  • SQLite embarcado → volume. O app já tem o banco dentro dele (um arquivo SQLite); só falta um disco que sobreviva aos deploys. A sugestão oferece criar um volume no caminho onde o app grava (por exemplo /app/data). Nada muda no código do app.
  • Driver externo (PostgreSQL, MySQL, MongoDB) → banco gerenciado. O app fala com um banco que roda fora dele e que ainda não existe. A sugestão oferece criar um banco gerenciado e ligar automaticamente com a referência ${{<db>.DATABASE_URL}} (onde <db> é o nome do serviço-banco criado). Veja como essa ligação funciona em Conexão e referências.

Em ambos os casos a Nebular corrige a si mesma para aceitar o app como ele é: ela nunca edita o código do seu app.

Toda sugestão nasce no estado “sugerida” e fica parada até uma decisão:

  • Aceitar cria o recurso (volume ou banco) e o liga ao serviço. O próximo deploy já usa.
  • Descartar arquiva a sugestão. Ela não volta a nascer pelo mesmo sinal na mesma janela (sem spam).

Quem decide é você: uma pessoa no painel ou um agent pela API. Enquanto ninguém autoriza, nada é criado e nenhum dado é tocado.

Liste as sugestões abertas de um projeto com o escopo projects:read (Tokens e escopos):

Janela do terminal
curl -sS "$NEBULAR_API/v1/projects/$P/suggestions" \
-H "Authorization: Bearer $NEBULAR_TOKEN"

Cada item traz o diagnóstico já pronto para mostrar a um humano, no campo detail:

{
"items": [
{
"id": "01j9zksf51s25jd7735q8fefyf",
"service_id": "01j9zktb8c3m0xr4v6p2n1qd7e",
"service_name": "polylang",
"project_id": "01j9zkt0a2b3c4d5e6f7g8h9jk",
"kind": "volume",
"engine": "",
"target_path": "/app/data",
"source": "runtime_readonly",
"status": "suggested",
"detail": "Este app guarda dados em /app/data, mas o disco do app é somente-leitura. Posso criar um volume persistente em /app/data para os dados sobreviverem aos deploys. Criar agora?",
"created_at": "2026-10-12T12:00:00Z"
}
]
}

Os campos de cada sugestão:

Campo O que é
id identificador da sugestão (use-o para descartar ou aceitar)
service_id, service_name o serviço que precisa de persistência
project_id o projeto do serviço
kind volume (disco persistente) ou database (banco gerenciado)
engine motor do banco (sqlite, postgres, mysql, mongo) ou vazio quando é volume
target_path caminho do volume (ex.: /app/data); nulo quando é banco
source o gatilho: build_detect, runtime_readonly, runtime_connrefused ou unresolved_ref
status suggested, dismissed, accepted ou fulfilled
detail mensagem em linguagem simples, pronta para a tela
created_at quando a sugestão foi criada

Para arquivar uma sugestão que não faz sentido, use o escopo projects:write:

Janela do terminal
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/suggestions/$SID/dismiss" \
-H "Authorization: Bearer $NEBULAR_TOKEN"

A sugestão passa a dismissed e não reaparece pelo mesmo sinal na mesma janela.

Aceitar uma sugestão — ou seja, de fato criar o volume ou o banco e ligá-lo ao serviço — está sendo finalizado e ainda não está disponível nesta versão da API. A rota prevista é:

POST /v1/projects/{pid}/suggestions/{sid}/accept (escopo de escrita)

O formato exato do corpo dessa chamada ainda não está definido e será documentado aqui quando a criação por aceite entrar no ar. Até lá, você pode criar o recurso sugerido pela rota direta: um volume para o caso SQLite embarcado, ou um banco gerenciado com a referência de conexão para o caso de driver externo.

Tudo o que o painel mostra e faz com sugestões, um agent faz pela API /v1 — listar e descartar hoje, aceitar quando a criação por aceite entrar. Veja também o Contrato de App Nebular, que lista os erros de deploy que disparam estas sugestões, e o cookbook Como corrigir cada erro de deploy com o remédio de cada código. Quando a sugestão é um banco e o app usa SQLite embarcado, uma alternativa ao volume é migrar para libSQL gerenciado.