Note
Kontext für LLMs und KI-Agenten: Ein strukturierter maschinenlesbarer Verzeichnisbaum, ein Architekturüberblick und ein API-Leitfaden stehen unter llms.txt bereit.
Local-first-Synchronisierung für unabhängige SQLite-Datenbanken über geprüfte Snapshots und von der Anwendung wählbare Merge-Policies. Das Modul wurde aus der BACH-ProSync-Architektur extrahiert und enthält keine Abhängigkeiten von BACH, OneDrive, Rechnernamen oder Benutzerpfaden.
Dies ist kein verteilter SQL-Server. Jeder Knoten besitzt und öffnet nur seine lokale Datenbank. Ein gemeinsamer Ordner, ein eingebundener Object Store, ein Wechseldatenträger oder ein anderer Dateitransport überträgt geschlossene Snapshots mit Manifesten. Beim Pull wird ein Snapshot geprüft und innerhalb einer Transaktion in die lokale Datenbank zusammengeführt.
Passt gut zu sync-master aus derselben
Modulfamilie: Ein sync-master-Yard ist ein natürlicher Transit-Transport. Dazu wird
--transit auf eine werkzeugeigene Zone db-transit/<namespace>/ im Yard gesetzt
(dort Protokollregel R9). sync-master transportiert die Dokumente, dieses Modul
verantwortet Datenbankintegrität und Merge; beide bleiben unabhängig.
sequenceDiagram
autonumber
participant NodeA as Node A (app.db)
participant Transit as Shared Transit (db-transit)
participant NodeB as Node B (app.db)
Note over NodeA: Live local writes
NodeA->>NodeA: SQLite Backup API (Online Snapshot)
NodeA->>Transit: Push atomic snapshot & SHA-256 manifest
Note over Transit: Verified Transit Storage (R9)
NodeB->>Transit: Read manifest & PRAGMA quick_check
NodeB->>NodeB: Transactional Row Merge (LWW / Policy)
Note over NodeB: Eventual Consistency Reached
sqlite-transit-sync ist ein Companion-Modul zu
dev-bricks/sync-master aus derselben
Modulfamilie: sync-master verantwortet den Dateitransport (sync.files), dieses
Modul die Datenbankintegrität und den Merge (sync.database). Beide sind
eigenständig oder als Teil von Stacks aus der
ellmos-ai-Familie nutzbar; siehe den
ellmos-ai/stacks-Katalog. Seine Rolle in
diesem Baukasten: kein Live-SQLite über Dateisynchronisierung, sondern ausschließlich
geprüfte Snapshots mit von der Anwendung wählbaren Merge-Policies.
- konsistente Online-Snapshots über die SQLite-Backup-API;
- atomare Veröffentlichung über eine temporäre Datei und
os.replace; - geschlossene Snapshots im Rollback-Journal-Modus mit Fail-closed-Bereinigung aller temporären SQLite-Sidecars vor der Veröffentlichung;
- SHA-256-Manifest und Prüfung mit
PRAGMA quick_check; - lokaler Pull-Zustand je Knoten und idempotente Wiederholung;
- zeilenweises Last-write-wins pro Primärschlüssel für Tabellen mit Zeitstempel;
- Merge gemeinsamer Spalten für grundlegende Toleranz gegenüber Schema-Drift;
- konfigurierbare Tabellenausschlüsse und Snapshot-Redaktion mit anschließendem
VACUUM; - inhaltsbezogener Credential-Scan, der die Veröffentlichung abbricht, wenn ein
Snapshot weiterhin zugangsdatenähnliche Werte enthält (standardmäßig aktiv;
gemeldet wird
table.column, niemals der Wert selbst); - eigene
MergePolicyfür fachliche Regeln, Tombstones oder CRDTs; - Python-API und JSON-CLI ohne zusätzliche Laufzeitabhängigkeiten.
python -m pip install -e .Auf jedem Knoten wird eine Konfiguration angelegt. Jeder Knoten verwendet seine eigene Datenbank und Zustandsdatei, aber dasselbe Transit-Verzeichnis und denselben Namespace.
sqlite-transit-sync init \
--config node.json \
--database ./app.db \
--transit ./shared-transit \
--node-id laptop \
--namespace my-app
sqlite-transit-sync push --config node.json
sqlite-transit-sync pull --config node.json --dry-run
sqlite-transit-sync pull --config node.json
sqlite-transit-sync status --config node.json
sqlite-transit-sync verify --config node.jsonDas Anwendungsschema muss auf jedem Knoten bereits existieren. Ein automatisches Erstkopieren ist absichtlich deaktiviert, weil ein generisches Modul nicht entscheiden kann, welches Schema, welche Secrets, lokalen Tabellen oder Migrationen zu einer Anwendung gehören.
{
"database": "./app.db",
"transit": "./shared-transit",
"state": "./node-state.json",
"node_id": "laptop",
"namespace": "my-app",
"timestamp_columns": ["updated_at", "modified_at", "created_at"],
"exclude_tables": ["secrets", "sqlite_sequence"],
"snapshot_exclude_tables": ["secrets"],
"scan_snapshot_for_secrets": true,
"secret_scan_skip_tables": [],
"secret_scan_extra_patterns": [],
"secret_patterns_file": null
}Relative Pfade werden vom Speicherort der Konfigurationsdatei aus aufgelöst. Die aktive Datenbank darf niemals innerhalb des Transit-Verzeichnisses liegen.
Anwendungen, die einen Audit-Hash für genau die eingelesene Konfiguration benötigen, können die Bytes einmal lesen und mit demselben quellrelativen Parser verwenden, ohne die Datei ein zweites Mal einzulesen:
import hashlib
from pathlib import Path
from sqlite_transit_sync import SyncConfig
config_path = Path("node.json").resolve()
payload = config_path.read_bytes()
config = SyncConfig.from_bytes(payload, source_path=config_path)
config_sha256 = hashlib.sha256(payload).hexdigest()| Schlüssel | Standardwert | Bedeutung |
|---|---|---|
database |
(erforderlich) | Aktive SQLite-Datenbank dieses Knotens |
transit |
(erforderlich) | Gemeinsames Verzeichnis für Snapshots und Manifeste |
state |
./.sync-state.json |
Pull-Zustand dieses Knotens; außerhalb des Transits aufbewahren |
node_id |
Rechnername | Identifiziert den veröffentlichenden Knoten in Snapshot-Namen |
namespace |
"default" |
Trennt unabhängige Datensätze innerhalb eines Transits |
timestamp_columns |
["updated_at","modified_at","created_at"] |
Für Last-write-wins geprüfte Spalten |
exclude_tables |
["secrets","sqlite_sequence"] |
Tabellen, die niemals in die lokale Datenbank gemergt werden |
snapshot_exclude_tables |
["secrets"] |
Tabellen, deren Zeilen vor der Veröffentlichung aus dem Snapshot gelöscht werden, gefolgt von VACUUM |
scan_snapshot_for_secrets |
true |
Bricht die Veröffentlichung ab, wenn der Snapshot-Inhalt weiterhin wie Zugangsdaten aussieht |
secret_scan_skip_tables |
[] |
Tabellen, die der Scan auslässt; bei einer einzelnen störenden Tabelle besser als die vollständige Abschaltung |
secret_scan_extra_patterns |
[] |
Zusätzliche reguläre Ausdrücke, ergänzend zur Trigger-Datei |
secret_patterns_file |
null (mitgelieferte Datei) |
Pfad zu einer eigenen Trigger-Datei; ersetzt die eingebauten Muster |
Der state-Standardwert in dieser Tabelle gilt, wenn eine JSON-Konfiguration ohne
diesen Schlüssel geladen wird. sqlite-transit-sync init schreibt ohne --state
stattdessen .<config-stem>-state.json (für node.json: .node-state.json).
snapshot_exclude_tables kann nur Tabellen entfernen, die bereits bekannt sind.
Der Scan beantwortet die darüber hinausgehende Frage: Sind Zugangsdaten in einer
Freitextspalte gelandet – etwa in einer Notiz, Logzeile oder Sitzungszusammenfassung?
Er läuft nach der Redaktion und vor der Veröffentlichung auf der Snapshot-Kopie.
Bei einem Treffer löst er einen SyncError mit table.column aus; der gefundene
Wert wird niemals ausgegeben und gelangt daher nicht in Logs, Tracebacks oder
CI-Ausgaben. Der unvollständige Snapshot wird verworfen, sodass nichts das
Transit-Verzeichnis erreicht.
Das Abschalten ist eine legitime Entscheidung, kein Notbehelf. Wer den Transport selbst kontrolliert und ihm vertraut – eigener Server, EU-gehostetes Volume unter eigenem Vertrag oder verschlüsselter Wechseldatenträger – und Zugangsdaten bewusst mit den Daten transportieren möchte, setzt:
{ "scan_snapshot_for_secrets": false }Wenn nur eine Tabelle Fehlalarme erzeugt, sollte secret_scan_skip_tables verwendet
werden. Der Schutz bleibt dann für alle anderen Tabellen aktiv.
Die Muster liegen als Daten statt im Code unter
sqlite_transit_sync/credential-triggers.json. Dadurch lässt sich die Erkennung
verschärfen, ohne auf ein Release zu warten:
{
"version": 1,
"patterns": [
{ "name": "github", "regex": "gh[pousr]_[A-Za-z0-9]{16,}", "prefilter": "gh" },
{ "name": "acme-internal", "regex": "ACME-[0-9]{4}", "prefilter": "ACME-" }
]
}prefilterist ein optionales Literal für einen schnellen SQL-LIKE-Vorfilter, damit große Snapshots schnell bleiben. Es muss in jedem Wert vorkommen, auf den der reguläre Ausdruck passt; andernfalls werden Funde übersehen. Fehlt ein solches Literal, wird die gesamte Spalte gelesen, damit die Prüfung korrekt bleibt.- Mit
secret_patterns_fileersetzt eine eigene Datei die Standardmuster vollständig;secret_scan_extra_patternsergänzt sie.
Die Muster sind bewusst herstellerpräfixiert. Eine allgemeine Regel für „lange hexadezimale Zeichenfolgen“ würde Prüfsummen, UUIDs und Git-SHAs erfassen, obwohl diese legitime Datenbankinhalte sind. Ein Scanner mit vielen Fehlalarmen wird abgeschaltet und schützt dann gar nicht mehr. Ein sauberer Scan bedeutet „kein bekanntes Muster gefunden“, niemals „dieser Snapshot enthält garantiert keine Secrets“.
Der Scan entfernt Zugangsdaten aus dem Synchronisierungsweg. Er verteilt sie nicht. Wenn das eigentliche Problem lautet „meine Rechner benötigen dieselben Passwörter oder API-Schlüssel“, ist dieses Modul das falsche Werkzeug – ebenso wie jeder Dokumenten-Synchronisierungsordner. Stattdessen sollte einer der folgenden Ansätze verwendet werden; alle halten Klartext von einem nicht selbst kontrollierten Anbieter fern:
| Ansatz | Geeignet für | Hinweise |
|---|---|---|
| Vaultwarden (selbst gehostetes Bitwarden) | Menschen und CLI auf mehreren Rechnern | Läuft auf einem kleinen ständig aktiven Rechner; Zugriff über ein privates Netz wie WireGuard oder Tailscale statt über eine öffentliche Freigabe. Offizielle Bitwarden-Clients, Browser-Erweiterungen und die bw-CLI funktionieren damit, sodass auch Skripte und Agenten Secrets abrufen können. |
| SOPS + age | Secrets neben dem Code | Verschlüsselte Dateien können sicher committet oder in beliebige Synchronisierungsordner gelegt werden, weil nur Chiffrat übertragen wird. Unterstützt Schlüssel je Empfänger und eignet sich gut für Git-Reviews. |
pass (GPG) + Git |
Unix-orientierte Einzelnutzer und kleine Teams | Eine Datei pro Secret, normales Git-Remote, kein Server erforderlich. |
| KeePassXC-Datenbank über Syncthing | kein Server, kein Cloud-Konto | Peer-to-Peer-Dateisynchronisierung; der Tresor bleibt eine einzelne verschlüsselte Datei. |
| Infisical / OpenBao (Vault-Fork) | Teams, Maschinenidentitäten und Rotation | Echte Secret-Server mit Audit-Logs und dynamischen Zugangsdaten; mehr bewegliche Teile, als ein Haushalt benötigt. |
| Plattformeigene Speicher | ein Rechner, eine Anwendung | macOS-Schlüsselbund, Windows DPAPI/Anmeldeinformationsverwaltung, systemd-creds oder der Secret-Store der CI. Kein Abgleich, aber auch keine Offenlegung. |
Unabhängig von der Wahl bleibt dieselbe Trennung entscheidend: ein Kanal für Daten, ein anderer für Zugangsdaten. Die Aufgabe dieses Moduls besteht darin, sicherzustellen, dass der erste Kanal nicht unbemerkt zum zweiten wird – genau das erzwingt der Scan.
from sqlite_transit_sync import SyncConfig, TransitSync
sync = TransitSync(SyncConfig.from_file("node.json"))
snapshot = sync.push()
reports = sync.pull()
print(snapshot.sha256, [report.as_dict() for report in reports])Wenn Timestamp-LWW nicht ausreicht, kann TransitSync ein Objekt erhalten, das
MergePolicy.merge(local, remote, snapshot) implementiert.
| Aspekt | sqlite-transit-sync |
Distributed SQL, zum Beispiel CockroachDB oder YugabyteDB |
|---|---|---|
| Grundmodell | Jeder Knoten besitzt eine unabhängige lokale SQLite-Datenbank | Alle Server bilden gemeinsam eine logische SQL-Datenbank |
| Schreibzugriff | Zunächst lokal, später synchronisiert | Direkt durch den Cluster koordiniert |
| Synchronisierung | Asynchroner Snapshot-Pull mit Zeilen-Merge | Laufende Replikation zwischen Clusterknoten |
| Konsistenz | Eventual Consistency nach erfolgreichem Austausch | Üblicherweise starke oder serialisierbare Konsistenz |
| Konsens und Quorum | Nicht erforderlich | Meist Raft-basierter Mehrheitskonsens |
| Globale Transaktionen | Nein | Ja, auch über mehrere Knoten oder Shards |
| Konfliktbehandlung | Anwendungsspezifische MergePolicy; standardmäßig Timestamp-LWW |
Transaktionen, MVCC, Sperren und Konsens |
| Offline-Betrieb | Ein Knoten kann unabhängig weiter lesen und schreiben | Schreibzugriffe benötigen normalerweise ein erreichbares Quorum |
| Netzwerkausfall | Lokale Arbeit läuft weiter; die Synchronisierung wartet | Minderheitspartitionen können ihre Schreibfähigkeit verlieren |
| Ausfallsicherheit | Lokale Datenbanken bleiben nutzbar; Transit und Backups benötigen eigene Absicherung | Replikation und automatisches Failover, solange ein Quorum verfügbar ist |
| Sichtbarkeit | Änderungen werden nach Push und Pull gemeinsam sichtbar | Bestätigte Änderungen sind im Cluster unmittelbar autoritativ |
| Schemaänderungen | Die Anwendung migriert jede lokale Datenbank | Clusterweite SQL-Migrationen |
| Löschungen | Benötigen Tombstones oder eine eigene Policy | Normale transaktionale SQL-Löschungen |
| Infrastruktur | Python, SQLite und ein konfigurierbarer Dateitransport | Mehrere dauerhafte Datenbankserver, TLS, Monitoring und Backups |
| Mindestzahl ständig aktiver Server | Keine; ein Knoten genügt | Für Fehlertoleranz üblicherweise mindestens drei |
| Wichtigster Vorteil | Offline-first-Einfachheit, niedrige Kosten und fachliche Merge-Regeln | Starke Konsistenz, parallele Writer und Hochverfügbarkeit |
| Wichtigste Grenze | Keine globale ACID-Transaktion oder sofortige gemeinsame Wahrheit | Deutlich höherer Betriebs- und Ressourcenaufwand |
| System | Vorteile | Nachteile | Geeignete Use Cases | Ungeeignete Use Cases |
|---|---|---|---|---|
sqlite-transit-sync |
Sehr geringer Ressourcenbedarf; offlinefähig; kein zentraler Server; lokale Datenhaltung; transportunabhängig; Merge-Regeln können der Fachdomäne folgen | Änderungen werden verzögert sichtbar; Konflikte, Löschungen, Zeitregeln und Migrationen bleiben Anwendungsverantwortung; keine globalen ACID-Transaktionen und kein Quorum-Failover | Persönliche Wissens- und Taskdatenbanken; lokale KI-Agenten; Laptop-, Workstation- und Serveraustausch; Außen- und Edge-Anwendungen; Desktop-Software mit optionaler Synchronisierung; Forschungsnotizen | Zahlungen, knappe Lagerbestände, Sitzplatzreservierungen, Echtzeit-Zusammenarbeit am selben Datensatz oder viele konkurrierende Writer |
| Distributed SQL | Gemeinsame autoritative Datenbank; starke Konsistenz; globale Transaktionen; koordinierte parallele Schreibzugriffe; automatische Replikation und Failover; horizontale Skalierung | Dauerhafte Server, Netzwerk, Zertifikate, Monitoring und Upgrades erforderlich; Quorum kann bei Partitionen Schreibzugriffe blockieren; höhere Latenz und Kosten | Finanz- und Buchungssysteme; SaaS-Plattformen; E-Commerce-Bestand; globale Benutzerkonten; Multiplayer-Backends; hochverfügbare Unternehmensdienste | Kleine persönliche Werkzeuge, zeitweise getrennte Geräte, Einzelbenutzer-Desktop-Anwendungen oder bereits zuverlässig durch lokale SQLite-Datenbanken abgedeckte Workloads |
| Anforderung | Bevorzugter Ansatz |
|---|---|
| Knoten müssen offline weiterarbeiten | sqlite-transit-sync |
| Änderungen dürfen erst nach einem Synchronisierungsschritt sichtbar werden | sqlite-transit-sync |
| Daten sollen lokal bleiben und Konflikte sind selten | sqlite-transit-sync |
| Viele Clients verändern dieselben Datensätze gleichzeitig | Distributed SQL |
| Jeder Commit muss sofort global autoritativ sein | Distributed SQL |
| Globale Transaktionen oder automatisches Cluster-Failover sind Pflicht | Distributed SQL |
Für wenige zeitweise verbundene persönliche oder Edge-Geräte ist
sqlite-transit-sync meist die einfachere Lösung. Sobald echte konkurrierende
Writer entstehen, ist häufig eine zentrale PostgreSQL-Instanz der nächste sinnvolle
Schritt. Distributed SQL wird interessant, wenn starke Konsistenz zusätzlich den
Ausfall einzelner Server über mehrere dauerhaft betriebene Knoten überstehen muss.
- Eine aktive SQLite-Datenbank niemals aus einem Netzwerk- oder Cloud-Synchronisierungsordner öffnen.
- SHA-256 erkennt Beschädigung, authentifiziert aber keinen feindlichen Transport.
- Das standardmäßige LWW setzt vergleichbare Zeitstempel voraus und leitet keine Löschungen ab.
- Gleiche Zeitstempel konvergieren über einen deterministischen Inhaltsvergleich. Dieser technische Fallback ersetzt keine fachlichen Konfliktregeln.
- Tabellen ohne Primärschlüssel oder Zeitstempelspalte werden übersprungen.
- Snapshot-Redaktion löscht gelistete Tabellen und führt anschließend
VACUUMaus. Trotzdem muss jede Tabelle mit Zugangsdaten oder privaten Daten gelistet werden; das generische Modul kann fachliche Secrets nicht zuverlässig erkennen. - Anwendungsmigrationen, Clock Policy, Aufbewahrung und Konfliktsemantik bleiben bei der integrierenden Anwendung.
- Pro Knoten darf ohne zusätzlichen Prozess-Lock der Host-Anwendung nur ein Synchronisierungsprozess laufen.
Siehe ARCHITECTURE.md, README.md und SECURITY.md.
Geprüfte Discovery-Projektion für module:sqlite-transit-sync aus
catalog:v4-bundles
(a52688938bcad21469beb546acfe6dd79ca40196a2bbaf246e5bd6aaac4bbbd7).
Das Ziel-Repository ist public. Die Bundle-Manifeste bleiben die Autorität
für Mitgliedschaften; dieser Abschnitt installiert oder aktiviert keine
Komponenten. Die Freigabe beruht auf einem öffentlichen Modul-Registry-Eintrag
und einer ausdrücklichen Default-deny-Allowlist für Bundles.
- Sichtbarkeit des Bundle-Rezepts:
private; Rolle:declared-component; Anforderung:recommended. - Modulpartner:
module:cloud-safe-exporter,module:receipt-validator,module:sync,module:system-explorer-export,module:system-gap-master. - Skill-Partner:
skill:agent-config-sync,skill:mcp-config-sync,skill:system-onboarding.
Kompositions- und Runtime-Details werden bewusst nicht offengelegt.
Für KI-Agenten, LLMs und automatisierte Werkzeuge steht unter llms.txt ein strukturierter Verzeichnisbaum mit API-Index bereit.
python -m unittest discover -s tests -vDas Modul wurde 2026 aus BACH system/hub/db_sync.py (ProSync) extrahiert. Die
eigenständige Fassung ersetzt BACH-spezifische Pfade, Handler, Secrets und
Tabellenannahmen durch Konfigurations- und Policy-Schnittstellen. Sie führt den
Merge außerdem je Primärschlüssel aus und ergänzt geprüfte Manifeste.
MIT – siehe LICENSE.
