Skip to main content

Portable Memory

An open, vendor-neutral format and protocol for carrying AI memory across apps, devices, and vendors — losslessly, locally, and verifiably governed.

AI assistants are starting to remember — your preferences, your projects, your history across sessions. Portable Memory is an open, vendor-neutral format for that memory — proposed and stewarded in the open (see GOVERNANCE): a .mem bundle is a plain folder of JSONL files + a manifest + checksums that any tool can read, verify, transfer, and merge. No server, no lock-in, no proprietary blob.

your-memory.mem/
  manifest.json     items/episode.jsonl     audit/tombstones.jsonl     CHECKSUMS

📄 Read the paper: Memory Belongs to the User: Portable Memory, an Open Standard Proposal for Cross-Vendor AI Memory — the vision, the design, the evidence, and an open invitation to collaborate (MacPaw Research, 2026). See Citation.

Try it in 60 seconds

Your AI memory is already portable enough to paste: every assistant now hands you a prompt that dumps it as text (Claude's and Gemini's import pages give you the standard one — "Format each entry as: [date saved, if available] - memory content"). Portable Memory turns that lossy text into something you can keep, verify, merge, and delete:

mem paste → validate → render: the pasted memory text becomes a verifiable .mem bundle and back

pip install portable-memory

# 1. Run the standard memory-export prompt in ChatGPT (or Claude, Gemini, Grok…) and save the
#    code block it returns as export.txt. Then:
mem paste export.txt --out my-memory.mem --source chatgpt

mem validate my-memory.mem          # checksums, structure, nothing injected
mem inspect  my-memory.mem          # what's inside
mem render   my-memory.mem --fence  # paste-ready text for Claude's or Gemini's memory import

Same text in, byte-identical bundle out — in Python and Swift (Conformance/fixtures/transfer/ is the proof). Paste the same export twice and it merges instead of duplicating (ids are content-derived). Also: mem ingest --from openai conversations.json (the ChatGPT data export), --from claude ~/.claude/memory (memory files), --from mem0 memories.json.

This is the Python reference SDK. The format itself is language-neutral (see Spec/ + Schemas/). Its sibling, the Swift reference SDK, lives at github.com/MacPaw/portable-memory-swift — both implementations write byte-identical bundles and share the exact same on-disk format, spec, schemas, and conformance fixture.


Why this exists

Today, the memory an assistant builds up about you is trapped. Every product stores it in its own private shape, in its own database, on its own servers:

  • You can't move it. Switch assistants or devices and you start from zero — years of context, gone.
  • There's no shared meaning. One vendor's "memory" is a text blob, another's is a graph, a third's is a chat log. Nothing speaks to anything else.
  • You can't prove it's deleted. Ask to forget something and it may linger in a vector index, an embedding cache, a knowledge graph, or a backup replica. "Deleted" is a promise, not a guarantee.

This is the data-portability gap of the AI era — the same gap the web closed for documents, mail, and calendars with open formats. Memory is more personal and higher-stakes, and it deserves the same: a format you own, that moves with you, and that deletes when you say so.

What you get

For people For builders For the industry
Own your memory. Export it, keep a local copy, move it between assistants and devices. Adopt, don't reinvent. Implement one small protocol and exchange memory with any other adopter. No lock-in. A neutral, local-first alternative to N proprietary silos.
Real deletion. A delete propagates to every derived copy, with a recorded proof-of-reach you can verify (supports GDPR / EU AI Act erasure). Trust & compliance for free. Inherit verifiable deletion, an audit trail, and an Evidence Pack you can hand to a compliance team. An ecosystem. Interop is the substrate for portable, composable AI memory.
Inspect it. Plain text + checksums — open it in any editor. Lossless on-ramp. Unknown record kinds round-trip verbatim and unknown fields on episodes ride along as ext, so adopting never costs you data (every kind: RFC-0002). A conformance bar. A badge that means something, gated on the hardest guarantee.

What makes it trustworthy

Three properties set this apart from "just another export schema":

  1. Verifiable deletion is the headline, not a footnote. A deletion is a first-class, portable tombstone that propagates to every artifact derived from the content — indexes, vectors, caches, graph edges, replicas — and records proof of what was removed. The conformance badge is gated on this (level L2): you must prove a deleted item is unreachable via every retrieval route, not merely "removed from a table."
  2. Lossless superset, not a lowest common denominator. A memory can move into the standard and back out — or between two vendors — without losing what the format doesn't model: entire vendor-specific record kinds round-trip verbatim, and fields the standard doesn't model on an episode — the atomic memory — are preserved as ext. Extending ext carriage to every record kind is proposed in RFC-0002.
  3. Local-first and inspectable. A bundle is a self-contained directory of JSONL + a manifest + a CHECKSUMS file. No server is needed to read, verify, or transfer it; source text is the portable truth and embeddings are an optional, model-tagged accelerator the receiver re-derives — so a bundle is never tied to one embedding model.

Design principles

Vendor-neutral · local-first · lossless round-trip · governed (deletions propagate, carry proof-of-reach, and are verifiable when signed) · engine-agnostic (source text is authoritative) · auditable (every mutation logged).


How it works

The format is a .mem bundle; the protocol is one Python class you subclass.

mybundle.mem/
  manifest.json            format version, capabilities, model tags, counts, integrity
  items/<kind>.jsonl       episode, entity, edge, fact, resource, chunk, core, procedure,
                           context, community, category, preference, secretRef (+ vendor kinds)
  embeddings/              OPTIONAL, model-tagged; receiver may ignore and re-embed
  audit/log.jsonl          every mutation (provenance trail)
  audit/tombstones.jsonl   deletions + redactions — applied FIRST on import
  CHECKSUMS                sha256 per file

Subclass PortableMemoryStore — map your store to/from the portable record DTOs. The SDK owns the format (bundle I/O, manifest, checksums, deterministic JSONL, tombstone-first import ordering, since filtering, ext/passthrough, path-safety). You own persistence and re-deriving your own indexes/embeddings on import. Every method has a default (empty read / no-op write), so you override only the kinds you support — a store with just episodes implements two methods, not thirty.

from datetime import datetime
from portable_memory import (
    PortableMemoryStore, PortableEpisode,
    BundleExporter, BundleImporter, BundleValidator,
)


class MyStore(PortableMemoryStore):
    def export_episodes(self) -> list[PortableEpisode]:
        ...  # map your rows -> [PortableEpisode]

    def import_episode(self, e: PortableEpisode, ext: str | None) -> None:
        ...  # upsert by id; `ext` carries foreign fields verbatim
    # …override the kinds you have; the rest default to no-op.


store = MyStore()
manifest = BundleExporter().export(store, "mybundle.mem")          # write a .mem
report   = BundleImporter().import_bundle(store, "mybundle.mem")   # merge one in
result   = BundleValidator().validate("mybundle.mem")             # L0 check

The core is pure standard libraryjson, hashlib, dataclasses, os. There is nothing to await: the Swift SDK is async only for actor isolation of its store, whereas this SDK does local file I/O synchronously, so the whole API is plain synchronous calls. The merge semantics — ordering, integrity checks, tombstone no-resurrection guard, byte-for-byte serialization — are identical.

Ingest another vendor's export with an adapter (mem0, ChatGPT, and Claude ship in the box):

from portable_memory.adapters import Mem0Adapter, OpenAIAdapter, ClaudeAdapter

episodes  = Mem0Adapter.parse_episodes(open("mem0-export.json", "rb").read())
episodes += OpenAIAdapter.parse_episodes(open("conversations.json", "rb").read())  # ChatGPT data export
episodes += ClaudeAdapter.parse_episodes([                                          # Claude memory files
    {"path": "memory/MEMORY.md", "content": open("memory/MEMORY.md").read()},
])

Deletion propagation — the trust core

This is the guarantee the whole standard rests on. A delete (or redact, for erasure) must reach everything derived from the content, and an import must apply tombstones before additions — so a bundle that still ships the stale rows can never resurrect deleted content. The tombstone carries the proof-of-reach (the ids of every artifact removed), which is exactly what an Evidence Pack turns into a compliance artifact.

Conformance levels

Level Requirement
L0 Read / Export — a valid, checksum-clean bundle
L1 Import / Merge — lossless, idempotent, bi-temporal merge
L2 Deletion propagation — honors tombstones across all derived artifacts (the badge)
L3 Governed — full audit trail, Evidence Pack, signed tombstones

Cross-vendor interoperability

The format is a superset container that absorbs other providers' memory without loss:

.mem mem0 OpenAI / ChatGPT Anthropic / Claude Letta
episode a memory a saved memory / turn a memory-tool entry archival/recall message
core profile persona/project notes core memory block
entity / edge / fact graph relations derive on import derive on import

What a source doesn't model rides in via ext (fields) and passthrough (kinds); new adapters map what's modeled and preserve the rest. Full mapping in the spec §10.


Install

pip install portable-memory

Signing (detached Ed25519 signatures over the manifest, plus signed tombstones) is optional — it lazily imports cryptography only when you actually sign or verify:

pip install "portable-memory[signing]"
import portable_memory

The core is pure standard library (json, hashlib, dataclasses) and depends on nothing — so a reader/exporter/importer that never signs pulls in zero third-party packages, on any platform that runs CPython 3.10+.

What's in this repo

📦 portable_memory/ The Python reference SDK — format types, DTOs, the PortableMemoryStore seam, BundleExporter / BundleImporter / BundleValidator, tombstones, and the mem0 adapter.
📄 Spec/portable-memory-spec.md The language-neutral specification (v1.0).
🧬 Schemas/ JSON Schemas — validate manifest.json and each record kind in any language, no SDK required.
Conformance/ The L0–L3 checklist, the deletion-propagation probe, and a sample .mem fixture.

Status: spec v1.0; this is the Python reference SDK. The Swift reference SDK is its sibling implementation of the same format. Anyone — any vendor, any language — is welcome to implement the spec and the JSON Schemas; the goal is for the format to grow outward across the ecosystem.

FAQ

Is my memory encrypted inside a .mem bundle? The bundle is plain JSONL by design — inspectable and vendor-neutral. Encryption is an envelope concern: encrypt the bundle at rest or in transit with whatever you already use (e.g. age, GPG, an encrypted archive). The format never weakens that; it just doesn't reinvent it.

What about secrets / API keys / credentials? They are never exported as plaintext or ciphertext. A secretRef carries only the metadata skeleton (label, category, sensitivity, encryption metadata); the encrypted value stays in the originating vault unless an explicit, authorized, encrypted transfer is negotiated.

What happens to embeddings across different models? Source text is the portable truth; embeddings are an optional, model-tagged accelerator. A receiver reuses vectors only if its model tag matches exactly — otherwise it re-embeds from text. The reference SDK doesn't inline vectors at all, so a bundle is never tied to one embedding model.

Do I have to support every record kind? No. Every method on PortableMemoryStore has a default (empty read / no-op write), so you implement only the kinds you actually have — a store with just episodes overrides two methods.

What if a bundle has fields or kinds my engine doesn't understand? They survive untouched. Unknown fields on an episode are preserved in ext; entire unknown record kinds round-trip verbatim. Importing another vendor's bundle and re-exporting drops nothing — that's what "lossless superset" means.

How are merges and conflicts handled? Import is merge-by-id and idempotent — re-importing the same bundle is a no-op. Bi-temporal facts and edges supersede rather than overwrite (both rows kept, validity windows set), so history is preserved and a point-in-time (as_of) view reconstructs any past state.

How do I claim a conformance level? Run the Conformance/ checklist. L0: the validator passes on your export. L1: export → import reproduces the store and a second import is a no-op. L2 (the badge): pass the deletion-propagation probe — a deleted item is unreachable via every route and a tombstone was written. L3: full audit trail + Evidence Pack + signed tombstones.

Why is deletion the conformance gate, of all things? Because it's the hardest guarantee and the one people actually test. A memory layer that can't prove a delete is gone everywhere — vector index, cache, graph, replica — is a liability the moment a user checks. Gating the badge on it makes the badge mean something.

Is this Python-only? No. The format and protocol are language-neutral (see Spec/ + Schemas/ — validate in any language). This repo is the Python reference SDK; the Swift reference SDK is its sibling, and both emit byte-identical bundles. Any other language can implement the spec directly.

Does adopting it require a server or a specific database? No. A bundle is just files, and the SDK has no storage opinion — you map your existing store through the protocol by subclassing PortableMemoryStore. It's local-first; sync/replica is opt-in.

How does this relate to MCP (Model Context Protocol)? They're complementary. MCP is a runtime protocol for connecting models to tools; Portable Memory is a data format for the memory itself. An MCP-based assistant can export/import its memory as a .mem bundle.

Is the format stable and versioned? format is semver in the manifest and capabilities[] declares optional features. Readers preserve unknown fields and kinds, so a newer bundle never loses data in an older reader.

Contributing & governance

Portable Memory is an open format proposal, not a finished standard — implementations in other languages, adapters, and spec feedback are exactly what it needs. See CONTRIBUTING, GOVERNANCE (how it's stewarded and where it's headed), and SECURITY. For how this compares to mem0 / Letta / Zep / MCP and to GDPR data-portability efforts, see spec §12 — Prior art.

Citation

If you use Portable Memory in research or products, please cite the paper (GitHub also picks this up via “Cite this repository”):

Sergii Kryvoblotskyi, Nataliia Stulova, Vladyslav Hamolia. Memory Belongs to the User: Portable Memory, an Open Standard Proposal for Cross-Vendor AI Memory. MacPaw Research, July 2026. https://research.macpaw.com/publications/portable-memory

@misc{kryvoblotskyi-2026-portable-memory,
  author = {Sergii Kryvoblotskyi and Nataliia Stulova and Vladyslav Hamolia},
  title  = {Memory Belongs to the User: Portable Memory, an Open Standard
            Proposal for Cross-Vendor AI Memory},
  month  = {July},
  year   = {2026},
  note   = {Standards proposal — preprint for community review},
  url    = {https://research.macpaw.com/publications/portable-memory}
}

License

MIT — use it, build on it, ship it.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

portable_memory-0.3.0.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

portable_memory-0.3.0-py3-none-any.whl (63.5 kB view details)

Uploaded Python 3

File details

Details for the file portable_memory-0.3.0.tar.gz.

File metadata

  • Download URL: portable_memory-0.3.0.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for portable_memory-0.3.0.tar.gz
Algorithm Hash digest
SHA256 f8459b7eee26b69b7e2586e5de0bb5853ccc8b0370007a595992f1109fc5ffb3
MD5 5e5a466033b3fff83f363bebe324a9b7
BLAKE2b-256 7e09625b6f71344347801f9bb60d6dac77e66da3c28af6db954876f2a0b35832

See more details on using hashes here.

Provenance

The following attestation bundles were made for portable_memory-0.3.0.tar.gz:

Publisher: publish.yml on MacPaw/portable-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file portable_memory-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: portable_memory-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 63.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for portable_memory-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fad490745783a58fb0f1cb36c1e0fa6931d50dd101a6e4b10b549c1e44158898
MD5 59fac41dbc1b2087d020cae23adf90de
BLAKE2b-256 73dd9c4eb67889acfbe3c9459f9d27868941c85bf36d5ae81acc779e0449c6bf

See more details on using hashes here.

Provenance

The following attestation bundles were made for portable_memory-0.3.0-py3-none-any.whl:

Publisher: publish.yml on MacPaw/portable-memory

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

0.1.2

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page