# _OTHER_CONFIGS — variáveis que **não** ficam no `.env`

> O `.env` guarda só o **estritamente necessário** (identificação, segredos e alguns
> toggles). Todo o resto tem **default ideal para desenvolvimento** definido em `config/`
> (e no `docker-compose.yml`, para infraestrutura). Este documento lista esses defaults e
> **quando** sobrepor.

Regra de ouro: **nunca** adicione uma variável ao `.env` sem necessidade real. Se o default
não serve para o seu caso, sobreponha pontualmente (documente o porquê no `.env`).

---

## 0. Perfis por `APP_ENV` (fonte única)

`config/environments.php` define o perfil ativo. Trocar `APP_ENV` ajusta automaticamente:

| Chave | `dev` | `homolog` | `production` |
|---|---|---|---|
| `debug` (Laravel) | `true` | `false` | `false` |
| `force_https` | `false` | `true` | `true` |
| `log_level` | `debug` | `debug` | `info` |
| `log_deprecations_channel` | `stack` | `null` | `null` |
| `log_requests` | `true` | `true` | `true` |
| `db_query_log` | `true` | `false` | `false` |
| `telemetry` | `true` | `true` | `true` |
| OPcache | **desligado** | ligado (revalida) | ligado (`validate_timestamps=0`) |
| `zend.assertions` | `1` | `-1` | `-1` |
| Caches do Laravel (`optimize`) | **desligado** | ligado | ligado |
| `display_errors` / `error_reporting` | `On` / `E_ALL` | `Off` / `E_ALL` | `Off` / `E_ALL & ~E_DEPRECATED` |
| Cookie de sessão `secure` / `encrypt` | `false`/`false` | `true`/`true` | `true`/`true` |

- OPcache/assertions são aplicados pelo **entrypoint** (`docker/php-fpm/entrypoint.sh`)
  num `php.ini` extra — não são ajustáveis em runtime.
- Caches do Laravel: `make up` (dev) roda `optimize:clear`; o deploy de produção roda
  `php artisan optimize`.
- Após trocar `APP_ENV`, recrie os containers (`make up`) para o entrypoint reaplicar.
- Overrides pontuais: `APP_DEBUG`, `FORCE_HTTPS`, `LOG_LEVEL`, `LOG_DEPRECATIONS_CHANNEL`,
  `DB_QUERY_LOG`, `TELEMETRY_ENABLED`, `SECURITY_COOKIES`, `SESSION_*`.

---

## 1. O que permanece no `.env`

| Variável | Papel |
|---|---|
| `APP_NAME` | Identidade do tenant (título, e-mails). |
| `APP_ENV` | `production` · `homolog` · `dev` — base para **todos** os outros defaults. |
| `APP_URL` | URL pública canônica. |
| `APP_KEY` | Chave de criptografia (**gerada**, nunca versionada). |
| `DB_DATABASE` · `DB_USERNAME` · `DB_PASSWORD` | Identidade/segredo do PostgreSQL. |
| `REDIS_PASSWORD` | Segredo do Valkey. |
| `AWS_ACCESS_KEY_ID` · `AWS_SECRET_ACCESS_KEY` | Credenciais do S3 (RustFS/AWS). |
| `MAIL_FROM_ADDRESS` | Remetente (entregabilidade). |

Backup do banco e offsite **não** ficam no `.env` (defaults no `scripts/update_prod.sh`; ver §2).

---

## 2. Defaults de `config/` (não precisam de `.env`)

### Aplicação (`config/app.php`)
| Env opcional | Default |
|---|---|
| `APP_LOCALE` / `APP_FALLBACK_LOCALE` | `pt_BR` |
| `APP_FAKER_LOCALE` | `pt_BR` |
| `TRUSTED_PROXIES` | `127.0.0.1`, `10/8`, `172.16/12`, `192.168/16` | Rede interna do container (cadeia Apache→Caddy→FPM). |
| `TRUSTED_HOSTS` | derivado do `APP_URL` + `localhost`/`127.0.0.1` | Anti host-header poisoning; produção só define `APP_URL`. |
| `FORCE_HTTPS` | perfil (`app.force_https`) |
| `APP_MAINTENANCE_DRIVER` | `file` |
| `TRUSTED_PROXIES` / `TRUSTED_HOSTS` | vazio (defina em produção — spec 16) |

### Banco (`config/database.php`)
| Env opcional | Default |
|---|---|
| `DB_CONNECTION` | `pgsql` (**fallback**; MySQL é excepcional e sob medida) |
| `DB_HOST` / `DB_PORT` | `postgres` / `5432` |
| `DB_SSLMODE` | `prefer` | Mais compatível: PG em container sem TLS; use `verify-full` se externo. |
| `DB_TIMEZONE` | `UTC` | Sessão do PG alinhada ao `app.timezone` (evita deslocar `timestamptz`/cota em 3h). |
| `REDIS_HOST` / `REDIS_PORT` / `REDIS_CLIENT` | `valkey` / `6379` / `phpredis` |

### Cache / Sessão / Fila
| Env opcional | Default |
|---|---|
| `CACHE_STORE` | `redis` (`config/cache.php`) |
| `SESSION_DRIVER` | `redis` (`config/session.php`) |
| `SESSION_LIFETIME` | `120` |
| `SECURITY_COOKIES` | perfil; liga `__Host-` + `Secure` no cookie de sessão |
| `SESSION_SECURE_COOKIE` / `SESSION_HTTP_ONLY` / `SESSION_SAME_SITE` | perfil (`secure`/`true`/`lax`) |
| `SESSION_ENCRYPT` | perfil (`true` em prod/homolog) |
| `SESSION_COOKIE` | `[__Host-]<app>-session` (o prefixo segue `SECURITY_COOKIES`) |
| `QUEUE_CONNECTION` | `redis` (`config/queue.php`) |
| `BROADCAST_CONNECTION` | `log` |

### Diagnóstico (`config/diagnostics.php`)

| Env opcional | Default | Observação |
|---|---|---|
| `DIAGNOSTICS_TIMEOUT` | `2` | Timeout (segundos) de cada check — aplicado no cliente nativo (PDO/Redis/S3). Curto de propósito: o diagnóstico não pode pendurar a request nem o comando. |

O diagnóstico é somente leitura; `backup_directory` e `disk_path` têm defaults em `storage_path()`
(não vão para o `.env`). A tela `GET /suporte/diagnostico` (domínio do painel) e
`php artisan core:diagnose` usam o mesmo serviço
(`Core\Internal\Diagnostics\DiagnosticsService`). O timeout também vale para a fila Redis
(cliente dedicado com `LLEN`/`ZCARD`) e para o `HeadBucket` do S3, que nesse uso roda sem retries.

### Armazenamento S3 / RustFS (`config/filesystems.php`)
| Env opcional | Default | Observação |
|---|---|---|
| `FILESYSTEM_DISK` | `s3` | PRD-6/ADR-030: S3-like desde o dia zero. |
| `AWS_DEFAULT_REGION` | `us-east-1` | |
| `AWS_BUCKET` | `public-v7` | |
| `AWS_ENDPOINT` | `http://storage:9000` | RustFS local. |
| `AWS_USE_PATH_STYLE_ENDPOINT` | `true` | Exigido pelo RustFS. |

**Trocar para AWS S3 (`.env` apenas):**
```
AWS_ENDPOINT=https://s3.us-east-1.amazonaws.com
AWS_USE_PATH_STYLE_ENDPOINT=false
AWS_BUCKET=<seu-bucket>
AWS_DEFAULT_REGION=<região>
```

### Backup do banco (`scripts/update_prod.sh`) e offsite (`disks.offsite`)

**Nada disto entra no `.env` por padrão.** O `scripts/update_prod.sh` já usa `BACKUP_KEEP=7`, backup remoto
**desligado** (`BACKUP_OFFSITE=0`) e o disco `s3`. Só sobreponha quando precisar — e registre o
porquê junto da variável no `.env`:

```ini
# Opcional: retenção dos dumps em $STORAGE_PATH/backups/ (default 7).
BACKUP_KEEP=7
# Opcional: 1 = copiar cada dump também para o disco remoto (default 0 = só local).
BACKUP_OFFSITE=0
# Opcional: disco destino do push/prune (default s3 = RustFS local).
BACKUP_DISK=s3
# Opcional (só com BACKUP_DISK=offsite): endpoint S3 externo.
OFFSITE_ENDPOINT=https://s3.exemplo.com
OFFSITE_BUCKET=concordia-backups
OFFSITE_DEFAULT_REGION=us-east-1
OFFSITE_ACCESS_KEY_ID=change-me
OFFSITE_SECRET_ACCESS_KEY=change-me
OFFSITE_USE_PATH_STYLE_ENDPOINT=false
```

| Env | Default | Observação |
|---|---|---|
| `BACKUP_KEEP` | `7` | Quantos dumps manter em `$STORAGE_PATH/backups/` (os mais novos). |
| `BACKUP_OFFSITE` | `0` | `1` = copiar cada dump para o disco de `BACKUP_DISK`. |
| `BACKUP_DISK` | `s3` | Disco do `core:backup:push`/`prune` (`offsite` para fora do host). |
| `OFFSITE_ENDPOINT` | — | Endpoint S3 externo; vazio desabilita o disco. |
| `OFFSITE_BUCKET` | — | Obrigatório com `BACKUP_DISK=offsite`. |
| `OFFSITE_DEFAULT_REGION` | `AWS_DEFAULT_REGION` | |
| `OFFSITE_ACCESS_KEY_ID`/`OFFSITE_SECRET_ACCESS_KEY` | `AWS_*` | Credenciais separadas do bucket de mídia. |
| `OFFSITE_USE_PATH_STYLE_ENDPOINT` | `false` | `true` para MinIO/alguns provedores. |

O disco `s3` é o **RustFS do próprio host**: `BACKUP_OFFSITE=1` com ele protege contra erro de
migração, **não** contra perda do host (G-15). Com `BACKUP_OFFSITE=1` + `BACKUP_DISK=s3`
apontando para `storage:9000`/loopback, o deploy emite aviso explícito.

- `core:backup:pull --disk=offsite [--path=…]` baixa o dump mais recente e **confere o tamanho**;
  cópia truncada é removida (nunca alimenta um restore).
- `tools/db-restore-drill.sh --from-offsite [--disk=offsite]` puxa a cópia e mede o RTO.
- Com `BACKUP_OFFSITE=1` e `BACKUP_DISK=s3` apontando para `storage:9000`/loopback, o `scripts/update_prod.sh`
  emite aviso explícito — o deploy não finge ser offsite.

### Backup da mídia (`disks.offsite-media`, D7)

A mídia publicada é **single-drive** (ADR-030): sem cópia, a perda do host perde as imagens.
O bucket offsite da mídia é **dedicado** (não se mistura com os dumps do banco) e usa o mesmo
endpoint e as mesmas credenciais do `OFFSITE_*`:

```ini
# O destino da cópia da mídia (mesmo endpoint/credenciais do offsite dos dumps).
OFFSITE_ENDPOINT=https://s3.exemplo.com
OFFSITE_MEDIA_BUCKET=concordia-midia
OFFSITE_USE_PATH_STYLE_ENDPOINT=false
```

| Env | Default | Observação |
|---|---|---|
| `OFFSITE_MEDIA_BUCKET` | — | Bucket dedicado da mídia. **Vazio = a mídia não tem cópia fora do host** (o `scripts/update_prod.sh` avisa). |
| `OFFSITE_ENDPOINT` | — | Endpoint S3 externo; o mesmo do `OFFSITE_*` dos dumps. |
| `OFFSITE_USE_PATH_STYLE_ENDPOINT` | `false` | `true` para RustFS/MinIO. |

- `php artisan media:backup [--dry-run] [--target=offsite-media]` — copia a mídia para o bucket
  offsite e grava o **manifesto** (`manifests/media-backup.json`) com o SHA-256 de cada objeto.
  Roda **diariamente** pela agenda; **falha alto** se o destino não estiver configurado.
- `php artisan media:restore --to=s3` — devolve ao disco de destino **exatamente** o que o
  manifesto lista (nunca um espelho destrutivo).
- `php artisan media:inventory` — inventário de integridade (objetos, tamanho e SHA-256 por
  prefixo) em `build/media-inventory.json`.

Apontado para o **mesmo** RustFS, o destino protege contra apagamento, corrupção e *overwrite* —
**não** contra a perda do host. Contra a perda do host, o destino precisa ser outro servidor.

### Mídia (`config/media.php`)

O teto de upload é **dado de instância** (pode ser sobrescrito no deploy), com dois níveis por tipo:
`soft_bytes` (recomendado → **aviso** ao autor) e `hard_bytes` (→ **recusa** com a mensagem do tipo).

| Tipo | `soft_bytes` | `hard_bytes` | Observação |
|---|---:|---:|---|
| Imagem raster | 2 MiB | 20 MiB | Foto de CMS acima de 20 MB é recusada; acima de 2 MB avisa. |
| SVG | 512 KiB | 2 MiB | Vetor grande é sinal de exportação errada. |
| GIF | 2 MiB | 10 MiB | Preferir WebP/vídeo para animação. |
| PDF | 20 MiB | **100 MiB** | Diários/editais escaneados de 80–100 MB passam. |

- **A borda e o PHP aceitam o maior hard** (PDF 100 MiB): `Caddy 110MiB`, Apache
  `LimitRequestBody 115343360`, PHP `upload_max_filesize=110M`/`post_max_size=114M`. O
  `MediaLimitsTest` falha se algum transporte ficar abaixo do maior hard.
- **Lote:** `batch.max_files` 50, `batch.max_bytes` 250 MiB (262 144 000), `batch.ttl_hours` 24;
  `quota.daily_bytes` 2 GiB, `quota.daily_files` 500. O rate `media-upload` é 120/min no app.
- Não há variável de `.env`: ajuste `config/media.php` (instância) e, se aumentar o hard, atualize
  borda/PHP no mesmo commit — o teste é o guarda.

### Log / Observabilidade

Todo log vai para **dois destinos** por padrão (canal `stack`): `json` (stderr —
observabilidade) **e** `daily` (arquivo local com rotação diária e retenção de **730 dias**).

| Env opcional | Default |
|---|---|
| `LOG_CHANNEL` | `stack` (`json` + `daily`) |
| `LOG_STACK` | `json,daily` |
| `LOG_LEVEL` | perfil: `debug` (dev/homolog) · `info` (production) |
| `LOG_DAILY_DAYS` | `730` (retenção local) |
| `LOG_DEPRECATIONS_CHANNEL` | perfil: `stack` (dev) · `null` |
| `LOG_REQUESTS` / `LOG_SLOW_REQUEST_MS` | `true` / `1000` |
| `DB_QUERY_LOG` | perfil: `true` (dev) — loga cada SQL em `debug` |
| `TELEMETRY_ENABLED` | `true` (telemetria é capacidade do projeto) |
| `OTEL_SERVICE_NAME` | `concordia` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | **vazio** em todos os perfis (sem coletor ⇒ sem exportação; é o default seguro) · no dev, com o profile `telemetry` no ar, use `http://jaeger:4318` |
| `OTEL_EXPORTER_OTLP_TIMEOUT_MS` | `50` |
| `OTEL_CIRCUIT_BREAKER_SECONDS` | `30` |

### E-mail
| Env opcional | Default |
|---|---|
| `MAIL_MAILER` | `log` (dev); `smtp` em produção |
| `MAIL_HOST` / `MAIL_PORT` | `mailpit` / `1025` (perfil `debug`) |
| `MAIL_FROM_NAME` | `APP_NAME` |

### LGPD / privacidade (`config/lgpd.php`)
| Env opcional | Default | Observação |
|---|---|---|
| `LGPD_DPO_NAME` / `LGPD_DPO_EMAIL` / `LGPD_DPO_PHONE` | `Encarregado…` / `dpo@exemplo.gov.br` / vazio | Canal do encarregado publicado na política (`/politica-de-privacidade`). |
| `LGPD_DSAR_DUE_DAYS` | `15` | Prazo de resposta ao titular (LGPD art. 19). |
| `LGPD_DSAR_RETENTION_DAYS` | `365` | Guarda do protocolo após a conclusão antes de anonimizar (`retention:expunge`). |
| `LGPD_ANALYTICS_THIRD_PARTY` | `false` | Sem terceiros por padrão (ADR-002); a política muda a redação conforme o valor. |

### Métricas privacy-first (`config/metrics.php`)
| Env opcional | Default | Observação |
|---|---|---|
| `METRICS_RETENTION_DAYS` | `400` | Janela dos agregados diários (`metrics:expunge`/`retention:expunge`); a auditoria tem política própria. Também é o prazo publicado na política de privacidade (o texto lê a config). |
| `METRICS_IGNORED_ROUTES` | `media.*,seo.*` | Allowlist (`Str::is`, separada por vírgula) de rotas de asset que ficam fora do agregado; não use para esconder páginas. |
| `METRICS_UNIQUE_DRIVER` | `valkey` | `array` = contador de únicos em memória (testes/dev isolado; `phpunit.xml` fixa `array`). |
| `METRICS_UNIQUE_CONNECTION` | `default` | Conexão do ValKey usada pelo HyperLogLog. |
| `METRICS_UNIQUE_PREFIX` | `metrics:unique:` | Prefixo da chave diária do HLL. |
| `METRICS_UNIQUE_TTL_DAYS` | `90` | TTL do HLL diário (ADR-047 §2); o hash nunca vai para o PostgreSQL. |

---

## 3. Infraestrutura (Compose)

Estas têm default no `docker-compose.yml` e **não** ficam no `.env`:

| Variável | Default | Quando sobrepor |
|---|---|---|
| `COMPOSE_PROJECT_NAME` | slug do `APP_NAME` (`tools/project-name.sh`) | Nome do projeto Compose (containers/volumes/rede). "Prefeitura de Santa Barra" → `prefeitura-santa-barra`; só defina para sobrepor. |
| `CONTAINER_RUNTIME` | `docker` | **Padrão e único runtime de produção** (ADR-045). `podman` é opt-in de dev: `make podman-prepare` ou `make runtime RUNTIME=podman`; `scripts/update_prod.sh` recusa podman. |
| `MOUNT_LABEL` | `:Z` | Sufixo SELinux do bind mount. `:Z` (relabel) é o padrão **mais seguro** e o do Docker; o opt-in podman o define vazio (usa `label=disable`). |
| `UID` / `GID` | `1000` / `1000` | Usuário do host diferente. |
| `WEB_PORT` | `50700` | Porta publicada no host. Sem valor, `make env` deriva **50700–50789** do nome do projeto; `MAILPIT_PORT`/`JAEGER_UI_PORT`/`JAEGER_OTLP_PORT` usam 50790–50792. Nenhuma outra porta é publicada. |
| `DATA_PATH` | `./s3-local` | Produção **fora da cota** (ex.: `/srv/public-v7`). |
| `STORAGE_PATH` | `./storage` | Runtime do Laravel fora do repo. |
| `AWS_DEFAULT_REGION` | `us-east-1` | Região do S3. |
| `PHP_IMAGE` / `PHP_TAG` | `concordia/php-fpm` / `local` | Imagem própria do PHP-FPM (build local/deploy). |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | vazio (todos os perfis) · dev opt-in: `http://jaeger:4318` com o profile `telemetry` | Coletor OTLP; vazio = sem exportação (default). |

> **Nome do projeto:** por padrão o `COMPOSE_PROJECT_NAME` é o **slug do `APP_NAME`** (sem
> conectivos): `"Prefeitura de Santa Barra"` → `prefeitura-santa-barra`. Resolvido por
> `tools/project-name.sh` e aplicado via `-p` em `make`, `scripts/update_prod.sh`, `test-db.sh` e CI.
> Sobrepõe-se com `COMPOSE_PROJECT_NAME` no `.env`.

> **Dados:** por padrão, PostgreSQL e RustFS vivem em `./s3-local/` (dentro do repositório,
> ignorado pelo `.gitignore`). Em produção na WHM, aponte `DATA_PATH` para fora da cota do
> cPanel (ADR-006/ADR-030).

---

## 4. Testes

As `<env>` de `phpunit.xml` cobrem o isolamento (sem `.env.testing`): banco `concordia_test`,
drivers em memória (`array`/`sync`) e telemetria desligada. Ver `wiki/specs/06-observabilidade.md`
e `wiki/specs/08-ia-e-qualidade.md`.

---

## 5. Defaults de segurança (hardening) — `config/`

O framework traz defaults permissivos que **sobrescrevemos** com valores seguros:

| Arquivo | Default seguro | Por quê |
|---|---|---|
| `hashing.php` | `HASH_DRIVER=argon2id` | ADR-020 exige `PASSWORD_ARGON2ID` (default do framework: `bcrypt`). |
| `cors.php` | `allowed_origins = []` (sem wildcard) | Default do framework: `['*']`; portal é same-origin. Libere via `CORS_ALLOWED_ORIGINS`. |
| `cache.php` | `serializable_classes = false` | Impede desserializar objetos do cache (PHP object injection). |
| `app.php` | `APP_CIPHER=AES-256-GCM` | Cifra autenticada (AEAD) em vez de `AES-256-CBC`. |
| `logging.php` | `permission = 0640` | Logs podem conter PII; restringe leitura. |
| `view.php` | `compiled = storage_path('framework/views')` | Sem `realpath()` (evita boot quebrado em clone limpo). |
| `session.php` | `__Host-` + `Secure` + `use_strict_mode` | ADR-020/ADR-032 (ver §0 e §3). |

Outros defaults herdados do framework já são seguros e ficam como estão: `broadcasting.default=null`,
`concurrency.default=process`, `queue.failed.driver=database-uuids`.

> Trocar `APP_CIPHER` exige re-cifrar dados persistidos (sessões/cookies). Pré-lançamento: sem
> impacto. `HASH_VERIFY`, `ARGON_MEMORY`/`ARGON_TIME` permitem ajuste fino do custo.
