# 05 — O que **não** adotar do `cms`

Práticas observadas no CMS que parecem boas em isolation mas conflitam com o `AGENTS.md`, com o
axioma A0 (artefato único) ou com a operação real. Cada linha traz a evidência e o substituto.

| # | Prática observada | Evidência | Por que não | No V7 |
|---|---|---|---|---|
| 1 | Três alvos de build (`dev`/`stage`/`prod`) e config congelada por ambiente | `cms/containers/cms/Dockerfile:142-219`; `configs/env.dev.conf`/`env.prod.conf`/`env.stage.conf`; `CMS_TARGET`/`INFRA_TARGET` | Duplica artefato, permite que o que foi testado não seja o que roda, e viola ADR-032 (`APP_ENV` é o único interruptor) e A0. | Uma imagem; perfil por `APP_ENV`; logs ligados em produção (U-05). |
| 2 | Entrypoint como root que faz `groupmod`/`usermod` e `chown -R` no bind mount | `cms/containers/cms/configs/entrypoint.sh:9-28` | Percorre `storage`/`vendor` a cada boot, mascara erro de permissão e exige root no runtime. | Non-root via `user: ${UID}:${GID}` no Compose; sem `chown` no boot. |
| 3 | `sudo NOPASSWD: ALL` e agentes de IA (Gemini/Copilot/Claude) na imagem | `cms/containers/cms/Dockerfile:172-180` | Superfície de ataque e tamanho de imagem; ferramenta de dev não é runtime. | Ferramentas no host/devcontainer; imagem só com runtime. |
| 4 | Token de Git gravado no `.git/config` do clone montado | `entrypoint.sh:72-86` (`git config --local url.https://user:token@…`) | O segredo passa a viver no bind mount e no backup; vaza em `git config --list`. | Credencial efêmera no CI; nunca no repo clonado. |
| 5 | `/var/run/docker.sock` montado na aplicação | `cms/docker-compose.yml:69` | Acesso ao socket é equivalente a root no host; RCE no PHP vira root. | Nunca na app; no máximo no coletor, `:ro`, sob profile e documentado. |
| 6 | `install-php-extensions` baixado de `releases/latest` | `Dockerfile:49` | Build não reprodutível; troca silenciosa de script. | Se adotar, pinar release + SHA-256 (S-09). |
| 7 | `git clone` do BLAKE3 sem tag/commit e `pip install docling` sem versão | `Dockerfile:76,121` | Qualquer push muda o artefato; quebra ADR-001. | Commit pinado **e** checksum (S-09); versões fixas. |
| 8 | YARA 4.5, Ghostscript, Docling e toolchain Python no runtime | `Dockerfile:93-127` | Centenas de MB e CVEs que o app web não usa; tempo de build e de scan. | Só o que o request precisa; processamento pesado em serviço/worker separado, se houver demanda. |
| 9 | `php.ini` de desenvolvimento no `base` (`display_errors=On`, `html_errors=On`, `E_ALL`) | `cms/containers/cms/configs/php.ini:37-40` | Erro exibido ao cliente e detalhe interno exposto; o `base` alimenta todos os alvos. | `display_errors=Off` e log em stderr (`docker/php-fpm/php.ini:5-8`). |
| 10 | `user_ini.filename=".user.ini"` | `php.ini:21-22` | Permite que arquivo do docroot (cPanel) altere o PHP — exatamente o que ADR-005 proíbe. | `user_ini.filename=` vazio (php.ini e pool). |
| 11 | `max_execution_time=600`, `max_input_time=600`, `request_terminate_timeout=600s` | `php.ini:30-34`, `fpm.conf:30` | Um cliente lento ou payload malformado ocupa worker por 10 min; DoS barato. | 30 s no PHP + 35 s no FPM (P-02); elevar só com caso real. |
| 12 | `opcache.jit=1255` + `jit_buffer_size=128M` + `enable_cli=1` | `99_prod.ini:3-11` | A medição do V7 mostra `tracing` 2,4× pior (spec 17 §4); JIT em CLI consome memória sem ganho. | JIT `off`; reavaliar só com medição nova (P-09). |
| 13 | Preload com `opcache.preload_user` | `99_prod.ini:15-16` | Só faz sentido quando o master roda como root; no V7 o FPM é non-root. | Se adotar preload, sem `preload_user` e com medição (P-09). |
| 14 | HSTS emitido pelo proxy interno | `balancer/configs/headers.conf:10` | Dois donos do mesmo header; o TLS termina no Apache do WHM (ADR-005/spec 16). | HSTS só no `app-ssl.conf` do Apache; Caddy não emite. |
| 15 | Rate limit, tarpit e cache de página em Lua contra o Valkey | `balancer/configs/sites/site.conf.template:61-121` | Duplica ADR-021/ADR-022 no edge, adiciona latência (100 ms timeout) e um ponto de falha; `limit_req`/`proxy_cache` nem são usados. | Rate limit na app (Valkey) + `mod_qos`/`mod_evasive` no Apache; cache de resposta nativo. |
| 16 | `container_name` fixo | `cms/docker-compose.yml:49,88,…` | Impede escala e colide entre instâncias; o nome do projeto já resolve. | Sem `container_name`; `-p $COMPOSE_PROJECT_NAME`. |
| 17 | Grafana com `admin/admin` e Loki com `auth_enabled: false` | `docker-compose.yml:320`; `loki/configs/loki.yaml:1` | Se a porta for publicada, o painel é aberto; sem auth, qualquer container lê logs. | Senha por secret, sem porta pública, rede interna; se publicar, auth (O-02). |
| 18 | Vector com sinks apenas `console` | `telemetry/configs/vector.yaml:80-96` | Não há pipeline: o log não é retido nem consultável; o “coletor” só ecoa. | Sink Loki/Tempo + buffer em disco + retenção (O-02). |
| 19 | Loki/Tempo sem retenção | `loki.yaml:36-39`; `tempo.yaml:16-22` | Crescimento sem teto no volume. | `retention_period` + compactor (O-02). |
| 20 | `dozzle:latest` com `docker.sock` | `docker-compose.yml:354-362` | Tag móvel + UI com acesso ao socket; supply chain + privilégio. | `docker compose logs`/Vector; se usar UI, pinar por digest e restringir. |
| 21 | SonarQube e `postgres:17-alpine` com senha default | `docker-compose.yml:368-396` | Fora do escopo de runtime; senha default e tag móvel. | Qualidade no CI; sem serviço de Sonar no host do cliente. |
| 22 | Redis/Valkey sem `requirepass` e healthcheck sem auth | `cache/configs/valkey.conf`; `docker-compose.yml:158-162` | Qualquer container da rede lê/grava cache e fila. | Senha obrigatória (já no V7) + probe autenticado (R-06). |
| 23 | `envsubst` no entrypoint do Nginx | `balancer/Dockerfile:96-104` | Config gerada em runtime diverge do arquivo versionado; difícil auditar. | Caddyfile estático; variáveis de ambiente não interpolam config. |
| 24 | TLS 1.3/HTTP3 dentro do container | `balancer/configs/nginx.conf:44-51`; `site.conf.template:13-20` | O TLS é do Apache do WHM; certificado autoassinado no container é ruído. | `auto_https off`; TLS só no Apache (já no V7). |
| 25 | MinIO AIStor com licença | `containers/storage/Dockerfile`; README §3 | Licença não permissiva (ADR-002/ADR-030). | RustFS Apache-2.0 (já no V7). |
| 26 | `extra_hosts: host.docker.internal` | `cms/docker-compose.yml:51-52` | Só serve a Xdebug no host; em produção é superfície desnecessária. | Sem `extra_hosts` em runtime. |
| 27 | Build args UID/GID + sincronização de usuário em runtime | `Dockerfile:15-16,35-38` | Amarra a imagem ao host e exige root no boot. | UID/GID pelo Compose; imagem independente do host. |
| 28 | `pull_policy` e tags por alvo | `docker-compose.yml:48`; `image: maxima/cms:${CMS_TARGET}` | A tag deixa de identificar o conteúdo; rollback fica ambíguo. | Digest para terceiros (já) + tag por commit/imagem única (P-01/R-04). |

## Anti-patterns específicos do RustFS (skill `rustfs`)

Não vêm do CMS; vêm da operação de object storage e valem como regra para o V7. A análise completa
está em `08-rustfs-storage.md`. Referências `NN:linha` nesta seção são da skill
`.agents/skills/rustfs/`.

| # | Prática | Evidência | Por que não | No V7 |
|---|---|---|---|---|
| 29 | Copiar `/data`, shards, `xl.meta` ou `format.json` como backup | `rustfs/SKILL.md:57-58`; `12:16-21` | Não é backup lógico; `format.json` copiado reivindica identidade/UUID e quebra o slot | `mc mirror`/replicação S3 (RS-06); restore para scratch |
| 30 | Definir `EC:0` explícito para silenciar o warning | `04:117-119` | `EC:0` não tem redundância; o warning é a única sinalização | Manter o default implícito e documentar RS-10 |
| 31 | `RUSTFS_DURABILITY_MODE=relaxed` em single-node | `04:146-152`; `99:312` | Metadados podem sumir em queda de energia; só é defensável multi-nó com energia independente | `strict` explícito (RS-05) |
| 32 | NFS/CIFS/FUSE/overlay como `/data` | `SKILL.md:46-48`; `02:9,15-16` | Fora do boundary de produção; corrupção/IO inconsistente | `RUSTFS_UNSUPPORTED_FS_POLICY=fail` |
| 33 | `RUSTFS_UNSAFE_BYPASS_DISK_CHECK` ou `GET_SKIP_BITROT_VERIFY` | `03:56`; `04:192-200` | Ignora independência/bitrot; não é tuning | Não usar; medir com o caminho normal |
| 34 | `mc mirror --remove` como backup | `02:285` | Transforma backup em espelho destrutivo; delete local apaga o remoto | Backup sem `--remove` (RS-06) |
| 35 | Console/admin exposto sem TLS/OIDC | `SKILL.md:59-61`; `07:238-253` | Console default `rustfsadmin/rustfsadmin`; admin = root | Console desligado; sem porta; se ligar, TLS+OIDC |
| 36 | Tratar `replication-check` como read-only | `05:180-192` | Ele grava/apaga probes no target | Não usar como health check |
| 37 | Mudar endpoints/ordem/topologia de um pool existente | `SKILL.md:39-45`; `12:3-34` | `FormatV3` imutável; single-drive não expande | Novo deployment + migração via S3 |
| 38 | Apagar `format.json`/`pool.bin`/`.rustfs.sys` para “destravar” | `12:11-21,215-240`; `99:317-318` | Remove a autoridade de recuperação; não há force-reset | Restaurar réplica/backup e seguir o runbook |
| 39 | Root do storage compartilhado com o app | `docker-compose.yml:140-141`; `07:238-253` | RCE no PHP vira admin de IAM/KMS/buckets | Usuário de workload (RS-07) |
| 40 | Versioning sem limite/lifecycle | `03:80`; `06:40-55` | Versões ilimitadas consomem o disco | `RUSTFS_API_OBJECT_MAX_VERSIONS=20` + lifecycle (RS-08) |

## O que foi aproveitado apesar da origem

- `install-php-extensions` **como conceito** (menos ruído de compilação) → só com versão/digest (S-09).
- Profiles Compose e faixa de portas documentada → adaptados em U-02.
- `infra-audit.sh` → `make doctor` (U-03), sem os checks que dependem de TLS no container.
- `STOPSIGNAL`/labels de imagem → S-07.
- GUCs de log do Postgres e tuning do Valkey → P-05/P-06, com valores menos verbosos.
- Máscara de PII do Vector → O-02, com sinks completos.

## Critério para reavaliar uma rejeição

Só reintroduzir uma prática desta lista com **decisão registrada** (ADR/knudge), **medição** e
**teste observável**. Exemplos: preload só com p50/p95; `io-threads` só com ≥ 2 vCPU e latência
medida; `read_only` só depois de o upload de 110 MB passar; multi-stage só se o tempo de deploy
virar problema comprovado — e, nesse caso, sem congelar `APP_ENV`.
