Kleiner PHP/MariaDB-Memory-Server für OpenPaw/Paw. Der Fokus liegt auf robustem Speichern, Lesen und Suchen von Erinnerungen. Die Website bietet Startseite, Login und einen geschützten Chatbereich; spätere Clients wie Signal können dieselbe API nutzen.
public/: Webroot mit Website-Front-Controller, API unterpublic/api/und.htaccess.private/: lokale Konfiguration, Runtime-Dateien und Backups. Diese Dateien gehören nicht in den öffentlichen Webroot.sql/: MariaDB-Schema.- Keine Framework-Abhängigkeiten, kein Composer, kein Python zur Laufzeit.
- MariaDB ist die Primärdatenbank. SQLite bleibt nur ein möglicher späterer Fallback, falls wirklich nötig.
Die Website stellt Startseite, Login und einen geschützten Chatbereich bereit.
Der Chat speichert Threads und Nachrichten serverseitig, ohne den API-Token an
den Browser auszugeben. Web-Nachrichten können von einem lokalen Agenten über
authentifizierte Pull-Bridge-Endpunkte geclaimt und beantwortet werden, ohne
eingehende Ports am lokalen System zu öffnen. Chatnachrichten und
Memory-Einträge bleiben getrennt,
können aber über Referenzen verbunden werden. Mehrere globale Memories können
über chat_thread_memories einem Thread zugeordnet werden. Die API speichert
Erinnerungen mit Text, Tags, Metadaten, Art, Wichtigkeit, Scope, Quelle,
Confidence, Visibility und Zeitstempeln. Die Suche verwendet MariaDB-Fulltext; falls der
Fulltext-Index nicht nutzbar ist, fällt die API auf einfache LIKE-Suche
zurück.
Vor dem Deployment prüfen:
- PHP 8.1 oder neuer ist aktiv.
- PDO MySQL ist verfügbar.
- Fileinfo und GD mit JPEG-, PNG- und WebP-Unterstützung sind für Bild-Memories verfügbar.
- MariaDB-Datenbank und Datenbankbenutzer sind eingerichtet.
.htaccessundmod_rewritefunktionieren im Zielverzeichnis.- Der Webroot kann auf
public/zeigen oder die privaten Dateien liegen zuverlässig außerhalb des Webroots. - Cronjobs sind optional für regelmäßige Backup-Aufrufe nützlich.
- Verzeichnisschutz ist für
private/vorhanden oder über Webroot-Trennung unnötig.
Minimale Diagnosen ohne Upload:
php -v
php -m | grep -E 'pdo_mysql|mysqli|mbstring'
php -l public/index.php
php -l public/api/index.phpFür Endanwender ist der Ordner release/ vorgesehen. Er enthält die
installierbaren Dateien mit public/, private/, sql/, tools/ und docs/.
Die ausführliche Anleitung steht in docs/INSTALL.md.
- Release-Paket mit
scripts/build-release.sherzeugen. - ZIP aus
dist/hochladen und auf dem Webspace entpacken oder Dateien ausrelease/hochladen. - Webserver so konfigurieren, dass
public/der Webroot ist. - Startseite öffnen und den Web-Installer ausfüllen.
- Falls Webroot-Trennung nicht möglich ist,
private/zusätzlich per.htaccesssperren und vor dem Deployment prüfen, dass kein direkter Zugriff möglich ist.
Der Web-Installer erzeugt API-Token und optional Backup-Token automatisch.
Alle Endpunkte benötigen:
Authorization: Bearer <token>Der lokale Pull-Client für Paw/OpenClaw liegt unter tools/bridge-client/.
Setup und Betrieb sind in tools/bridge-client/README.md beschrieben.
Für Beispiele:
export BASE_URL='<base-url>'
export API_BASE_URL="${BASE_URL}/api"
export OPENPAW_MEMORY_TOKEN='<token>'Health:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
"${API_BASE_URL}/health"Erinnerung speichern:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"text":"Frank bevorzugt wartbare, kleine Lösungen.","tags":["frank","preference"],"metadata":{"topic":"architecture"},"kind":"preference","importance":0.8,"scope":"personal","source":"codex","source_ref":null,"confidence":1.0,"visibility":"private"}' \
"${API_BASE_URL}/memories"Erinnerungen lesen:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
"${API_BASE_URL}/memories?limit=20&offset=0"Einzelne Erinnerung lesen:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
"${API_BASE_URL}/memories/<id>"Suchen:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
"${API_BASE_URL}/memories/search?q=wartbar%20klein&limit=10"Aktualisieren:
curl -fsS \
-X PATCH \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"tags":["frank","preference","architecture"]}' \
"${API_BASE_URL}/memories/<id>"Löschen:
curl -fsS \
-X DELETE \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
"${API_BASE_URL}/memories/<id>"Backup erstellen, wenn backup.enabled aktiv ist:
curl -fsS \
-X POST \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
-H "X-Backup-Token: ${OPENPAW_MEMORY_BACKUP_TOKEN}" \
"${API_BASE_URL}/backups"Backups auflisten:
curl -fsS \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
-H "X-Backup-Token: ${OPENPAW_MEMORY_BACKUP_TOKEN}" \
"${API_BASE_URL}/backups"Backup wiederherstellen:
curl -fsS \
-X POST \
-H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
-H "X-Backup-Token: ${OPENPAW_MEMORY_BACKUP_TOKEN}" \
-H 'Content-Type: application/json' \
-d '{"file":"openpaw-memory-YYYYMMDD-HHMMSS-xxxxxxxx.json","mode":"upsert","dry_run":false}' \
"${API_BASE_URL}/backups/restore"Pflichtfeld beim Anlegen ist text. Alle anderen Felder haben Defaults.
{
"id": "optional-client-id",
"text": "Memory text",
"tags": ["tag-a", "tag-b"],
"metadata": {
"topic": "example"
},
"kind": "note",
"importance": 0.5,
"scope": "personal",
"source": "api",
"source_ref": null,
"confidence": 1.0,
"visibility": "private",
"observed_at": "2026-06-28T12:00:00Z"
}Antworten enthalten zusätzlich created_at und updated_at als UTC-Zeit.
Standardmäßig erlaubte Klassifizierungswerte:
kind:note,preference,fact,task,event,decisionscope:personal,project,system,sessionvisibility:private,internalsource:api,codex,openpaw,paw,signal,manual,smoke-test
Diese Listen können in private/config.php unter memory.allowed_* erweitert
werden.
Die Chatfunktion nutzt eigene Tabellen:
chat_threads: Titel, Kanal, Kontext, Status, Metadaten und Zeitstempel.chat_messages: Rolle, Text, Quelle, optionale externe Message-ID, optionalememory_id, Metadaten und Zeitstempel.chat_thread_memories: Zuordnung globaler Memories zu Chat-Threads.media_objects: normalisierte JPEG-, PNG- und WebP-Bilddaten mit SHA-256.memory_attachments: Bildbeschreibung und Zuordnung zu einem Memory.
Rohverläufe bleiben Chatdaten. Dauerhaft wichtige Erkenntnisse gehören als
kuratierte Einträge in memories und können einem Thread zugeordnet
werden. Memories bleiben bewusst global, damit sie in der normalen
Memory-Suche gefunden werden. Der Bezug zum Chat-Thread wird über
chat_thread_memories sowie die Metadaten origin und chat_thread_id
gespeichert. Die ältere Message-Verknüpfung bleibt kompatibel.
Die eingebaute Backup-Funktion exportiert Memories einschließlich ihrer
Bilddaten und Attachment-Metadaten als ZIP-Datei in private/backups/. Das
Archiv enthält ein JSON-Manifest und die Bilder als Binärdateien. Dafür muss
die PHP-Erweiterung ZipArchive verfügbar sein. Die Funktion ist standardmäßig
ausgeschaltet. Wenn Backups aktiviert werden, ist ein separater backup.token
Pflicht; der normale API-Bearer-Token reicht dafür nicht.
Der Restore-Endpunkt POST /backups/restore spielt eine vorhandene Backup-Datei
aus private/backups/ zurück. Er benötigt zusätzlich zum normalen API-Token den
separaten Backup-Token. Im Modus upsert werden vorhandene Erinnerungen anhand
der id aktualisiert und fehlende Erinnerungen eingefügt. Im Modus
insert_only werden vorhandene IDs übersprungen. Mit dry_run: true kann der
Restore geprüft werden, ohne Daten zu schreiben. id, Inhalte, observed_at,
created_at und updated_at werden aus dem Backup übernommen. Der Restore
akzeptiert weiterhin ältere JSON-Backups ohne Bilddaten.
Empfohlene Einstellungen:
backup.enablederst nach erfolgreichem Grundtest aktivieren.- Einen separaten
backup.tokensetzen. Er darf nicht leer sein und darf nicht dem normalen API-Token entsprechen. - Backup-Verzeichnis außerhalb des Webroots halten.
- Optional Cronjob einrichten, der
POST /backupsmit API-Token und Backup-Token aufruft. - Vor einem Restore zuerst
POST /backups/restoremitdry_run: trueausführen und das Ergebnis prüfen.
Checkliste vor öffentlicher Nutzung:
- Keine echte
private/config.phpcommitten. - Keine Domain, keine Hoster-Details und keine Secrets in README, Tests oder Beispieldateien eintragen.
- HTTPS auf der Webserver-Seite erzwingen.
- CSP ist standardmäßig aktiv. HSTS erst aktivieren, wenn HTTPS stabil geprüft ist.
- Langen Bearer-Token verwenden und regelmäßig erneuern.
- Backup-Token getrennt vom API-Token setzen, falls Backups aktiviert werden.
private/darf nicht öffentlich abrufbar sein.- Rate Limit aktiviert lassen.
- Login-Throttling aktiviert lassen.
- Installer nach Upload direkt ausführen und danach prüfen, dass
/installnur noch „Bereits installiert“ zeigt. - Dateiberechtigungen für Config und Backups restriktiv setzen.
- Backup-Dateien regelmäßig extern sichern und alte Backups bewusst löschen.
Für einen klassischen Webspace sind folgende Berechtigungen ein sinnvoller Ausgangspunkt:
private/ 750
private/config.php 600 oder 640
private/.htaccess 644
private/backups/ 700 oder 750
private/runtime/ 700 oder 750
Backup-ZIP-Dateien 600
private/config.php enthält Datenbankzugangsdaten und Tokens und sollte daher
möglichst nur für den Besitzer lesbar sein. private/backups/ und
private/runtime/ müssen für den PHP-Prozess beschreibbar sein. Falls PHP bei
700 beziehungsweise 600 mit Permission denied scheitert, zunächst die
Gruppenrechte mit 750 beziehungsweise 640 erweitern. Verzeichnisse oder
Dateien nicht pauschal auf 777 setzen.
Nach der Installation im Browser prüfen, dass weder die Konfiguration noch das Backup-Verzeichnis öffentlich erreichbar sind:
https://meinedomain.com/private/config.phphttps://meinedomain.com/private/backups/
Beide Aufrufe müssen mit 403 Forbidden oder 404 Not Found enden und dürfen
weder Dateiinhalte noch Verzeichnislisten anzeigen. Neu erzeugte Backup-ZIPs
setzt die Anwendung automatisch auf 0600.
Der vorhandene Python-Prototyp bleibt als Referenz erhalten. Für den Webspace ersetzt diese PHP-Version die Laufzeit vollständig:
- MariaDB-Schema einspielen.
- PHP-Config erstellen.
- API lokal oder in einer nicht-öffentlichen Testumgebung prüfen.
- Falls vorhandene SQLite-Daten übernommen werden sollen, einmaligen Export aus dem Prototyp als JSON erzeugen und kontrolliert nach MariaDB importieren.
- Danach OpenPaw/Paw, Codex oder später Signal gegen dieselbe HTTP-API konfigurieren.
Es erfolgt kein Upload und kein Deployment ohne separate Freigabe.