The mariadb profile
Status (source review 2026-10-21): adapter and migration assets, not a deployable
generated app profile. The CLI accepts MariaDB pruning, but only the Convex
selection path rewrites the root runtime/deployment composition. The web/worker
roots have no MySQL composition, and the previously described profile config
file is absent. The platform injects DATABASE_URL; these draft assets still
name MARIADB_URL, so their variable contract also needs reconciliation.
The tables and generated-app steps below describe the target design. Adapter
and migration-engine tests do not establish a generated application deployment.
The persistence profile for an app whose primary store is MariaDB instead of Postgres
(spec §5.1): the same procedures, the same modules, the same migrations discipline, on the
Kysely MySQL dialect (@conusai-kit/data-kysely/mysql) with jobs on BullMQ over Valkey
(@conusai-kit/jobs-bullmq).
| postgres (default) | mariadb (this profile) | |
|---|---|---|
| Primary store | Postgres 18 | MariaDB LTS |
| Data adapter | @conusai-kit/data-kysely |
@conusai-kit/data-kysely/mysql |
| Jobs | pg-boss (Postgres) | BullMQ on Valkey (@conusai-kit/jobs-bullmq) |
| Tenant wall | repository predicate + RLS (three roles, AT-D20) | repository predicate alone — MariaDB has no RLS |
| Links | postgres, identity |
mariadb, redis, identity |
| Process env | POSTGRES_URL, POSTGRES_READER_URL, POSTGRES_MIGRATE_URL |
MARIADB_URL, REDIS_URL |
What this folder carries
conusai.json— the profile's deployment contract: the same three services, linksmariadb+redis+identity. There is nomariadb.migrateflag: the three-role split it declares on Postgres (data-services DS21) exists to make row-level security bind, and MariaDB has none, so every process holds the oneMARIADB_URLand ordering is compose'sservice_completed_successfullyalone (AT-D14).compose.yaml,compose.dev.yaml— the profile's compose files. The dev overlay runsmariadb:ltsandvalkey/valkey:9-alpine, the images the platform's catalog offers.migrations/— the kernel tables (tenancy, sign-in, files, proposals, outbox, idempotency keys) as MariaDB DDL, generated bymysqlSchemaStatementsfrom the same entity definitions as the postgres files and committed, forward-only. A module's own MariaDB migrations live with the module:modules/<id>/adapters/kysely/mysql/migrations/, declared in its manifest asmigrations: { postgres: "migrations", mariadb: "adapters/kysely/mysql/migrations" }.
Target generated-app changes (not wired)
The profile is chosen at generation time (.conusai/template.json profile), and
kit template:prune --profile mariadb removes the other dialects. Concretely:
config/src/env.tsreadsMARIADB_URL(every process) instead of the three Postgres URLs — one login, because there is no RLS to bind (AT-D20 is Postgres-only).apps/webandapps/workercomposecreateMysqlDataSource(KyselyMysqlDialect+mysql2) and the worker's queue iscreateBullmqJobQueue/createBullmqWorkeroverREDIS_URL.- The migrate job runs
runMysqlMigrationsFromEnvoverprofiles/mariadb/migrations/(moved tomigrations/mariadb/) and the modules'adapters/kysely/mysql/migrations/. - This folder's
conusai.json,compose.yamlandcompose.dev.yamlreplace the root ones.
The dialect differences are explicit, never generated at run time (spec §5.3): a module's MariaDB schema is its own numbered SQL folder, reviewed like any migration.
Proof
@conusai-kit/data-kysely's MySQL dialect passes the shared repository and capability contract suites againstmariadb:ltsin testcontainers — the tenant predicate is the wall, and the contract is what proves it.@conusai-kit/jobs-bullmqpasses the shared JobQueue contract againstvalkey/valkey:9in testcontainers.scripts/vendor/test-template.sh app --profile mariadbruns the template's gate and then applies this folder's and the modules' MariaDB migrations to a real MariaDB LTS container, so the committed DDL is proven against the engine, not just generated.