openpaw-memory-server

API-Nutzung

Diese Anleitung beschreibt die Nutzung der OpenPaw-Memory-API aus Clients wie Codex, OpenPaw/Paw, Testskripten oder später einem Signal-Bridge-Prozess.

Grundregeln

export BASE_URL='<base-url>'
export API_BASE_URL="${BASE_URL}/api"
export OPENPAW_MEMORY_TOKEN='<token>'

Standardheader:

Authorization: Bearer <token>
Content-Type: application/json

Health

Prüft, ob die API erreichbar ist und die Authentifizierung funktioniert.

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/health"

Antwort:

{
  "ok": true,
  "service": "openpaw-memory",
  "version": "0.3.0",
  "database": "mariadb"
}

Erinnerung speichern

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{
    "text": "Frank bevorzugt kleine, wartbare Lösungen.",
    "tags": ["frank", "preference", "architecture"],
    "metadata": {
      "topic": "architecture"
    },
    "kind": "preference",
    "importance": 0.8,
    "scope": "personal",
    "source": "codex",
    "source_ref": null,
    "confidence": 1.0,
    "visibility": "private",
    "observed_at": "2026-06-28T12:00:00Z"
  }' \
  "${API_BASE_URL}/memories"

Pflichtfeld:

Optionale Felder:

Standardmäßig erlaubte Werte:

Die Listen können in private/config.php unter memory.allowed_kind, memory.allowed_scope, memory.allowed_visibility und memory.allowed_source angepasst werden.

Erinnerungen auflisten

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/memories?limit=20&offset=0"

Grenzen:

Erinnerung lesen

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/memories/<id>"

Erinnerungen suchen

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/memories/search?q=wartbar%20klein&limit=10"

Die Suche verwendet MariaDB-Fulltext. Wenn Fulltext nicht verfügbar ist, nutzt die API eine einfache LIKE-Suche als Fallback.

Grenzen:

Erinnerung aktualisieren

PATCH akzeptiert Teilupdates. Nicht gesendete Felder bleiben unverändert.

curl -fsS \
  -X PATCH \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{
    "tags": ["frank", "preference", "architecture", "memory"]
  }' \
  "${API_BASE_URL}/memories/<id>"

Erinnerung löschen

curl -fsS \
  -X DELETE \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/memories/<id>"

Antwort:

{
  "deleted": true,
  "id": "<id>"
}

Chats

Chatnachrichten bleiben von Memory-Einträgen getrennt. Ein Chat besteht aus einem Thread und beliebig vielen Nachrichten. Nachrichten können optional über memory_id auf kuratierte Memory-Einträge verweisen.

Thread anlegen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"title":"Paw / Frank","channel":"web","owner_context":"openpaw"}' \
  "${API_BASE_URL}/chats"

Threads auflisten:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats?limit=20&offset=0"

Threads suchen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats/search?q=Signal&limit=20"

Die Suche prüft Thread-Titel, Kanal, Kontext und Nachrichteninhalte.

Thread lesen oder aktualisieren:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats/<id>"

curl -fsS \
  -X PATCH \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"title":"Paw / Frank notes","status":"archived"}' \
  "${API_BASE_URL}/chats/<id>"

Thread löschen:

curl -fsS \
  -X DELETE \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats/<id>"

Die zugehörigen Nachrichten werden durch die Datenbankbeziehung mit gelöscht.

Nachricht speichern:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"role":"frank","text":"Bitte merk dir diesen Verlauf.","source":"web"}' \
  "${API_BASE_URL}/chats/<id>/messages"

Nachrichten lesen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats/<id>/messages?limit=50&offset=0"

Memory für einen Thread speichern:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"text":"Die wichtigste Erkenntnis dieses Threads."}' \
  "${API_BASE_URL}/chats/<id>/memories"

Thread-Memories lesen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/chats/<id>/memories"

Der folgende ältere Endpunkt bleibt zur Kompatibilität verfügbar und ordnet das erzeugte Memory ebenfalls dem Thread zu:

curl -fsS \
  -X POST \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  "${API_BASE_URL}/chats/<id>/messages/<message-id>/memory"

Der Kompatibilitätsendpunkt setzt weiterhin chat_messages.memory_id, ordnet das Memory aber zusätzlich über chat_thread_memories dem Thread zu. Wenn die Nachricht bereits verknüpft ist, wird das vorhandene Memory zurückgegeben.

Wichtige Felder:

Pull-Bridge

Die Bridge verwendet dieselbe Bearer-Token-Authentifizierung wie die übrige API. Es werden keine eingehenden Verbindungen zum lokalen Agenten benötigt. Nachrichten, die Frank im Web-Chat sendet, werden mit Status pending gespeichert.

Pending Nachrichten atomar claimen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"limit":10}' \
  "${API_BASE_URL}/bridge/messages/claim"

Die Antwort enthält ein kurzlebiges claim_token und die geclaimten Nachrichten. Ohne Antwort werden Claims standardmäßig nach 900 Sekunden wieder freigegeben. Der Wert kann über bridge.claim_timeout_seconds konfiguriert werden.

Antwort zurückschreiben:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{"claim_token":"<claim-token>","text":"Antwort von Paw"}' \
  "${API_BASE_URL}/bridge/messages/<message-id>/reply"

Die Antwort wird als normale paw-Nachricht im ursprünglichen Thread gespeichert. Wiederholte Reply-Requests erzeugen keine doppelte Antwort.

Browser-Polling mit Website-Session:

GET /chat/messages?thread=<thread-id>&after=<last-message-id>

Alternativ unterstützt auch GET /api/chats/<id>/messages?after=<last-message-id> denselben Cursor.

Bilder an Memories

Die erste Bildstufe unterstützt JPEG, PNG und WebP. Uploads sind standardmäßig auf 10 MB, 8192 × 8192 Pixel und 40 Millionen Pixel begrenzt. Der Server ermittelt den MIME-Type aus dem Inhalt, dekodiert das Bild mit GD, kodiert es neu und entfernt dabei eingebettete EXIF-Daten. Identische normalisierte Bilder werden über SHA-256 nur einmal gespeichert.

Bild hochladen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -F "image=@screenshot.png" \
  "${API_BASE_URL}/media"

Die Antwort enthält media.id. Danach wird das Bild einem bestehenden Memory zugeordnet:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H 'Content-Type: application/json' \
  -d '{
    "media_id":"<media-id>",
    "role":"screenshot",
    "caption":"Fehler beim Start der Bridge",
    "alt_text":"Terminal mit einer Timeout-Fehlermeldung",
    "ocr_text":"BridgeError: Agent command timed out",
    "source":"openclaw",
    "source_ref":"chat:<thread-id>:<message-id>",
    "original_filename":"screenshot.png",
    "metadata":{"topic":"bridge-client"}
  }' \
  "${API_BASE_URL}/memories/<memory-id>/attachments"

Attachments auflisten und Bildinhalt abrufen:

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  "${API_BASE_URL}/memories/<memory-id>/attachments"

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -o image.png \
  "${API_BASE_URL}/attachments/<attachment-id>/content"

Erlaubte Rollen sind image, screenshot, reference und document. Standardmäßig sind höchstens zehn Attachments pro Memory erlaubt. Die Grenzen können unter media in private/config.php angepasst werden.

Das App-Backup enthält die Bilddaten und Attachment-Metadaten. Ein zusätzliches vollständiges MariaDB-Backup bleibt als zweite Sicherungsebene empfehlenswert.

Backup erstellen

Backups sind standardmäßig deaktiviert. Wenn sie in der Config aktiviert sind, ist ein separater Backup-Token Pflicht. Der Backup-Token darf nicht leer sein und darf nicht dem normalen API-Bearer-Token entsprechen.

export OPENPAW_MEMORY_BACKUP_TOKEN='<backup-token>'

curl -fsS \
  -X POST \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H "X-Backup-Token: ${OPENPAW_MEMORY_BACKUP_TOKEN}" \
  "${API_BASE_URL}/backups"

Die API schreibt ein ZIP-Archiv in das private Backup-Verzeichnis. Es enthält manifest.json mit Memories, Medien- und Attachment-Metadaten sowie die Bildinhalte unter media/. Dafür muss die PHP-Erweiterung ZipArchive verfügbar sein.

Restore

Der Restore-Endpunkt spielt eine vorhandene Backup-Datei aus dem privaten Backup-Verzeichnis zurück. Er benötigt Bearer-Token und Backup-Token.

Trockenlauf:

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.zip","mode":"upsert","dry_run":true}' \
  "${API_BASE_URL}/backups/restore"

Restore ausführen:

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.zip","mode":"upsert","dry_run":false}' \
  "${API_BASE_URL}/backups/restore"

Parameter:

upsert aktualisiert vorhandene Erinnerungen anhand der id und fügt fehlende ein. insert_only fügt nur fehlende Erinnerungen ein und überspringt vorhandene IDs. id, Inhalte, observed_at, created_at und updated_at werden aus dem Backup übernommen.

ZIP-Backups verwenden das Format openpaw-memory-backup-v2 und enthalten auch Bilddaten. Ältere JSON-Dateien im Format openpaw-memory-backup-v1 können weiterhin wiederhergestellt werden; sie enthalten nur Memories.

Antwort:

{
  "restored": true,
  "dry_run": false,
  "file": "openpaw-memory-YYYYMMDD-HHMMSS-xxxxxxxx.zip",
  "mode": "upsert",
  "count": 10,
  "inserted": 2,
  "updated": 8,
  "skipped": 0,
  "media_count": 3,
  "attachment_count": 4,
  "media_inserted": 3,
  "media_deduplicated": 0,
  "attachments_inserted": 4,
  "attachments_updated": 0,
  "attachments_skipped": 0
}

Backups auflisten

curl -fsS \
  -H "Authorization: Bearer ${OPENPAW_MEMORY_TOKEN}" \
  -H "X-Backup-Token: ${OPENPAW_MEMORY_BACKUP_TOKEN}" \
  "${API_BASE_URL}/backups"

Fehlerformat

Fehlerantworten haben dieses Format:

{
  "error": "message"
}

Wichtige Statuscodes:

Client-Hinweise