kw-db: Postgres-Zugriff via CLI und MCP
Diese Seite dokumentiert das Postgres-Werkzeug auf diesem Server. Das System folgt KISS und YAGNI: ein Cluster, ein Admin-Konto, eine Engine, je ein dünner Adapter als CLI und MCP. Backups und Multi-Cluster-Unterstützung sind bewusst weggelassen.
Was kw-db macht
kw-db ist eine Python-Library um den lokalen Postgres-Cluster (17.10). Sie kann Datenbanken auflisten, Tabellen introspizieren, Lese-Queries ausführen, Schreib-Operationen mit Schutz, sowie DDL und destruktive Operationen unter einem Admin-Gate. Eine kleine Migrations-Funktion ist enthalten (nur up, keine downs).
Drei Schichten
| Schicht | Pfad | Zugriff |
|---|---|---|
| Engine (Library) | /var/www/db/ | from kw_db.composition import Composition |
| CLI (db-cli) | /var/www/cli/db/ | Wrapper /usr/local/bin/db-cli |
| MCP-Server | /var/www/mcp/db/ | Wrapper /usr/local/bin/db (stdio JSON-RPC) |
Konto und Credentials
Es gibt einen dedizierten Postgres-Rollen-Account kw_admin mit den Flags LOGIN, CREATEDB und CREATEROLE. Keine Superuser-Rechte: das Tool darf Datenbanken und Rollen anlegen und ändern, aber nicht beispielsweise pg_hba.conf umschreiben.
Credentials liegen ausschließlich unter /var/www/_credentials/kw_admin/credentials.json mit Modus 0640 und Gruppe www-data. Das ist die einzige Stelle, an der das Passwort steht. Es wird nicht in Repos, Env-Variablen oder anderen Dateien dupliziert.
Sicherheitsmodell
Drei Schranken greifen ineinander:
- SQL-Klassifizierer: Jedes Statement bekommt anhand des ersten Tokens eine Klasse zugewiesen (read, write, ddl, destructive). Kommentare werden vorher entfernt.
- Admin-Gate: DDL- und destruktive Statements (CREATE, ALTER, DROP, TRUNCATE) werden nur ausgeführt, wenn die Umgebungsvariable
KW_DB_ADMIN=1gesetzt ist. Default ist 0. - Protected-DB-Liste: Die Datenbank
ki_webist hartcodiert geschützt. Schreibzugriffe und DDL werden mit einer eigenen Fehlerklasse abgelehnt, weil diese DB ihre Validierung über die PHP-API erhält. Lesen ist erlaubt.
Audit-Log: jedes erfolgreiche write- oder DDL-Statement landet als JSON-Zeile in /var/log/kw_db/audit.log. Felder: Zeitstempel, Operation, Datenbank, Erfolg, Detail (z. B. rows=2), erste 1000 Zeichen des SQL. Reads werden nicht geloggt.
Tool-Surface des CLIs
| Subcommand | Klasse | Bedingung |
|---|---|---|
list-databases | read | - |
list-tables [-d db] [-s schema] | read | - |
describe TABLE [-d db] [-s schema] | read | - |
query --sql ... oder stdin | nur read | refused bei DML/DDL |
execute --sql ... --yes | write | --yes; ki_web ist geschützt |
admin --sql ... --yes | ddl / destructive | --yes + KW_DB_ADMIN=1 |
migrate status bzw. migrate up | migration | nur up wendet an |
ping | read | - |
Tool-Surface des MCPs
| Tool | Bedingung |
|---|---|
list_databases, list_tables, describe_table | jederzeit |
query(sql, database?) | nur read |
execute_sql(sql, database?) | nur DML; ki_web geschützt |
execute_admin_sql(sql, database?) | DDL / destructive; nur mit KW_DB_ADMIN=1 |
migrate_status, migrate_up | up wendet pending Migrations an |
Der MCP-Server ist als kw-db in Claude Code, Codex und qwen-code registriert. Tool-Aufrufe laufen via stdio JSON-RPC durch /usr/local/bin/db.
Output-Formate
Sowohl db-cli als auch scrape-cli und ki-web-cli kennen vier Formate, gesteuert über --format: json (Default, für jq-Pipes), table (interaktive Shell), plain (eine Zeile je Record, Tab-separiert, für xargs und cut), csv (neu, mit Header-Zeile, CSV-Escaping nach stdlib). Mit --columns slug,title,updated_at lassen sich in table, plain und csv einzelne Felder herausziehen.
Migrations
Migration-Files leben unter /var/www/db/migrations/ und heißen NNNN_beschreibung.sql. Eine Buchhaltungstabelle kw_db_migrations(name PRIMARY KEY, applied_at) in der Default-DB (postgres) hält fest, welche Files schon angewendet wurden. migrate up wendet alles in Datei-Reihenfolge an, was noch nicht in der Tabelle steht. Jede Anwendung läuft in einer impliziten Transaktion.
Es gibt bewusst keine down-Migrations und kein Versions-Skipping. Wer eine Migration zurückfahren will, schreibt eine neue Migration, die das Gegenteil tut.
Beispiele
Read-only Introspektion
db-cli ping
db-cli list-databases
db-cli --format csv --columns name,owner,size_bytes list-databases
db-cli list-tables -d ki_web
db-cli --format table --columns name,data_type,is_nullable describe pages -d ki_webEine Query, eine schreibende Operation
db-cli --format json query --sql "SELECT count(*) AS n FROM pages" -d ki_web
db-cli execute -d kw_demo --yes --sql "INSERT INTO t (name) VALUES ('hi')"DDL unter Admin-Gate
KW_DB_ADMIN=1 db-cli admin --yes --sql "CREATE TABLE t (id serial primary key, name text)" -d kw_demo
KW_DB_ADMIN=1 db-cli admin --yes --sql "DROP TABLE t" -d kw_demoMigrations
db-cli migrate status
db-cli migrate upArchitektur
Gleicher DDD-Hexagonal-Schnitt wie ki-web und kw-scrape:
- Domain:
DatabaseInfo,TableInfo,ColumnInfo,RowSet,MigrationRecord, Fehlerklassen, der SQL-Klassifizierer. - Application: Ports (
SqlExecutorPort,IntrospectorPort,MigrationRunnerPort,AuditLogPort), Use Cases (DbUseCases) mit den drei Guards. - Infrastructure:
Psycopg3Executor(psycopg 3.3, short-lived Connections, autocommit aus, commit nach jedem write),PgIntrospector(Queries gegen pg_catalog),MigrationRunner(nur up),FileAuditLog(append-only JSONL).
Env-Variablen
| Variable | Default | Wirkung |
|---|---|---|
| KW_DB_ADMIN | 0 | Auf 1 setzen, um DDL und destruktive Statements zu erlauben |
| KW_DB_AUDIT_LOG | /var/log/kw_db/audit.log | Pfad zur Audit-Log-Datei |
| KW_DB_MIGRATIONS_DIR | /var/www/db/migrations | Verzeichnis mit Migrations-Files |
Exit-Codes (CLI)
| Code | Bedeutung |
|---|---|
| 0 | OK |
| 2 | Usage-Fehler (z. B. fehlendes --yes) |
| 4 | Validierung oder Guard-Verletzung (ki_web protected, Admin-Gate) |
| 5 | Backend-Fehler (psycopg-Exception, fehlgeschlagene Migration) |
Was bewusst weggelassen wurde
- Keine Backups.
pg_dumpundpg_restorestehen als Shell-Tools weiter zur Verfügung. - Keine Multi-Cluster-Unterstützung. Nur dieser Server, nur dieses Postgres, nur dieser Cluster.
- Keine Down-Migrations. Wer rückwärts will, schreibt eine neue Migration.
- Keine Connection-Pools. Pro CLI-Aufruf eine Connection. Pro MCP-Tool-Call eine Connection.
- Kein async. psycopg3 läuft synchron.
- Keine git-basierten Workflows.
Wichtigste Pfade
| Pfad | Inhalt |
|---|---|
| /var/www/db/ | Engine kw-db |
| /var/www/db/migrations/ | Versionierte SQL-Migrationen |
| /var/www/cli/db/ | CLI db-cli |
| /var/www/mcp/db/ | MCP-Server kw-db |
| /var/www/_credentials/kw_admin/credentials.json | Login für kw_admin |
| /var/log/kw_db/audit.log | Audit-Log für writes, DDL und Migrationen |
| /usr/local/bin/db-cli | CLI-Wrapper |
| /usr/local/bin/db | MCP-Wrapper (stdio) |