Datenschutz¶
Wo personenbezogene Daten in einem Deployment liegen, was es verlässt und unter welcher Konfiguration, welche Kontrollen im Code existieren und einen Test hinter sich haben und welche noch offene Issues sind. Geschrieben für den, der eine Datenschutzprüfung eines Deployments beantwortet — einen Datenschutzbeauftragten, einen Sicherheitsverantwortlichen, einen Betreiber — und ehrlich gehalten über den Unterschied zwischen dem, was die Software kann, und dem, was ein Deployment tatsächlich entschieden hat.
Eine Seite ist keine Compliance
Nichts hier ist ein Nachweis, dass ein Deployment die DSGVO einhält. Die Codebasis lässt sich innerhalb einer konformen Umgebung betreiben; ob eine solche es ist, hängt von den konfigurierten Providern ab, von der entschiedenen Aufbewahrung, den unterzeichneten Verträgen und dem Betreiber, der sie führt. Jede dieser Bedingungen ist unten als etwas benannt, das zu beschaffen und zu prüfen ist, nie als etwas Angenommenes.
Standardmäßig verlässt nichts das System¶
Ein frisches Deployment hält alles in seinem eigenen PostgreSQL und auf seiner eigenen Platte und sendet nirgendwohin etwas. Jeder Sprung nach außen ist eine Zeile oder eine Einstellung, die jemand danach hinzufügt: ein Model-Profil, das einen Provider benennt, eine Collection, die einen Parser wählt, eine Suchmethode auf einem Spec, ein Logfire-Token, ein mem0-Host, ein MCP-Server, ein Kanal-Bot. Entfernen Sie die Zeile, und der Sprung ist weg.
Ein Deployment, das keine dritte Partei in der Kette will, konfiguriert sich entsprechend, und die Software macht mit:
| Thema | Die lokale Antwort |
|---|---|
| Das Chat-Model | Ein ollama- oder litellm-Profil — schlüssellos, auf einen Endpunkt gerichtet, den Sie selbst betreiben. Jeder der 27 Provider mit base_url nimmt auch ein Gateway von Ihnen |
| Dokumenten-Parsing | pymupdf, der Standard, läuft im Worker. LiteParse-OCR läuft ebenfalls im Worker oder an einem OCR-Server, den Sie als lokalen Dienst registrieren. LlamaParse ist eine Wahl je Collection und braucht einen Vault-Schlüssel; ohne einen wird nichts außer Haus geparst |
| Embeddings | Ein Ollama, das Sie betreiben, unter Knowledge → Integrations als lokaler Dienst registriert und je Collection als Provider ollama gewählt. Schlüssellos, und der einzige Provider, den eine app-scoped Collection nutzen darf |
| Traces | Lassen Sie LOGFIRE_TOKEN ungesetzt und binden Sie kein observability-Token an einen Spec oder ein Environment. Runs halten die Trace-Id weiterhin lokal fest |
| Suche, Browsing, Memory, Tools | Binden Sie kein search-Secret, keine Capability web_fetch, browser_use oder memory_mem0, keine MCP-Verbindung |
| Ihr eigenes SMTP-Relay | |
| Sprache und Bilder | Profile bei einem Provider, den Sie betreiben, oder gar kein solches Profil |
Embeddings eingeschlossen: Der Katalog nennt OpenRouter und OpenAI, die eine
Collection mit einem Vault-Schlüssel erreicht, und ollama, das eine Collection
an einem lokalen Dienst erreicht — einer Zeile, die einen Server benennt, den die
Organisation oder das Deployment betreibt. Eine Collection auf ollama schickt
ihre Chunks und Anfragen an keinen anderen Host und zahlt niemandem etwas. Ein
Deployment, das Dokumente auf eigener Hardware halten muss, legt dort jede
Collection an.
Wer wofür verantwortlich ist¶
| Partei | Rolle | Was das hier bedeutet |
|---|---|---|
| Die Organisation, die es betreibt (eine Stadt, ein Unternehmen) | Verantwortlicher | Entscheidet über Zwecke, Aufbewahrung, welche Provider ein Agent erreichen darf, und unterzeichnet die Verträge mit ihnen |
| Vstorm als Autor der Software | Keines von beiden, bei einem selbst gehosteten Deployment | Der Code läuft auf Ihrer Infrastruktur, und nichts telefoniert nach Hause. Vstorm sieht Ihre Daten nie |
| Vstorm, wenn es das Deployment für Sie betreibt | Auftragsverarbeiter | Ein Auftragsverarbeitungsvertrag ist ein Vertrag zwischen uns, keine Einstellung. Er muss vor dem ersten veröffentlichten Agent existieren |
| Ein Model-, Embedding-, Parsing-, Such- oder Observability-Provider | Unterauftragsverarbeiter, per Konfiguration gewählt | Die Plattform hält fest, welchen Provider und welchen Endpunkt jeder Agent nutzt. Deren Standort, Aufbewahrung und Trainingsbedingungen sind ihre und werden je Deployment geprüft |
Die Plattform trainiert und finetunt nichts. Sie sendet Prompts, Dokumente und Tool-Ergebnisse an die Provider, die ein Deployment konfiguriert, und speichert, was zurückkommt. Ob ein Provider API-Verkehr zum Training verwendet, ist eine Eigenschaft seines Kontos und seiner Bedingungen, und die Checkliste am Ende verlangt die Erklärung, statt sie anzunehmen.
Wo personenbezogene Daten liegen¶
Alles unten liegt im eigenen PostgreSQL des Deployments, auf seiner eigenen
Platte oder in einem Dienst, den das Deployment gewählt hat. Jede Tabelle, die
einer Organisation gehört, trägt eine organization_id; eine Kindzeile — eine
Nachricht, ein Anhang, eine Bewertung — ist über die Conversation oder den
Benutzer gescoped, an dem sie hängt, und der Lesezugriff geht über die Prüfung
des Elternteils.
Die Datenbank¶
| Speicher | Hält | Personenbezogene Daten darin | Zweck |
|---|---|---|---|
users, sessions, organization_members |
Konten und Anmeldungen | E-Mail, Name, Avatar, gehashtes Passwort oder die Google-Konto-Id, Refresh-Token-Hash, IP-Adresse und User-Agent je Session | Authentifizierung und Autorisierung |
conversations, messages, tool_calls |
Jeder Chat auf jeder Oberfläche | Der Text, den Menschen geschrieben haben, die Antworten und das Reasoning des Models, Tool-Argumente und -Ergebnisse, eine fortlaufende Zusammenfassung langer Threads | Die Kernfunktion des Produkts; die Historie, zu der ein Mensch zurückkehrt |
chat_files |
Anhänge an einer Nachricht | Dateiname, Typ, Größe, der extrahierte Text (parsed_content) und der Pfad der Bytes auf der Platte |
Antworten über eine Datei |
context_files |
Dauerhaftes Wissen, das ein Builder für Agents geschrieben hat | Was der Autor dort hineingeschrieben hat — und es erreicht den Prompt wörtlich. Siehe Kontextdateien | Anweisungen und Fakten, die ein Agent immer kennen soll |
agent_memory_files |
Notizen, die ein Agent über eine Person oder einen Gruppenchat geschrieben hat | Was der Agent für merkenswert hielt, geschlüsselt auf person:<user_id> oder einen Chatraum |
Kontinuität zwischen Conversations |
rag_documents, knowledge_bases und eine Vektortabelle je Collection |
Hochgeladene und synchronisierte Dokumente, ihre Chunks und Embeddings | Der Dokumenttext und seine Vektoren, der ursprüngliche Pfad der Datei in der Quelle | Retrieval |
agent_runs, tool_approvals, run_manifests |
Was jeder Run gekostet und getan hat | Der System-Prompt und die letzte an das Model übergebene Anfrage, Tool-Argumente, die auf Freigabe warten, die entscheidende Person und ihre Notiz | Budgets, Freigaben, Run-Historie |
agent_triggers |
Geplante und ereignisgesteuerte Runs | Der Prompt sowie Konfiguration und Filter der Ereignisquelle | Einen Agent ohne Menschen laufen lassen |
app_admin_audit_logs |
Wer Zugriff geändert oder Geld ausgegeben hat — die Spur der Organisation und die des Deployment-Administrators teilen sich eine Tabelle | Akteur, Impersonator, IP-Adresse, die Aktion und eine details-Map. Die Map benennt meist Felder, aber manche Einträge halten Werte: die E-Mail des impersonierten Kontos, die E-Mail eines vom Administrator gelöschten Kontos, eine Veröffentlichungsnotiz |
Rechenschaft. Siehe Governance |
embed_visitors, channel_identities, channel_sessions |
Fremde auf einer gehosteten Seite und Menschen auf Slack, Telegram oder Mattermost | Ein zufälliger Besucherschlüssel; eine Plattform-Benutzer-Id, ein Benutzername und ein Anzeigename; die Chat-Id | Den richtigen Thread fortsetzen |
message_ratings |
Daumen und Kommentare zu Antworten | Der Bewertende und sein Kommentar | Qualitätsprüfung |
agent_workspaces, sandbox_operations |
Dateien, an denen ein Agent gearbeitet hat, und das Log dessen, was er ausgeführt hat | Beim state-Backend die Dateien selbst, als JSON; bei einem Container die Session-Id und jedes Kommando, Ziel und Ergebnis-Resümee |
Die Sandbox. Siehe Die Sandbox |
organization_secrets, model_profiles, mcp_connections, channel_bots |
Zugangsdaten und wohin sie zeigen | Nur versiegelter Chiffretext, mit einem Hinweis; Provider, Model und base_url im Klartext |
Provider erreichen. Siehe Secrets |
messages.search_vector ist ein Volltextindex über denselben Inhalt, und
conversations.summary_messages ist eine vom Model geschriebene Verdichtung
davon. Beides sind Kopien des Chats und gehen mit ihm.
Außerhalb der Datenbank¶
| Speicher | Hält | Gelöscht, wenn |
|---|---|---|
MEDIA_DIR auf dem API-Host (Volume media_data) |
Chat-Anhänge unter <user_id>/, Avatare, Embed-Logos, generierte Bilder unter generated_<org>/ und eine temporäre Kopie jedes Dokuments unter _rag_tmp, solange es geparst wird |
Ein Dokument über das Produkt gelöscht wird. Nichts im Produkt entfernt die Bytes eines Chat-Anhangs — weder das Löschen seiner Conversation noch das Löschen seines Besitzers. Siehe Was das Löschen erreicht |
SANDBOXD_WORKSPACE_ROOT auf dem Sandbox-Host |
Die Dateien jedes containergestützten Workspace | Die Conversation gelöscht wird oder SANDBOXD_WORKSPACE_TTL sie wegräumt; ungesetzt bleiben sie unbegrenzt. Siehe Wie lange etwas überlebt |
| Redis | Rate-Limit-Buckets, geschlüsselt auf den Aufrufer — bei einer öffentlichen Oberfläche ist das die IP-Adresse im Klartext, im Schlüssel, für die TTL des Fensters; Dedup-Schlüssel für Trigger und Kanäle; OAuth-Austauschzustand; vorgemerkte Einladungen | Beim Ablauf; nichts hier überlebt seine Minuten |
| Prefect | Historie und Logs der Flow-Runs | Parameter sind Ids und Pfade, mit einer Ausnahme: der event_context eines ereignisgesteuerten Runs — Absender, Betreff und Text einer Gmail-Nachricht, eines GitHub-Issues, eines Webhook-Payloads — reist als Flow-Parameter und bleibt in der Run-Historie. Die Logs des Workers laufen durch denselben Redaktionsfilter wie die der API |
| Logfire, wenn konfiguriert | Traces jedes Runs | Die Aufbewahrung des Providers. Heute trägt ein Trace den vollen Prompt, die Ausgabe und die Tool-Argumente — siehe Traces |
| Ihr SMTP-Relay | Einladungen, Magic Links, Freigabeanfragen, Budget-Warnungen, Nutzungsberichte | Was das Relay aufbewahrt. Eine Freigabe-Mail nennt den Agent, das Tool und einen Link, nicht die Argumente des Tools |
| Backups | Ein pg_dump ist die ganze Datenbank; das Media-Volume sind die Dateien |
Ihr Backup-Ablauf. Eine Löschung erreicht ein bereits gezogenes Backup nie — siehe Backups |
Was das Deployment verlässt¶
Nichts geht hinaus, solange nicht eine Zeile oder eine Einstellung ein Ziel benennt. Dies ist die vollständige Liste der Ziele, mit der Konfiguration, die über jedes entscheidet.
| Ziel | Was gesendet wird | Entschieden durch | Standort und Bedingungen |
|---|---|---|---|
| Das Chat-Model | Die bisherige Conversation, eingefügte oder beschriebene Anhänge, abgerufene Chunks, Tool-Ergebnisse | Ein Model-Profil: provider, model, base_url und ein versiegelter Schlüssel. Siebenundzwanzig Provider; ollama und litellm sind schlüssellos und werden an einem Endpunkt erreicht, den Sie betreiben, und openai, anthropic, google, huggingface und andere akzeptieren eine base_url, ein EU-Endpunkt oder ein Gateway ist also ein Feld und keine Abzweigung |
Die des Providers. Je Profil prüfen |
| Das Embedding-Model | Jeder Chunk jedes Dokuments einer Collection und jede Retrieval-Anfrage | Je Collection, und nur dort: embedding_provider (openrouter, openai oder ollama, aus dem Katalog) und, für die ersten beiden, der Vault-Schlüssel embedding_secret_id, der zahlt. Einen deploymentweiten Embedding-Schlüssel gibt es nicht; eine schlüsselpflichtige Collection ohne einen verweigert Indexierung und Suche. ollama ist schlüssellos und wird an dem lokalen Dienst erreicht, den die Collection benennt (embedding_endpoint_id), einem Host, den Sie betreiben |
Die des Providers oder Ihr eigener Host. Eine dauerhafte Wahl |
| LlamaCloud | Das ganze Dokument | Eine Collection, deren pdf_parser llamaparse ist; sie muss einen Vault-Schlüssel benennen (llamaparse_secret_id), einen Deployment-Schlüssel gibt es nicht. Der Standard pymupdf parst im Worker |
Die von LlamaCloud, falls genutzt |
| Ein OCR-Server | Gerenderte Seiten eines Dokuments | Eine Collection, deren pdf_parser liteparse ist und deren ocr_endpoint_id einen lokalen Dienst benennt; ohne einen läuft OCR im Worker |
Ihr eigener Host — ein lokaler Dienst liegt konstruktionsbedingt im Netz des Deployments |
| Ein Bildbeschreibungs-Model | Bilder in Dokumenten | Das image_description_model einer Collection |
Die dieses Model-Providers |
| Web-Recherche | Die Suchanfrage, die der Agent formuliert hat | web_research.method auf dem Spec: duckduckgo (kein Schlüssel), tavily, brave oder exa (je ein search-Secret), oder native, wobei der Provider des Chat-Models sucht |
Die des Suchanbieters oder des Model-Providers |
| Web-Fetch und Browser-Nutzung | Die URL; bei Browser-Nutzung die ganze Aufgabe | Die Capability auf dem Spec; Browser-Nutzung braucht zusätzlich einen CDP-Endpunkt, den Sie benennen | Die abgerufene Seite; der Browser-Host |
| Die Sandbox, ausgehend | Alles im Workspace, an jeden beliebigen Host — die Runtime workbench hat ein Netz, eine Shell und curl |
Die Capability sandbox und eine Runtime mit needs_network; die Kommandofreigabe steuert, was läuft, nicht wohin es sich verbindet |
Wohin das Kommando ging. Egress-Kontrolle ist die Firewall des Sandbox-Hosts, keine Einstellung hier |
| Ein MCP-Server | Tool-Argumente und -Ergebnisse | mcp_connections.url, je Organisation oder je Person |
Die des Serverbetreibers |
| mem0 | Die für eine Person oder einen Chat geschriebenen Memories | Die base_url der Capability memory_mem0, die in MEM0_ALLOWED_HOSTS stehen muss |
Die des mem0-Hosts, den Sie zulassen |
| Logfire | Spans für jede Anfrage und jeden Run | LOGFIRE_TOKEN deploymentweit; ein observability-Token auf einem Spec oder logfire_token_secret_id auf einem Environment lenkt diese Runs in ein anderes Projekt. LOGFIRE_BASE_URL wählt das US- oder EU-Deployment. Überall ungesetzt wird nichts gesendet |
Die von Pydantic, US oder EU |
| Speech-to-Text, Bildgenerierung | Die Sprachnachricht; der Prompt | Ein Profil für groq, mistral oder openai; ein Profil für google oder openai |
Die des Providers |
| Slack, Telegram, Mattermost | Die Antworten des Agents | Eine channel_bots-Zeile mit ihrem Token im Vault |
Der Messaging-Anbieter hält den Chat ohnehin schon |
| Google-Anmeldung | Nichts ausgehend; Google liefert E-Mail, Name, Bild und Konto-Id zurück | GOOGLE_CLIENT_ID |
Die von Google |
| Ihr SMTP-Relay | Die oben genannte Post | SMTP_HOST, SMTP_TLS |
Ihre |
Sync-Konnektoren laufen andersherum: Eine Google-Drive- oder S3-Quelle zieht
Dokumente herein, authentifiziert durch ein connector-Secret, und von da an
sind die Dokumente die Kopie des Deployments und folgen den Regeln oben. Wer
lesen kann, was eine Quelle aufgenommen hat, ist
eine Entscheidung der Quellzeile.
Kontrollen, und wo jede nachgewiesen ist¶
Jede Zeile benennt den Mechanismus im Code und den Test oder die Seite, die ihn festnagelt, oder das Issue, das es tun wird. Eine Zeile, deren letzte Spalte ein Issue ist, ist eine Lücke und steht als solche da.
| Kontrolle | Mechanismus | Nachgewiesen durch |
|---|---|---|
| Mandantentrennung | organization_id auf jeder Tabelle, die einer Organisation gehört, bei jeder Anfrage aus X-Organization-Id auf eine Mitgliedschaft aufgelöst; Kindzeilen nur über die Prüfung ihres Elternteils erreichbar |
tests/integration/test_conversation_tenant_isolation.py und Geschwister; Berechtigungen |
| Zugriff auf eine Zeile | Drei Schichten: Deployment-Administrator, Rolle in der Organisation, Grant je Ressource über resolve_access. Ein Bedienelement, das der Aufrufer nicht nutzen darf, wird nicht gerendert |
Ablehnungstests in tests/api/; Berechtigungen |
| Den Chat einer anderen Person lesen | Besitzer, eine ausdrückliche Freigabe oder der App-Administrator des Deployments — nie eine Rolle in der Organisation. Conversations haben ihre eigene Prüfung, ConversationService._may_read, statt der Grant-Formel |
admin_conversations.py verlangt is_app_admin; tests/integration/test_conversation_tenant_isolation.py |
| Zugangsdaten im Ruhezustand | Envelope-Verschlüsselung je Organisation, versionierte Masterschlüssel, Rotation mit Trockenlauf | Secrets, vier durch Tests festgenagelte Garantien |
| Inhalte im Ruhezustand | Von der Anwendung nicht verschlüsselt. Die Postgres-Daten, media_data und das Workspace-Wurzelverzeichnis der Sandbox verlassen sich auf Platten- oder Volume-Verschlüsselung, die Sie bereitstellen |
Kontrolle des Betreibers. Ein S3-Backend mit serverseitiger Verschlüsselung für Dateien ist #1423 |
| Auf dem Transportweg, eingehend | HTTPS an Ihrem Proxy; Strict-Transport-Security, wenn ENVIRONMENT=production; Session-Cookies httpOnly, und secure aus dem Schema der Anfrage bei Anmeldung und Refresh. Die Route für den Passwortwechsel setzt secure nur in einem Produktions-Build |
Deploy; frontend/src/app/api/auth/login/route.ts |
| Auf dem Transportweg, zu den Speichern | POSTGRES_SSLMODE und REDIS_SSL; agenticos cmd doctor meldet, ob die hergestellte Verbindung verschlüsselt war |
Verschlüsselte Verbindungen; tests/integration/test_store_tls.py |
| Auf dem Transportweg, zu den Providern | HTTPS zu jedem katalogisierten Endpunkt. Eine eigene base_url wird ohne Host oder mit Zugangsdaten darin abgelehnt, aber http:// wird akzeptiert, für ein Ollama oder ein Gateway im Netz des Deployments selbst; ein Klartext-HTTP-Profil, das aus diesem Netz hinauszeigt, sendet Prompts und Schlüssel im Klartext. Punkt 4 der Checkliste listet jedes solche Profil |
refused_field("base_url", ...) im Model-Profile-Service; das Schema ist Sache des Betreibers |
| Secrets in Antworten, Logs, Audit, Exporten | Kein Endpunkt gibt einen Klartext zurück; SecretStr überall; Specs referenzieren Secrets per Id |
Secrets |
| Personenbezogene Daten in Logs | app/core/logging.py redigiert E-Mail-Adressen, JWTs, API-Schlüssel, Bearer-Token und password=-Paare aus jedem Log-Record, in API wie Worker |
tests/test_logging.py; der Worker installiert es in prefect_app.py (#440) |
| Personenbezogene Daten, die das Model erreichen | Die Capability guardrails redigiert IBANs, Kartennummern, US-Sozialversicherungsnummern und E-Mail-Adressen aus Prompts, Antworten und Tool-Ergebnissen, sofern konfiguriert |
Capabilities; ihre Tests unter tests/ |
| Personenbezogene Daten in einer Fehlerspalte | rag_documents.error_message und Verwandte halten Stufe und Klasse fest, nie den Text des Kunden |
app/services/rag/failures.py (#423) |
| Rechenschaft | Audit-Einträge teilen die handelnde Transaktion und scheitern geschlossen; Impersonation nennt beide Personen; Massenexporte werden festgehalten | Governance |
| Audit-Export | GET /audit/export, CSV oder JSONL über ein Fenster, auf audit:read gegated und in der Spur selbst festgehalten |
Governance (#1422) |
| Manipulationsnachweis der Spur | Noch keiner | #1622 |
| Traces | observability.content je Agent: full zeichnet alles auf, none nur Zeit, Tokens, Kosten und Tool-Namen |
Umgebungen (#1413); redacted ist #1616 |
| Aufbewahrung nach Zeitplan | Nur sandbox_operations-Zeilen werden weggeräumt, nach 30 Tagen. Das Wegräumen abgebrochener Runs finalisiert sie; es löscht nichts |
#1420 |
| Löschung einer Person | Die Kontolöschung bereinigt, was sie blockieren würde; die Löschung des Memory ist ein eigener Aufruf und reicht bis mem0 | Was das Löschen erreicht; #1421 für das, was es zurücklässt |
| Zugang zu den eigenen Daten | Kein Export-Endpunkt; keine Sicht auf das eigene Memory | #1421, #1594 |
| Unternehmensidentität | Google-Anmeldung und Passwörter; noch kein OIDC | #1419 |
| Die Kontrollmatrix, die eine Sicherheitsprüfung liest | Diese Seite und Einführen | #1412 ergänzt die Zuordnung zu HIPAA und SOC 2 |
| Öffentliche Oberflächen | Der Besucherschlüssel einer gehosteten Seite ist zufällig, nie aus der Person abgeleitet; Einlass und Uploads sind je Adresse ratenbegrenzt, die Adresse liegt für die Dauer des Fensters in einem Redis-Schlüssel und sonst nirgends | Kanäle |
| Rechtliche Hinweise | Die eigenen AGB- und Datenschutz-URLs des Deployments ersetzen die eingebauten Seiten | Das Deployment |
Traces¶
instrument_pydantic_ai() läuft mit dem Standard der Bibliothek, ein Span hält
also die Nachricht des Benutzers, die Antwort des Models und jedes Tool-Argument
und -Ergebnis. Mit ungesetztem LOGFIRE_TOKEN, ohne observability-Token auf
irgendeinem Spec und ohne logfire_token_secret_id auf irgendeinem Environment
wird nichts gesendet, und die Trace-Id wird trotzdem lokal festgehalten. Ein
Deployment, das Traces ohne den Inhalt braucht, setzt observability.content des
Agents auf none: aufgezeichnet werden Zeit, Tokens, Kosten und Tool-Namen, und
kein Nachrichtentext verlässt die Maschine. Alles dazwischen — der Inhalt durch
einen PII-Filter — ist #1616.
Was das Löschen erreicht¶
Löschen ist das, was das Produkt heute tut, wenn jemand darum bittet; geplante Aufbewahrung ist #1420.
| Aktion | Entfernt | Lässt zurück |
|---|---|---|
DELETE /conversations/{id} (der Besitzer) |
Die Conversation, ihre Nachrichten, Tool-Aufrufe, Bewertungen, Freigaben und chat_files-Zeilen, per Kaskade; ein Container-Workspace wird über purge_for_conversation gesäubert |
Die Bytes der Anhänge unter MEDIA_DIR. Keine Route löscht eine Chat-Datei; der einzige Codepfad, der eine solche entfernt, verwirft den verwaisten Upload eines Kanal-Bots. Run-Zeilen und Manifeste, die die Conversation benannten, behalten ihre Prompt-Kopie. Verfolgt in #1421 |
DELETE /memory/person/{user_id} (die Person oder members:manage) |
Jede agent_memory_files-Zeile, die auf die Person geschlüsselt ist, über alle Agents der Organisation hinweg, und dasselbe in jedem gebundenen mem0-Speicher |
Notizen, die auf einen Gruppenchat geschlüsselt sind, in dem die Person gesprochen hat |
DELETE /users/{id} |
Das Konto, seine Sessions, seine persönliche Organisation und persönlichen Collections samt ihren Vektortabellen und Dateien, durch ausdrücklichen Abbau; Conversations und Chat-Dateien per Kaskade | Das Memory der Person — owner_key ist ein String und kein Fremdschlüssel, agent_memory_files- und mem0-Einträge überleben also, sofern nicht zuvor DELETE /memory/person lief. Audit-Einträge, die die Akteurs-Id und bei manchen Aktionen die E-Mail nennen; Nachrichten in geteilten Conversations; die Anhang-Bytes von oben. Die Inventur davon ist das Ergebnis von #1421 |
| Ein Dokument oder eine Collection löschen | Die Zeilen, die Vektortabelle und die gespeicherte Datei, über einen dauerhaften Flow nach dem Commit | Nichts, sobald der Flow gelaufen ist; die Zählstände in sync_logs bleiben |
| Eine Organisation löschen | Alles, was auf sie gescoped ist, mit demselben aufgeschobenen Abbau | Persönliche Collections, die die Id lediglich mitführten |
Keines davon erreicht ein Backup. Eine Wiederherstellung bringt zurück, was gelöscht wurde, der Backup-Ablauf gehört also zur Aufbewahrungsrichtlinie und wird mit ihr zusammen niedergeschrieben.
Was ein Deployment entscheiden und beschaffen muss¶
Die Software kann nichts davon liefern. Jedes ist ein Nachweis, den die Prüfung verlangen wird, und etwas anderes als die technische Fähigkeit, die ihn möglich macht.
- Ein Auftragsverarbeitungsvertrag mit jedem konfigurierten
Unterauftragsverarbeiter — jedem Provider, den eine Zeile in
model_profilesoderorganization_secretsbenennt, dem Embedding-Provider, LlamaCloud, falls eine Collection es nutzt, dem Suchanbieter, den diemethodeines Agents benennt, dem mem0-Host, Logfire, dem SMTP-Relay und Google, falls die Anmeldung aktiviert ist. - Eine Erklärung zum Datenstandort je Provider, abgeglichen mit der
base_url, die jedes Profil tatsächlich nutzt. Ein Provider mit EU-Endpunkt ist nur dann in der EU, wenn das Profil es sagt. - Ein Trainingsausschluss je Provider: die Kontoeinstellung oder Vertragsklausel, unter der API-Daten nicht zum Training verwendet werden. Die eigene Position der Plattform ist ein Satz — sie trainiert nichts — und der Rest ist deren Sache.
- Ein Auftragsverarbeitungsvertrag mit Vstorm, nur wenn Vstorm das Deployment betreibt.
- Ein Aufbewahrungsplan für Conversations, Dateien, Memory, Dokumente, Runs und Audit, und daneben der Backup-Ablauf. Bis #1420 einen erzwingt, ist Aufbewahrung manuelles Löschen.
- Platten- oder Volume-Verschlüsselung auf dem Datenbank-Host, dem Media-Volume und dem Sandbox-Host, da die Anwendung Inhalte nicht selbst verschlüsselt — und eine Egress-Regel auf dem Sandbox-Host, wenn Agents Kommandos ausführen dürfen.
- Die Rechtsseiten, auf die das Deployment verlinkt, und wer eine Auskunfts- oder Löschanfrage beantwortet, solange #1421 offen ist.
Ein Deployment prüfen¶
Reproduzierbare Prüfungen, vom Host aus, gegen das laufende Deployment. Jede
gibt Fakten aus, die die Prüfung beilegen kann; keine gibt Zugangsdaten oder die
Daten einer Person aus. Führen Sie die Kommandos aus backend/ aus oder über
docker compose exec api.
# 1. Läuft es, und sind die Verbindungen zu den Speichern verschlüsselt?
# `postgres` meldet den TLS-Zustand der Verbindung, die der Doctor selbst
# hergestellt hat.
uv run agenticos cmd doctor
# 2. Jede versiegelte Zugangsinformation öffnet sich noch unter den
# konfigurierten Masterschlüsseln.
uv run agenticos cmd vault-rotate --dry-run
# 3. Die Einstellungen, die entscheiden, was hinausgeht. Leer ist die leise
# Antwort.
env | grep -E '^(ENVIRONMENT|LOGFIRE_TOKEN|LOGFIRE_BASE_URL|MEM0_ALLOWED_HOSTS|POSTGRES_SSLMODE|REDIS_SSL|SMTP_TLS|LOG_PROVIDER_WRITE_TO_DISK|RATE_LIMIT_TRUST_FORWARDED_FOR)=' \
| sed -E 's/(KEY|TOKEN)=.+/\1=<set>/'
LOG_PROVIDER_WRITE_TO_DISK muss außerhalb der Entwicklung false sein: Der
loggende E-Mail-Provider schreibt sonst ganze Mailtexte auf die Platte.
-- 4. Jeder Provider und Endpunkt, den ein Agent erreichen kann, ohne die
-- Schlüssel.
SELECT o.name AS organization, p.label, p.provider, p.model, p.base_url
FROM model_profiles p JOIN organizations o ON o.id = p.organization_id
ORDER BY 1, 2;
-- Profile, die Klartext-HTTP sprechen. Jedes muss auf das eigene Netz des
-- Deployments zeigen; alles andere sendet Prompts und Schlüssel im Klartext.
SELECT label, provider, base_url FROM model_profiles WHERE base_url LIKE 'http://%';
SELECT o.name AS organization, s.purpose, s.kind, s.name
FROM organization_secrets s JOIN organizations o ON o.id = s.organization_id
ORDER BY 1, 2;
-- Collections: wer sie embeddet, und welche außer Haus parsen.
SELECT name, embedding_provider, embedding_model,
ingestion_config ->> 'pdf_parser' AS pdf_parser,
ingestion_config ->> 'llamaparse_secret_id' IS NOT NULL AS llamaparse_key,
embedding_endpoint_id, ingestion_config ->> 'ocr_endpoint_id' AS ocr_endpoint_id
FROM knowledge_bases ORDER BY 1;
-- Die Server im eigenen Netz, auf die Collections gerichtet werden dürfen.
-- Jede Adresse hier sollte eine sein, die Sie betreiben.
SELECT o.name AS organization, s.kind, s.provider, s.name, s.base_url, s.is_active
FROM local_services s LEFT JOIN organizations o ON o.id = s.organization_id
ORDER BY 1 NULLS FIRST, 2, 4;
SELECT scope, name, url, auth_type FROM mcp_connections WHERE is_enabled ORDER BY 1, 2;
SELECT name, connector_type, collection_name FROM sync_sources WHERE is_active ORDER BY 2, 1;
-- 5. Runs, die in ein eigenes Projekt getract werden: ein Token auf dem
-- veröffentlichten Spec oder auf einem Environment.
SELECT a.slug, v.version, 'spec' AS via
FROM agent_versions v JOIN agents a ON a.id = v.agent_id
WHERE v.spec -> 'observability' ->> 'token_secret_id' IS NOT NULL
UNION ALL
SELECT a.slug, NULL, 'environment ' || e.name
FROM agent_environments e JOIN agents a ON a.id = e.agent_id
WHERE e.logfire_token_secret_id IS NOT NULL;
-- 6. Was eine Aufbewahrung erreichen müsste. Passen Sie das Alter an den
-- entschiedenen Plan an.
SELECT 'conversations' AS store, count(*) FROM conversations WHERE created_at < now() - interval '365 days'
UNION ALL SELECT 'agent_runs', count(*) FROM agent_runs WHERE created_at < now() - interval '365 days'
UNION ALL SELECT 'audit', count(*) FROM app_admin_audit_logs WHERE created_at < now() - interval '365 days'
UNION ALL SELECT 'agent_memory_files', count(*) FROM agent_memory_files
UNION ALL SELECT 'chat_files', count(*) FROM chat_files;
# 7. Anhang-Bytes, deren Zeilen fort sind. Eine chat_files-Zeile verschwindet
# per Kaskade mit ihrer Nachricht, während die Datei bleibt, die Differenz
# wächst also mit jeder gelöschten Conversation (siehe „Was das Löschen
# erreicht"). Generierte Bilder und das Parse-Arbeitsverzeichnis haben
# absichtlich keine Zeile und sind ausgenommen. Über den Datenbank-Container:
# Die API kennt ihren Connection String nur als berechnete Einstellung, nicht
# als Variable, die eine Shell lesen könnte.
docker compose exec -T db psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atc "SELECT storage_path FROM chat_files
UNION SELECT storage_path FROM rag_documents WHERE storage_path IS NOT NULL" \
| sort > /tmp/referenced.txt
(cd "${MEDIA_DIR:-./media}" && find . -type f -not -path './generated_*' -not -path './_rag_tmp/*' \
| sed 's|^\./||' | sort) > /tmp/on_disk.txt
comm -23 /tmp/on_disk.txt /tmp/referenced.txt | wc -l # Dateien, die nichts referenziert
Avatare und Embed-Logos liegen ebenfalls auf der Platte und werden aus
users.avatar_url und agent_embeds.logo_path referenziert; nehmen Sie diese
Spalten in die Abfrage auf, wenn die Zahl oben nicht null ist und Sie die Liste
genau haben wollen.
Legen Sie die Ausgabe von 1 bis 6 der Prüfung zusammen mit den Verträgen aus dem vorigen Abschnitt bei. Punkt 7 ist eine Zahl, die zu beobachten ist, bis #1421 die Bytes mit der Conversation entfernt.
Offene Bedingungen für eine erste Einführung¶
Festgehalten für das Deployment, für das diese Seite geschrieben wurde, und zutreffend für jedes Deployment, bis jede einzelne geschlossen ist.
Im Code, verfolgt:
- Traces tragen vollen Inhalt, sofern ein Agent
observability.contentnicht aufnonesetzt; ein gefiltertes Dazwischen gibt es nicht — #1616. - Keine geplante Aufbewahrung — #1420.
- Anhang-Bytes und das Memory einer Person überleben die Löschung ihres Besitzers; kein Export personenbezogener Daten; die Löschinventur — #1421.
- Kein Manipulationsnachweis der Audit-Spur — #1622.
- Dateien nur auf lokaler Platte, vom Volume verschlüsselt oder gar nicht — #1423.
- Keine Selbstbedienungssicht auf das eigene Memory — #1594.
- Keine OIDC-Anmeldung — #1419.
- Die Kontrollmatrix für HIPAA und SOC 2 — #1412.
Im Deployment, vom Betreiber entschieden: die Verträge, Standorte, Trainingsausschlüsse, der Aufbewahrungsplan, der Backup-Ablauf, die Plattenverschlüsselung, der Sandbox-Egress und die Rechtsseiten aus dem vorigen Abschnitt.
Eine Prüfung, die jede Zeile oben entweder geschlossen oder schriftlich akzeptiert vorfindet, hat, was diese Seite ihr geben kann. Der Rest gehört dem Deployment.