# MySQL

Bancos MySQL 8 ou MariaDB no VPS com conta própria que exige TLS, permissões só no seu banco e acesso externo por IP.

Fonte: https://ardumaker.com/pt/docs/mysql/

- Categoria: Servidores e infraestrutura
- Estado: Disponível
- Requisitos: Um VPS ativo com a função `mysql` disponível; Se o motor já existir: MySQL 8.x ou MariaDB 10.4 ou superior; Ser membro com acesso ao VPS para instalar; admin para revelar, abrir acesso ou apagar dados
- Ferramentas MCP (Só leem): `ver_catalogo_apps`, `ver_preflight_app`, `ver_app`, `ver_acceso_app`, `ver_operacion_app`, `listar_motores_db_vps`, `listar_bases_db_vps`
- Ferramentas MCP (Escrevem ou modificam): `instalar_app`, `revelar_credencial_app`, `cambiar_lista_ip_app`, `reintentar_operacion_app`
- Ferramentas MCP (Destrutivas): `revocar_acceso_app`, `desinstalar_app`

## O que permite fazer?

Cada banco MySQL é um app com conta própria. O motor (MySQL ou MariaDB) é um por VPS e compartilhado pelos seus bancos; se não existir, o MySQL é instalado a partir dos pacotes da distribuição.

- Criar vários bancos `utf8mb4`, cada um com sua conta e suas permissões.
- Revelar usuário, senha e URI de conexão.
- Abrir o motor para a internet só para certos IPs, com TLS.
- Pela aba MySQL do VPS: bancos, usuários, permissões e tabelas.

## Quando convém usar?

- Aplicações que esperam MySQL (PHP, Laravel, muitas ferramentas de terceiros).
- Um [WordPress](https://ardumaker.com/pt/docs/wordpress) usa este mesmo motor, com banco e conta próprios por site.

## Como funciona na ArduMaker?

1. Motor: se o agente disser que não está instalado, é instalado uma vez (`mysql-server` e `mysql-client`); se já existir, nunca é reinstalado nem muda de versão.
2. Variante: lê-se `mysql --version`. Só MySQL 8.x ou MariaDB 10.4+; outra coisa falha `motor_no_soportado` sem executar SQL.
3. Credencial `app<id>_<hex>` com senha aleatória, guardada criptografada antes de tocar o VPS.
4. Entra-se como administrador pelo socket, com a senha em um arquivo de opções efêmero, nunca na linha de comando.
5. Conta `'app<id>_<hex>'@'127.0.0.1'` com `REQUIRE SSL`, e `GRANT ALL PRIVILEGES` só sobre `<banco>.*`, sem `GRANT OPTION`, antes de criar o banco.
6. `CREATE DATABASE <banco> CHARACTER SET utf8mb4` só se não existia.
7. Verificação: TLS exigido, sem privilégios globais e permissões sobre um único banco, o seu.

A conta é `@'127.0.0.1'` de propósito: não `%` (abriria a conta para qualquer origem) nem `localhost` (socket, sem TLS). Quando o servidor não tem `partial_revokes` (ou é MariaDB), o `_` do nome é escapado no GRANT, porque ali é um curinga.

`config.external_access` decide se o banco sai para a internet: `{"ips": [...]}` abre só para esses IPs; `{"local": true}` (ou `false`) o deixa só dentro do VPS. Se não for informado, pelo painel usa-se o IP de quem instala; via MCP o IP do agente não conta, então sem `ips` o banco fica local em vez de falhar.

## O que muda realmente?

| O quê | Detalhe |
| --- | --- |
| Motor | MySQL ou MariaDB escutando em `127.0.0.1:3306` |
| Banco | `utf8mb4`; um nome igual com outras maiúsculas conta como o mesmo |
| Conta | `'app<id>_<hex>'@'127.0.0.1'` REQUIRE SSL |
| TLS no MySQL 8 | `SET PERSIST` + `ALTER INSTANCE RELOAD TLS`, sem reinício |
| TLS no MariaDB | Arquivo `90-ardumaker-tls.cnf`; na primeira vez exige reiniciar o motor |

> **INFO:** Não são aceitos como nome `mysql`, `information_schema`, `performance_schema` nem `sys`. `require_secure_transport` não é ativado globalmente: cortaria os clientes internos sem TLS; o TLS é exigido por conta.

## Acesso pela internet e lista de IP

Por padrão o motor escuta só dentro do VPS (3306 em `127.0.0.1`). Para usar o banco de fora, a ArduMaker publica um endpoint próprio do motor: `db-<id do VPS>` dentro da zona da plataforma (registro DNS direto, sem proxy), em uma porta reservada entre 61000 e 64999. No host, um proxy leva essa porta ao 3306 do VPS e o firewall só deixa entrar os IPs da lista.

O certificado é próprio do nome `db-<id>` e é terminado pelo `mysqld`: o proxy do host só passa TCP. A conta do app exige SSL venha de onde vier.

| Regra da lista | Detalhe |
| --- | --- |
| Formato | IPv4 ou IPv6, com ou sem prefixo; um IP solto é /32 ou /128. Nada de nomes, loopback, link-local nem multicast |
| Amplitude | Prefixo mínimo /24 em IPv4 e /48 em IPv6 por padrão; `0.0.0.0/0` e `::/0` são recusados na lista |
| Tamanho | Até 20 entradas; vazia = fechado para todos |
| Nomes de host | Até 5 (`allowed_hosts`), com até 8 IPs públicos cada; são resolvidos de novo a cada 5 minutos e uma falha de DNS mantém o último conhecido |
| Aberto a todos | Só com `allow_any` e `confirm_open` juntos; a resposta informa em `open_to_any` |

A lista é do motor, não de cada banco: alterá-la a partir de qualquer banco MySQL muda o acesso de todos os bancos desse motor nesse VPS (a resposta indica com `shared_with_engine` e `affected_apps`).

1. As regras de firewall que faltam são adicionadas, uma por CIDR (`ufw allow from <cidr> to any port <porta> proto tcp`).
2. Depois as que sobram são removidas e suas conexões abertas são cortadas.
3. Só então o listener é criado e verificado: regras iguais à lista, nenhuma regra aberta a qualquer um, listener presente e handshake TLS com o nome correto.

> **INFO:** Adicionar antes de remover faz com que o estado intermediário seja sempre a união de duas listas válidas: nunca há um instante aberto a todos. Se a verificação falhar, o listener é removido.

> **PRECAUCION:** Outro VPS do mesmo host não chega pelo IP público mesmo estando na lista: a conexão estoura o tempo. A resposta avisa em `warnings` (`mismo_host`); entre dois VPS assim usa-se um túnel SSH (`configurar_tunel_entre_vps`).

## Desinstalar, persistência e cópias

- Sem `delete_data` (o normal): o acesso externo próprio é fechado e o app deixa de aparecer como instalado, mas o banco, seu usuário e seus dados ficam no VPS. A próxima reconciliação o mostra como detectado. Nada se perde.
- Com `delete_data: true` (confirmado duas vezes, só admin): o banco é apagado e, se a conta foi criada pelo Apps, também a conta e sua senha guardada. Nunca uma conta do cliente nem `root`.
- O motor nunca é desinstalado pelo Apps: fica instalado mesmo sem bancos.
- Se o VPS ou o motor não responderem, `desinstalar_app` com `forzar=true` tenta cada passo uma vez, remove o app e lista em `not_cleaned` o que ficou no servidor.
- Os dados vivem no disco do VPS e viajam na sua cópia semanal, feita com o VPS ligado. Para uma cópia consistente de um banco específico convém também um dump próprio pelo [gerenciador de bancos de dados](https://ardumaker.com/pt/docs/gestor-de-bases-de-datos) (exportar) ou com as ferramentas do motor.

> **PELIGRO:** Com `delete_data` a desinstalação é irreversível.

## Esquema

**Conexões a um banco MySQL**

```text
App no mesmo VPS (`127.0.0.1:3306`)
  ↓
Cliente externo (`db-<id>:<porta>` com verificação de identidade TLS)
  ↓
Firewall e proxy do host (só IPs da lista → 3306 do VPS)
  ↓
mysqld (termina o TLS; a conta exige SSL e vê só o seu banco)
```

## Resultado esperado

A URI (sem senha) aparece quando o acesso externo está pronto; a completa é obtida com `revelar_credencial_app`.

```text
mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?ssl-mode=VERIFY_IDENTITY
mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?sslMode=verify-full   (MariaDB)
```

## Erros e resolução

| Erro | Causa | Tentar de novo? |
| --- | --- | --- |
| `motor_no_soportado` | Não é MySQL 8.x nem MariaDB 10.4+; nada é tocado | Não |
| `base_ya_existe` | Existe um banco com esse nome (sem distinguir maiúsculas) sobre o qual a conta não tem permissões | Não |
| `motor_detenido` / `base_no_creada` / `permisos_no_verificados` | Motor parado, SQL com falha ou permissões que não verificaram | Sim |
| `requiere_reinicio` | O MariaDB precisa reiniciar para o primeiro TLS; o acesso não abre até resolver | Após reiniciar |
| Acesso externo não pedido | O motor tem contas anônimas ou sem senha que o listener exporia | Após limpar essas contas |

As demais causas (lista de IP, TLS, credenciais antigas, fechamento não verificado) são as mesmas do [PostgreSQL](https://ardumaker.com/pt/docs/postgresql). O acompanhamento é com `ver_operacion_app` e `reintentar_operacion_app`.

## Relação com MCP

Todo o ciclo do banco é operado via MCP com as permissões da pessoa que autorizou o agente. Referência em [/mcp/docs](https://ardumaker.com/pt/mcp/docs) e [/mcp/bases-de-datos](https://ardumaker.com/pt/mcp/bases-de-datos).

- Leitura: `ver_catalogo_apps`, `ver_preflight_app`, `listar_apps_empresa`, `ver_app`, `ver_acceso_app` (host, porta, TLS e lista de IP, sem segredos), `ver_operacion_app`.
- Modificam: `instalar_app` com `app_type: mysql` (exige `idempotency_key`), `cambiar_lista_ip_app` (só se o dono for admin; os IPs são informados explicitamente: o IP do agente não é o da pessoa), `reintentar_operacion_app`, `refrescar_apps`.
- Sensível: `revelar_credencial_app` (só admin, não aceita `idempotency_key`; a resposta vem marcada `sensible: true` e a auditoria registra que foi revelado, nunca o valor).
- Destrutivas: `revocar_acceso_app` (fecha o acesso externo de todo o motor) e `desinstalar_app` (com `delete_data` apaga os dados; exige `confirm_delete_data`).

Para ler e escrever dados dentro do banco, o agente usa as ferramentas do [gerenciador de bancos de dados](https://ardumaker.com/pt/docs/gestor-de-bases-de-datos). Um agente autorizado como somente leitura não vê as ferramentas que modificam nem pode revelar credenciais.

## Funcionalidades relacionadas

- [Gerenciador de bancos de dados](https://ardumaker.com/pt/docs/gestor-de-bases-de-datos/)
- [PostgreSQL](https://ardumaker.com/pt/docs/postgresql/)
- [WordPress](https://ardumaker.com/pt/docs/wordpress/)
- [Firewall e portas](https://ardumaker.com/pt/docs/firewall-y-puertos/)
- [Cópias de segurança](https://ardumaker.com/pt/docs/backups/)
