connect a database

Connect MySQL or MariaDB

Provisioning read-only credentials for MySQL or MariaDB — the same instructions Evalyst shows you when a credential check fails.

source of truth: app repo docs/readonly/mysql.md

Run as a user that holds CREATE USER and GRANT OPTION (usually root on the primary; grants replicate to the rest of the cluster).

-- 1. the account. Scope the host as tightly as you can — the app's egress IP, or the subnet
--    your bastion/proxy connects from, not '%'.
CREATE USER 'evalyst_reader'@'<app_ip_or_subnet>' IDENTIFIED BY '<STRONG_PASSWORD>';

-- 2. SELECT on exactly one schema. Nothing else — no INSERT/UPDATE/DELETE, no DDL,
--    no FILE (that is `SELECT … INTO OUTFILE` / `LOAD_FILE`), no SUPER, no GRANT OPTION.
GRANT SELECT ON `<your_db>`.* TO 'evalyst_reader'@'<app_ip_or_subnet>';

-- 3. optional but recommended: cap what one session can burn
ALTER USER 'evalyst_reader'@'<app_ip_or_subnet>'
    WITH MAX_USER_CONNECTIONS 8 MAX_QUERIES_PER_HOUR 20000;

FLUSH PRIVILEGES;

Deliberately not granted: CREATE TEMPORARY TABLES. Evalyst never needs it, and its absence is one more thing standing between a bug and your data.

Check it yourself

mysql -u evalyst_reader -p'<PW>' -h <host> -e "SELECT COUNT(*) FROM <your_db>.<table>;"  # works
mysql -u evalyst_reader -p'<PW>' -h <host> -e "DELETE FROM <your_db>.<table> LIMIT 1;"   # must FAIL
mysql -u evalyst_reader -p'<PW>' -h <host> -e "SHOW GRANTS;"                             # SELECT only

Notes

  • How Evalyst verifies this without writing anything. It runs UPDATE \evalyst_readonly_probe` SET x = 1 WHERE 1 = 0 against a table that does not exist. MySQL checks table privileges *before* table existence, so error 1142 (command denied) proves the account cannot write, while 1146 (no such table`) proves it can — the privilege check passed and only the table was missing. Nothing is created or modified either way.
  • Point at a replica if you have one. @@global.read_only is reported in the verify evidence.
  • ProxySQL / connection proxies. The user must exist in two places: the backend cluster (CREATE USER + GRANT) and the proxy’s own user table. See your platform team’s runbook for the ProxySQL steps.
  • Connecting over SSH. For a database on a private network, give Evalyst a bastion instead of opening port 3306: config.ssh = {"host": "<bastion>", "user": "<ssh user>", "key_secret_ref": "SSH_PRIVATE_KEY"}, and keep config.host as the address the bastion uses (commonly 127.0.0.1, when MySQL listens on the db host’s loopback). Host-key trust is per-source, not from ~/.ssh/known_hosts — see the bastion notes.
  • Statement timeout. The driver sets max_execution_time per session from the source’s cost_caps.statement_timeout_ms. This only bounds SELECT, which is all Evalyst runs.