# 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.

## Quando uma sugestão aparece

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.

## As duas soluções: volume ou banco

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](/conectados/volumes/) 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](/bancos/postgres/) 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](/bancos/conexao-e-referencias/).

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.

## A plataforma nunca cria sozinha

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.

## Como um agent lê as sugestões

Liste as sugestões abertas de um projeto com o escopo `projects:read` ([Tokens e escopos](/seguranca/tokens-e-escopos/)):

```bash
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`:

```json
{
  "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 |

## Como um agent descarta uma sugestão

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

```bash
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 (criar o recurso) ainda vem a seguir

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 é:

```text
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](/conectados/volumes/) para o caso SQLite embarcado, ou um [banco gerenciado](/bancos/postgres/) com a [referência de conexão](/bancos/conexao-e-referencias/) para o caso de driver externo.

## Paridade painel e API

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](/agents/contrato-app/), que lista os erros de deploy que disparam estas sugestões, e o cookbook [Como corrigir cada erro de deploy](/agents/corrigir-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](/agents/corrigir-deploy/#migrar-sqlite-local-para-gerenciado).
