Data and privacy¶
This page lists what Myelinate stores, what it sends to third parties, and the tools for retention, erasure and export. It describes the system's behaviour; it isn't legal advice. Map it to your own obligations (GDPR, HIPAA, internal policy).
What is stored¶
All tables live in the database configured by MYELINATE_DATABASE_URL, and every row carries a tenant_id.
| Table | Contents | Personal data? |
|---|---|---|
episodes |
Agent runs: task, messages, tool calls (arguments, results, errors), outcome, judged outcome, verification details, feedback score, comment and correction, user_id, session_id, metadata, the reflection output |
Yes: transcripts (redacted, see below) |
recalls |
Each recall: query text (redacted), user_id, session_id, IDs of what was served |
Yes |
memories |
Facts, preferences and lessons, with user_id, bi-temporal validity, provenance (episode IDs) |
Yes, especially preference |
profiles |
Per-user profile card summarising preference memories | Yes |
skills, bullets, skill_versions |
Learned procedures and rules; provenance as episode IDs; immutable version snapshots | Normally no, but rules are derived from transcripts |
changesets |
Proposed edits with rationale, ops, episode_ids, reviewer, comments |
Rarely (derived text, reviewer identity) |
experiments |
Canary state and candidate rule text | No |
pair_decisions, agent_state |
Dedup decisions; per-agent counters and lift posteriors | No |
llm_usage |
Calls and tokens per tenant, day and purpose | No |
jobs |
Background jobs (payloads hold agent and skill IDs), last error | No |
api_keys |
Key name, SHA-256 hash, scopes, revoked flag | No secrets |
audit_log |
Actor, action, resource ID, details | Actor identities (oidc:<sub>), user IDs in user.erased entries |
Embeddings, if enabled, are stored as JSON arrays on skills, bullets and memories.
PII and secret redaction¶
With MYELINATE_REDACT_PII=true (the default), text is redacted with regular expressions before it is stored:
| Label | Pattern (summary) |
|---|---|
[EMAIL] |
email addresses |
[CARD] |
13–19 digit sequences, optionally separated by spaces or dashes |
[SSN] |
123-45-6789 |
[PHONE] |
international and national phone-number shapes |
[IBAN] |
IBAN-shaped strings |
[REDACTED aws_access_key] |
AKIA… |
[REDACTED private_key] |
-----BEGIN … PRIVATE KEY----- headers |
[REDACTED api_key] |
sk-, pk-, rk- style keys (20+ chars) |
[REDACTED github_token], [REDACTED slack_token], [REDACTED jwt] |
GitHub, Slack and JWT tokens |
[REDACTED password_assignment] |
password=…, secret: …, api_key=… |
Redacted: episode task, message content, tool-call arguments, results and errors, the error field, verification details, feedback comments and corrections, recall queries, and memory content (including learned memories).
Not redacted: episode metadata (stored as sent), user_id, session_id, framework, skills_loaded, the text
of skills authored or imported through the API (it is scanned for secrets, and findings block auto-approval), and text
sent to the embeddings endpoint at recall time (the query is embedded before redaction).
Limitations:
- This is pattern matching. Names, postal addresses, IP addresses, account numbers in other formats and free-text health or financial details are not detected. If transcripts may hold such data, redact upstream (in the agent or adapter) or put a DLP proxy in front.
- The card and phone patterns can over-match long digit runs, such as order numbers or timestamps. That can remove details the learner would have used.
- Setting
MYELINATE_REDACT_PII=falsestores text verbatim.
Retention¶
Episodes are the bulk of stored personal data. myelinate purge deletes, in one run:
| Data | Older than |
|---|---|
episodes |
MYELINATE_EPISODE_RETENTION_DAYS (default 90) |
recalls (redacted query text, user_id) |
MYELINATE_EPISODE_RETENTION_DAYS |
finished jobs (done, failed) |
MYELINATE_EPISODE_RETENTION_DAYS |
audit_log entries |
MYELINATE_AUDIT_RETENTION_DAYS (default 365) |
Nothing runs it automatically. Schedule it, for example with a daily Kubernetes CronJob or cron, using the same
environment as the gateway.
What purge does not delete: memories, profiles, skills and their versions, changesets, experiments and
llm_usage. Learned skills and memories keep their provenance as episode IDs, which simply stop resolving. Set
MYELINATE_AUDIT_RETENTION_DAYS to match your compliance requirement, and export the audit log first if you need it
for longer.
Right to erasure¶
curl -s -X DELETE "$GW/v1/admin/users/<user_id>" -H "Authorization: Bearer $ADMIN_KEY"
# → {"memories": 3, "episodes": 41}
DELETE /v1/admin/users/{user_id} (admin scope) hard-deletes, within the caller's tenant, every row whose
user_id matches in memories (all versions, including superseded history), episodes, profiles and recalls. It
writes a user.erased audit entry with the counts.
What it doesn't reach:
- Shared facts. With
MYELINATE_SHARE_LEARNED_FACTS=true, non-preference facts learned from a user's runs are stored with nouser_id, so erasure can't find them. Delete them individually. - Skills. Rules are written to generalise (names replaced by placeholders), but they are derived from transcripts.
They aren't erased. Review
skill_versionsif a rule could contain personal data, and remove it with an authored change. Version snapshots stay. - Free text in other users' data, or data recorded without a
user_id. - The audit log, which keeps the erased
user_idas the resource of theuser.erasedentry. - Backups. Handle these under your backup retention policy.
- Data already sent to the LLM provider (see below).
user_id is whatever the integration sends. Erasure only works if your adapters send a stable user identifier.
Memory: hard delete vs supersede¶
- Learning never deletes. When reconciliation decides a new memory updates or contradicts an old one, the old row
gets
invalid_atandsuperseded_by, and stays queryable. - Hard delete (
DELETE /v1/memories/{id},reviewscope) removes one row. Earlier versions in its supersede chain are separate rows. Delete them too, or use user erasure.
Bi-temporal history and as_of¶
# Current view (default)
curl -s "$GW/v1/memories?agent_id=support-bot&user_id=u-42" -H "$AUTH"
# What was believed at a point in time
curl -s "$GW/v1/memories?agent_id=support-bot&user_id=u-42&as_of=2026-09-01T00:00:00Z" -H "$AUTH"
# Everything, including superseded versions
curl -s "$GW/v1/memories?agent_id=support-bot&user_id=u-42&include_history=true" -H "$AUTH"
# Memories awaiting review
curl -s "$GW/v1/memories?agent_id=support-bot&status=pending" -H "$AUTH"
POST /v1/recall also accepts as_of for a point-in-time view of memories, which is useful when auditing what an agent
was told.
User-scoped facts¶
Memories with a user_id are only visible to recalls for that user. Memories without one are visible to every user of
the agent. Memories under agent_id="*" would be visible to every agent in the tenant, but learn-scoped requests
can't write there (422), and learned memories are always stored under the recording agent.
- Preferences learned from a run always belong to that run's user.
- Other facts learned from a user's run stay with that user unless
MYELINATE_SHARE_LEARNED_FACTS=true. In that case they become agent-wide and erasure no longer reaches them. - Memories written explicitly (
POST /v1/memories, MCPremember) use theuser_idin the request.
user_id is not authenticated. Any credential in the tenant can read any user's memories (see
Security).
What is sent to third parties¶
The LLM provider¶
Learning runs in background jobs and calls the Anthropic API (MYELINATE_LLM_PROVIDER=anthropic, model
MYELINATE_LLM_MODEL). These calls send:
| Purpose | Data sent |
|---|---|
judge_outcome, reflect |
The redacted episode: task, messages (each clipped to 3000 chars), tool calls (clipped), errors, verification details, feedback comment and correction; the rules that were injected |
curate |
Reflection summaries (task, diagnosis, insights), relevant skills and rules, previously rejected edits |
memory |
Candidate memories and similar existing memories (redacted) |
profile |
A user's preference memories |
dedup |
Pairs of similar rules |
judge_skill, evolve |
Skill text and redacted episodes that used the skill |
ask |
The question (not redacted) plus matching memories, profile and rules |
The hot path (/v1/recall, /v1/episodes, /v1/feedback) never calls the LLM. /v1/ask and POST /v1/memories
(when similar memories exist) call it synchronously.
Data residency considerations:
- Requests go to
api.anthropic.comunlessANTHROPIC_BASE_URLis set; the Anthropic SDK honours it. You can route through an egress proxy or an LLM gateway in your region, as long as it passes through the Messages API features used (structured JSON output, theeffortsetting and the beta header). Amazon Bedrock and Google Vertex clients aren't supported directly. - Check your Anthropic agreement for retention and zero-data-retention terms.
MYELINATE_LLM_PROVIDER=nonedisables all LLM calls. Recording and recall still work, explicit memories can still be added (one that resembles an existing memory would need the LLM to reconcile, so it is stored aspendingfor review instead), and human-authored skills are still served. Nothing is learned automatically.
The embeddings endpoint¶
If MYELINATE_EMBEDDINGS_URL is set, recall queries (unredacted), skill descriptions, rule text and memory content are
sent there. Use a self-hosted endpoint (TEI, vLLM, Ollama) to keep this in your network.
Export¶
curl -s "$GW/v1/agents/support-bot/export?limit=10000" -H "Authorization: Bearer $REVIEW_KEY" > episodes.jsonl
GET /v1/agents/{agent_id}/export (review scope, limit ≤ 100,000) streams the agent's episodes as NDJSON, oldest
first: episode_id, task, messages, tool_calls, reward, reward_strength, served, holdout, framework,
created_at. Each export is audited as episodes.exported. It's meant for RL or fine-tuning datasets and for audits.
For a data-subject access request there is no single endpoint. Combine GET /v1/memories?user_id=…&include_history=true
for each agent, GET /v1/profiles/{agent_id}/{user_id}, and a database query on episodes and recalls filtered by
user_id.
See also: Security, Backup and upgrades, Configuration reference.