Overview
Three related MySQL configuration bugs were discovered during the Hetzner demo deployment (#405). All three stem from the same area of the codebase (MySQL setup in the deployer) and are best fixed together.
Bug 1 — MySQL DSN hardcoded in tracker.toml with no URL-encoding: The tracker configuration template builds the MySQL DSN by interpolating raw credential values directly into the URL string. If the password contains URL-reserved characters the DSN becomes malformed and the tracker fails to connect. Additionally, the DSN (including the plaintext password) ends up in a mounted config file rather than an environment variable, violating the project's secret-handling convention.
Bug 2 — MySQL root password is not user-configurable: The deployer silently derives the MySQL root password as {app_password}_root (hardcoded in src/application/services/rendering/docker_compose.rs). Users have no way to supply their own root password via the environment configuration JSON.
Bug 3 — No validation that MySQL app username is not "root": The MySQL Docker image reserves the root username for the built-in administrator account. If a user supplies "root" as the app DB username in their environment JSON, Docker will refuse to initialize the database. The domain type MysqlConfig validates for an empty username but not for this reserved value.
Specification
See detailed specification: docs/issues/410-bug-multiple-mysql-configuration-issues.md
Implementation Plan
Phase 1: Reject reserved MySQL username (Bug 3)
Phase 2: Make root password configurable (Bug 2)
Phase 3: Move DSN to env var override and add URL-encoding (Bug 1)
Phase 4: Tests
Phase 5: Linting and pre-commit
Acceptance Criteria
Note for Contributors: These criteria define what the PR reviewer will check. Use this as your pre-review checklist before submitting the PR to minimize back-and-forth iterations.
Quality Checks:
Bug 3:
Bug 2:
Bug 1:
Related
Overview
Three related MySQL configuration bugs were discovered during the Hetzner demo deployment (#405). All three stem from the same area of the codebase (MySQL setup in the deployer) and are best fixed together.
Bug 1 — MySQL DSN hardcoded in
tracker.tomlwith no URL-encoding: The tracker configuration template builds the MySQL DSN by interpolating raw credential values directly into the URL string. If the password contains URL-reserved characters the DSN becomes malformed and the tracker fails to connect. Additionally, the DSN (including the plaintext password) ends up in a mounted config file rather than an environment variable, violating the project's secret-handling convention.Bug 2 — MySQL root password is not user-configurable: The deployer silently derives the MySQL root password as
{app_password}_root(hardcoded insrc/application/services/rendering/docker_compose.rs). Users have no way to supply their own root password via the environment configuration JSON.Bug 3 — No validation that MySQL app username is not
"root": The MySQL Docker image reserves therootusername for the built-in administrator account. If a user supplies"root"as the app DB username in their environment JSON, Docker will refuse to initialize the database. The domain typeMysqlConfigvalidates for an empty username but not for this reserved value.Specification
See detailed specification: docs/issues/410-bug-multiple-mysql-configuration-issues.md
Implementation Plan
Phase 1: Reject reserved MySQL username (Bug 3)
MysqlConfigError(mysql.rs): addReservedUsernamevarianthelp()arm forReservedUsernamewith actionable fix instructionsMysqlConfig::new(): addif username == "root"guard returningErr(MysqlConfigError::ReservedUsername)it_should_reject_root_as_usernamePhase 2: Make root password configurable (Bug 2)
schemas/environment-config.json: add optionalroot_passwordstring to the MySQL database objectMysqlConfig(mysql.rs): add optionalroot_passwordfield; update constructor and accessorcreate_mysql_contexts(docker_compose.rs): replaceformat!("{password}_root")with user-supplied value or a randomly generated passwordPhase 3: Move DSN to env var override and add URL-encoding (Bug 1)
percent-encodingtoCargo.tomlTrackerServiceConfig(env/context.rs): add optional database path fieldEnvContext::new_with_mysql: percent-encode username and password, build the full DSN stringTrackerContext(tracker_config/context.rs): removeMysqlTemplateConfigand the MySQL branch that builds ittemplates/docker-compose/.env.tera: addTORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATHinside{%- if mysql %}templates/docker-compose/docker-compose.yml.tera: inject the new env var into the tracker serviceenvironment:sectiontemplates/tracker/tracker.toml.tera: remove the MySQLpath =line; add a commentPhase 4: Tests
mysql.rs: addit_should_reject_root_as_usernameunit testenv/context.rs: add test thatnew_with_mysqlproduces a correctly percent-encoded DSN for a password containing special charactersenv/context.rs: add test thatnew(SQLite) leaves the database path field asNonetracker_config/context.rs: remove or update the test that referencedmysql_passwordonMysqlTemplateConfigcargo testto verify all tests passPhase 5: Linting and pre-commit
cargo run --bin linter all./scripts/pre-commit.shAcceptance Criteria
Quality Checks:
./scripts/pre-commit.shBug 3:
MysqlConfig::new()returnsErr(MysqlConfigError::ReservedUsername)when username is"root"MysqlConfigError::ReservedUsernamehas ahelp()message with an actionable fixBug 2:
root_passwordfield in the MySQL database objectroot_passwordis provided in the env JSON it is used asMYSQL_ROOT_PASSWORDin the rendered.envroot_passwordis omitted, a randomly generated password is used — it is not derived from the app passwordcreate_mysql_contextsno longer containsformat!("{password}_root")Bug 1:
tracker.tomlfor a MySQL deployment does not contain the database password.envfile containsTORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATHwith a correctly percent-encoded DSN when MySQL is configured.envfile does not containTORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATHwhen SQLite is configureddocker-compose.ymlinjectsTORRUST_TRACKER_CONFIG_OVERRIDE_CORE__DATABASE__PATHinto the tracker service environment when MySQL is configured@,+,/) produces a valid, correctly encoded DSN in the.envfileMysqlTemplateConfigno longer exists intracker_config/context.rscargo machetereports no unused dependenciesRelated