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

SchichtPfadZugriff
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:

  1. SQL-Klassifizierer: Jedes Statement bekommt anhand des ersten Tokens eine Klasse zugewiesen (read, write, ddl, destructive). Kommentare werden vorher entfernt.
  2. Admin-Gate: DDL- und destruktive Statements (CREATE, ALTER, DROP, TRUNCATE) werden nur ausgeführt, wenn die Umgebungsvariable KW_DB_ADMIN=1 gesetzt ist. Default ist 0.
  3. Protected-DB-Liste: Die Datenbank ki_web ist 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

SubcommandKlasseBedingung
list-databasesread-
list-tables [-d db] [-s schema]read-
describe TABLE [-d db] [-s schema]read-
query --sql ... oder stdinnur readrefused bei DML/DDL
execute --sql ... --yeswrite--yes; ki_web ist geschützt
admin --sql ... --yesddl / destructive--yes + KW_DB_ADMIN=1
migrate status bzw. migrate upmigrationnur up wendet an
pingread-

Tool-Surface des MCPs

ToolBedingung
list_databases, list_tables, describe_tablejederzeit
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_upup 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_web

Eine 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_demo

Migrations

db-cli migrate status
db-cli migrate up

Architektur

Gleicher DDD-Hexagonal-Schnitt wie ki-web und kw-scrape:

Env-Variablen

VariableDefaultWirkung
KW_DB_ADMIN0Auf 1 setzen, um DDL und destruktive Statements zu erlauben
KW_DB_AUDIT_LOG/var/log/kw_db/audit.logPfad zur Audit-Log-Datei
KW_DB_MIGRATIONS_DIR/var/www/db/migrationsVerzeichnis mit Migrations-Files

Exit-Codes (CLI)

CodeBedeutung
0OK
2Usage-Fehler (z. B. fehlendes --yes)
4Validierung oder Guard-Verletzung (ki_web protected, Admin-Gate)
5Backend-Fehler (psycopg-Exception, fehlgeschlagene Migration)

Was bewusst weggelassen wurde

Wichtigste Pfade

PfadInhalt
/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.jsonLogin für kw_admin
/var/log/kw_db/audit.logAudit-Log für writes, DDL und Migrationen
/usr/local/bin/db-cliCLI-Wrapper
/usr/local/bin/dbMCP-Wrapper (stdio)