Provedor Garage — arquivos (bucket + key + presign)¶
Status: Aprovado · Responsável: Gustavo Madruga · Atualizado em: 2026-07-03
O provider de arquivos da Central de Apps: por app, provisiona 1 bucket (globalAlias =
app_id) + 1 access key read/write escopada, no cluster Garage (S3-compatível, Admin API v2).
O app acessa os arquivos por URLs presignadas que o auth assina (broker-signed) — a write-key
nunca sai do servidor. Porquê e alternativas: decisão 0012.
Envs no recurso auth (Coolify)¶
Lidas em runtime pelo auth (não build). Marque o token como Secret.
| Variável | Valor | Secret? |
|---|---|---|
GARAGE_ADMIN_URL |
Admin API interna do Garage (ex. http://garage:3903) ou https://arquivo-admin.xadm.biz |
não |
GARAGE_ADMIN_TOKEN |
admin token (o GARAGE_ADMIN_TOKEN/SERVICE_PASSWORD_GARAGE do cluster) |
sim ✅ |
GARAGE_S3_ENDPOINT |
endpoint S3 (ex. https://arquivo-api.xadm.biz) — usado no presign |
não |
GARAGE_S3_REGION |
região SigV4 (s3_region do garage.toml, ex. garage) |
não |
Ausentes → o provider recusa provisionar com erro claro (não quebra o boot). O cluster precisa de
GARAGE_ALLOW_WORLD_READABLE_SECRETS=true para o auth re-obter o secret na re-provisão
(GetKeyInfo?showSecretKey=true).
Provisão (idempotente)¶
Disparada pelo /xadm-setup (feature garage) → POST /api/setup/provision. O provider:
GET /v2/GetBucketInfo?globalAlias=<app_id>— acha o bucket, ouPOST /v2/CreateBucket.- Reusa a key associada ao bucket (
GetKeyInfo?showSecretKey=true) — ouPOST /v2/CreateKey+POST /v2/AllowBucketKey(read+write,owner:false). - Grava
client_config = {endpoint, bucket, region}(→app.json) e osecretAccessKeycifrado emsecret_ref. Re-rodar reusa bucket+key (sem duplicar).
Acesso a arquivos (presign broker-signed)¶
O app (com JWT de usuário do auth, claim oauth_app_id) chama:
GET /api/apps/garage/presign?app_id=<app>&op=GET|PUT&key=<objeto>
Authorization: Bearer <JWT do usuário>
→ 200 { "url": "<presigned SigV4, 10 min>", "expires_in": 600 }
- op=GET → download; op=PUT → upload. A URL vale ~10 min.
- Autz:
app_idtem que casar com ooauth_app_iddo token → 403 cross-app. Token semoauth_app_id→ 403. Sem token → 401. App sem Garage provisionado → 404.
Diagnóstico¶
- Provisão falha → a resposta do
/provisiontrazproviders[].last_error(ex.Garage GetBucketInfo HTTP 401= token inválido;Garage inacessível= URL/rede). - Presign 404 → o par (app,
garage) não estáligadano catálogo (rodar o setup). - Presign dá URL mas o objeto não abre → conferir
GARAGE_S3_ENDPOINT/GARAGE_S3_REGION(região SigV4 tem que casar com ogarage.toml) e se o cluster aceita path-style.
Acesso S3 direto (app server/web)¶
Para app server/web (tem production_url → recurso Coolify), o broker injeta a write-key no
recurso do app, além do client-grade — o app fala S3 direto (sem o round-trip de presign). Envs
que o app recebe:
| Env injetada | Origem | Secret? |
|---|---|---|
GARAGE_ENDPOINT |
client_config.endpoint |
não |
GARAGE_BUCKET |
client_config.bucket (= app_id) |
não |
GARAGE_REGION |
client_config.region (SigV4) |
não |
GARAGE_ACCESS_KEY_ID |
resource_ref.access_key_id |
não |
GARAGE_SECRET_ACCESS_KEY |
secret_ref (decifrada no ato) |
sim ✅ (mascarada por is_shown_once) |
O app configura um cliente S3 (path-style, endpoint override) com essas envs. A key é per-app,
escopada só ao bucket do app (read+write, sem owner). Rotação: re-rodar /xadm-setup (re-injeta) +
reiniciar o recurso. O secret entra só no Coolify, nunca no app.json.
Mobile/local (sem production_url) não recebe a write-key → usa o presign broker-signed
(acima), com o segredo só no auth. Presign para device, S3 direto para server — coexistem. Ver
decisão 0012.