# Variáveis e referências

Escopos: `env:read`, `env:write`, `env:reveal` ([Tokens e escopos](/seguranca/tokens-e-escopos/)).

## Escopos de variável

- **`service`**: vale só para um serviço. Rota: `/v1/projects/{p}/services/{s}/variables[/{name}]`.
- **`shared`**: guardada no projeto. Rota: `/v1/projects/{p}/shared-variables[/{name}]`. **Ela não entra sozinha no ambiente de nenhum serviço**: só chega a um serviço que a referencie com `${{shared.NOME}}` (veja abaixo).

Valor literal ou com **referências**: `${{<servico>.<VAR>}}` (variável de outro serviço do projeto) e `${{shared.VAR}}` (variável compartilhada). `shared` é nome reservado: nenhum serviço pode se chamar assim.

## Definir, ler e apagar

```bash
SUF=$(date +%s)-$RANDOM
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var-proj-$SUF" -d "{\"name\": \"vars-$SUF\"}" | jq -r .id)
S=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var-svc-$SUF" \
  -d "{\"name\": \"api\", \"kind\": \"app\", \"visibility\": \"private\", \"source\": {\"type\": \"image\", \"ref\": \"ghcr.io/org/api:1.0\"}}" | jq -r .id)
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/$S/variables/LOG_LEVEL" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "info"}'
```
```json
{"name": "LOG_LEVEL", "value": "info", "sensitive": false, "needs_redeploy": []}
```

O corpo do `PUT` é `{"value": "...", "sensitive": true|false}` (`sensitive` é opcional; uma variável já sensível nunca volta a ser não sensível). A resposta traz a variável e `needs_redeploy`, a lista de serviços que precisam de novo deploy para ver o valor (vazia quando nenhum serviço usa a variável). Variável **sensível**: o `PUT` nunca devolve o valor (`"value": null`) e as leituras só o mostram a um token com `env:reveal`; sem ele, `value` vem `null` e `sensitive` vem `true`.

Para definir várias de uma vez (tudo ou nada, de 1 a 200 itens), use `PUT .../variables` com `{"variables": [{"name": "A", "value": "1"}, {"name": "B", "value": "2", "sensitive": true}]}`; a resposta é `{"items": [...], "needs_redeploy": [...]}`. `GET .../variables?resolved=true` devolve as variáveis já com as referências expandidas e as automáticas (exige `env:reveal`).

Variável compartilhada do projeto:

```bash
SUF=$(date +%s)-$RANDOM
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var2-proj-$SUF" -d "{\"name\": \"vars2-$SUF\"}" | jq -r .id)
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/shared-variables/REGION" \
  -H "Authorization: Bearer $NEBULAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": "br"}'
```

Para que o serviço enxergue a variável compartilhada `REGION`, crie nele uma variável própria que a referencie (o nome pode ser o mesmo ou outro). Sem essa referência, `REGION` não aparece no ambiente do serviço:

```json
PUT /v1/projects/{p}/services/{s}/variables/REGION
{"value": "${{shared.REGION}}"}
```

Referência a outro serviço (o front lê a URL privada do back):

```bash
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var3-proj-$SUF" -d "{\"name\": \"vars3-$SUF\"}" | jq -r .id)
curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services" -H "$H" -H "Content-Type: application/json" \
  -d '{"name": "api", "kind": "app", "visibility": "private", "port": 8080, "source": {"type": "image", "ref": "ghcr.io/org/api:1.0"}}' >/dev/null
S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \
  -d "{\"visibility\": \"public\", \"slug\": \"vars3-$SUF\", \"source\": {\"type\": \"image\", \"ref\": \"ghcr.io/org/web:1.0\"}}" | jq -r .id)
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/$S/variables/API_URL" -H "$H" \
  -H "Content-Type: application/json" -d '{"value": "${{api.PRIVATE_URL}}"}'
```

Listar (valores sensíveis vêm mascarados sem `env:reveal`) e apagar:

```bash
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var4-proj-$SUF" -d "{\"name\": \"var4-$SUF\"}" | jq -r .id)
S=$(curl -sS -X POST "$NEBULAR_API/v1/projects/$P/services" -H "$H" -H "Content-Type: application/json" \
  -H "Idempotency-Key: var4-svc-$SUF" \
  -d '{"name": "app", "kind": "app", "visibility": "private", "source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/$S/variables/LOG_LEVEL" -H "$H" \
  -H "Content-Type: application/json" -d '{"value": "info"}'
curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/variables" -H "$H"
curl -sS -X DELETE "$NEBULAR_API/v1/projects/$P/services/$S/variables/LOG_LEVEL" -H "$H"
```

## Variáveis automáticas (resolvíveis por referência)

Todo serviço ganha, sem você definir:

| Variável | Valor |
|---|---|
| `NEBULAR_SERVICE_NAME` | o `name` do serviço |
| `PORT` | a porta interna |
| `PRIVATE_HOST` | `<name>` (hostname na [rede privada](/conectados/rede-privada/)) |
| `PRIVATE_URL` | `http://<name>:<port>` |
| `PUBLIC_URL` | URL pública, se o serviço é `public`; senão vazio |
| `PUBLIC_DOMAIN` | domínio público do serviço |

Serviços de banco também expõem `DATABASE_URL` e as variáveis do motor ([Conexão e referências](/bancos/conexao-e-referencias/)). Exemplo: `${{api.PRIVATE_URL}}`, `${{db.DATABASE_URL}}`.

Se um app precisa de um banco ou de disco persistente que ainda não existe, a plataforma detecta e sugere criar o recurso (e, no caso de banco, injetar a referência `${{db.DATABASE_URL}}`), sempre esperando sua autorização: veja [Sugestões de persistência](/conectados/sugestoes-de-persistencia/).

## Como a resolução funciona

- Acontece **no deploy**, por serviço. Quem é referenciado sobe antes (ordem topológica no deploy de grupo).
- Referência a variável ou serviço inexistente: o `POST` do deploy responde `422 unresolved_reference` e nada é criado; `details.references` lista os nomes com problema. O `PUT` da variável aceita a referência: o erro só aparece no deploy.
- Referências em ciclo: `422 reference_cycle` (também com `details.references`).
- Outros erros de resolução presentes na API: `invalid_reference` (um `${{...}}` que não é da forma `escopo.VAR`), `reference_too_deep` (aninhamento acima de 16 níveis) e `value_too_large` (valor expandido acima de 64 KiB ou ambiente acima de 256 KiB).
- Valor resolvido **herda a sensibilidade** do que foi referenciado (veja a próxima seção).
- **Re-resolução**: mudar um valor referenciado marca os dependentes como "precisam de redeploy". Faça um novo deploy dos dependentes (ou ative `auto_deploy`).

## Referência a valor sensível

Uma variável cujo valor é uma referência (`DATABASE_URL = ${{db.DATABASE_URL}}`) guarda o **texto da referência**, não o segredo. Por isso:

- O `PUT` pode responder `"sensitive": false` e a leitura sem `resolved` mostra o texto `${{db.DATABASE_URL}}`: nada de segredo trafega ali. Se quiser a variável tratada como sensível já na definição, envie `"sensitive": true` no `PUT` (a resposta volta com `value: null`).
- O **valor resolvido** herda a sensibilidade de quem foi referenciado: `GET ...?resolved=true` mostra `DATABASE_URL` com `"sensitive": true`, e o valor fica mascarado em log e painel.
- Essa leitura resolvida (e `env:reveal` em geral) entrega o segredo **em claro** ao token com o escopo `env:reveal`, que precisa ser pedido explicitamente ao criar o token ([Tokens e escopos](/seguranca/tokens-e-escopos/)). Sem o escopo, `resolved=true` dá `403`. Trate a resposta como segredo: não a registre em log nem a cole em chat.
- `depends_on` do serviço (usado para a ordem do deploy de grupo) é calculado das referências gravadas: o serviço `api` passa a listar `["db"]` na leitura do serviço assim que a variável com `${{db...}}` existe. A resposta de **criação** do serviço, anterior à variável, vem com `depends_on` vazio.

## Ler valores resolvidos

`GET .../variables?resolved=true` devolve os valores já expandidos. Exige o escopo `env:reveal` (token criado com step-up) e devolve valores sensíveis em claro.

```bash
SUF=$(date +%s)-$RANDOM
H="Authorization: Bearer $NEBULAR_TOKEN"
P=$(curl -sS -X POST "$NEBULAR_API/v1/projects" -H "$H" -H "Content-Type: application/json" \
  -d "{\"name\": \"var5-$SUF\"}" | jq -r .id)
S=$(curl -sS -X PATCH "$NEBULAR_API/v1/projects/$P/services/web" -H "$H" -H "Content-Type: application/json" \
  -d '{"source": {"type": "image", "ref": "ghcr.io/org/app:1.0"}}' | jq -r .id)
curl -sS -X PUT "$NEBULAR_API/v1/projects/$P/services/$S/variables/LOG_LEVEL" -H "$H" \
  -H "Content-Type: application/json" -d '{"value": "info"}' >/dev/null

curl -sS "$NEBULAR_API/v1/projects/$P/services/$S/variables?resolved=true" \
  -H "$H"
```
```json
{"items": [{"name": "LOG_LEVEL", "value": "info", "sensitive": false}], "next_cursor": null, "has_more": false}
```

## Se algo falhar

| Resposta | O que fazer |
|---|---|
| `403 forbidden` | Falta `env:write` (escrever), `env:read` (ler) ou `env:reveal` (`resolved=true`). Peça token novo. |
| `404 not_found` | Projeto, serviço ou variável inexistente ou fora do seu acesso. |
| `422 unresolved_reference` no deploy | Crie a variável/serviço citado ou corrija o nome na referência; redeploy. |
| `422 reference_cycle` no deploy | Quebre o ciclo (A referencia B que referencia A). |
| `400 invalid_request` | Nome ou valor inválido: corrija. |
