ADR-0031: ClickHouse's admin, apart from Langfuse¶
Status: Accepted Date: 2026-10-12 Deciders: Alex Nodeland
Amends ADR-0027: a new host's ClickHouse is restored as ClickHouse's own admin, not as Langfuse's user with the bucket's keys in the statement. Closes #140.
Context¶
ClickHouse's image makes the user CLICKHOUSE_USER names, gives it every privilege on every database, and removes its own default user. stackr named Langfuse's user so. That made Langfuse's credential ClickHouse's admin: the one an operator restored a host with, and one that a compromised Langfuse could use to reach the whole server.
Decision¶
- stackr makes ClickHouse's users itself, in
deploy/clickhouse/users.xml, and the image makes none (CLICKHOUSE_SKIP_USER_SETUP). stackr now removes thedefaultuser itself, as the image did. adminis the operators':- its password is
CLICKHOUSE_ADMIN_PASSWORD, whichmake envgenerates; - it signs in from inside the container only;
- it has every privilege, access management, and the named collections, such as a single host's
backups. It can't print a collection's secrets.
- its password is
langfuseis Langfuse's:ALLon its own database,default, where it runs its migrations;SELECTon the system tables Langfuse 4.46 reads, and no others:parts,tables,columnsandmutations, for its migrations, event propagation and native codec;processesandquery_log, where it follows a query's progress and cost;- not
system.replicas, which it reads only on a cluster, norsystem.asynchronous_metrics, which only its seeding script reads; - no other database: it can't make one, or reach another's data.
- Passwords stay off the data volume. Every password read from the environment, and the bucket's keys, are marked
hide_in_preprocessed, so the configuration ClickHouse writes to its data volume holds none of them. - The single host's
backupuser keeps what ADR-0027 gave it, with its system tables narrowed to the three the drill reads:tables,partsanddisks. - A new host's ClickHouse is restored as
admin, through thebackupscollection, so no key is written in a statement. - The smoke test checks it all. Langfuse's worker logs no missing privilege, and Langfuse's user is refused another database. On a single host,
adminrestores the latest backup, as the guide's rebuild does, into a scratch database.
Options considered¶
| Option | For | Against |
|---|---|---|
| Users in a configuration file (chosen) | Reviewed with the code; the same on one machine and on AWS's state host | A restart to change one |
| Users made in SQL, by an init job | Changed without a restart | One more job, with admin rights, before Langfuse starts |
| The image's user as admin, and a second user for Langfuse | The image's own setup | Langfuse's settings change, and the admin's name is the image's |
| An explicit list of system tables (chosen) | Langfuse reads only what it needs | A Langfuse release that reads another table fails until the list grows, which the smoke test catches |
SELECT ON system.* |
Never out of step with Langfuse | Every system table, query_log of every user's queries among them |
Consequences¶
- Easier: Langfuse's credential reaches only Langfuse's data and the system tables it reads.
- Harder: one more password in
.env, and a list of system tables to keep in step with Langfuse's releases. - Upgrading: nothing to migrate.
make uprecreates ClickHouse with its new users, andmake envadds the admin's password. By hand, use the client asCLICKHOUSE_USER=admin. - Next: the AWS recipe's state host makes the same users.