Skip to main content

plugmem

⚠️ Experimental. plugmem is mostly an AI-built experiment, written with the help of a small local model (Qwen3.6-35B-A3B-UD-Q4_K_XL.gguf) and various Claude models, in roughly equal measure. Expect non-professional design choices, rough edges, broken behavior, or mistakes. Use it at your own risk.

An embeddable bitemporal memory database for local-first applications and agents, embedded in your Python process. It stores short facts and answers a query with ranked facts and edges plus an optional bounded rendered block.

File-backed on disk, no server, no daemon. The plugmem-host engine is compiled to a CPython extension module through PyO3 and linked directly into the process, so the data lives in mapped files rather than in the interpreter's heap. Every call releases the GIL for the duration of the work.

No embedding model is required. Three of the four retrieval sources — text, graph and time — need nothing but the database. An embedder is optional and adds the fourth; see Do you need an embedder? for what changes when you add one and what you give up without it.

Contents: Install · Quick start · Do you need an embedder? · What it stores · Two clocks · How recall works · API · Errors · Configuration · Threads and the GIL · Typing · Many memories · What it is not for

Install

$ pip install plugmem

Prebuilt wheels cover Linux, macOS and Windows on x86-64 and arm64. One wheel per platform serves every CPython from 3.10 on — it is built against the stable ABI, which does not change between versions — plus a separate wheel for the free-threaded 3.14t build, which has an ABI of its own. No toolchain, no build step.

Quick start

import plugmem

db = plugmem.Plugmem.open("agent.plugmem")

db.remember("the user prefers tokio", entity="user", tags=["pref"])
db.remember("the release ships on friday", entity="release")

res = db.recall("tokio", k=5)
print(res.rendered)

page = db.list_tags(prefix="pre", limit=64)
print(page.items)            # [TagSummary(name='pref', count=1)]
# db.remove_tag("pref")      # global: revises every current fact carrying it

db.close()
## memory
- [f0] user: the user prefers tokio (2026-08; active) #pref

Plugmem is also a context manager, which is the usual way to write it:

with plugmem.Plugmem.open("agent.plugmem") as db:
    db.remember("the deploy target is fly.io", entity="release")

open is a static method rather than a constructor because it takes the file's exclusive lock, replays the journal and maps the snapshot. That work is proportional to what is on disk and should not be hidden in Plugmem(...).

Do you need an embedder?

No. The tradeoff determines how you should write queries.

Without one, three sources answer: BM25 over the text, the entity graph, and time. That is a working memory with no model, no API key, no network call and no per-query cost. What you lose is matching by meaning: BM25 needs shared words, so the query above finds the fact because both say "tokio". Ask a question that shares no terms with the stored fact:

db.recall("which runtime?", k=5)   # → no facts: no word in common

It returns nothing because "runtime" appears nowhere in "the user prefers tokio". Anchor on an entity (entities=["user"]) or use the words the fact uses, and it answers.

With one, a fourth source runs: each fact and each query is embedded, and cosine similarity finds the fact whose meaning is close even when no word matches. "which runtime?" then reaches "the user prefers tokio". The cost is a provider round trip per write and per text query, an API key, and a dim that is fixed for the life of the database.

You can also skip the provider and pass vectors yourself — see Bringing your own embedding — which is the route for a local model or one that is not an OpenAI-shaped HTTP endpoint.

Sensible default: start without one. Tag and anchor your facts, see whether lexical recall is enough for your queries, and add an embedder when you catch yourself wishing a query had understood a synonym.

What it stores

A fact is one short statement plus the things that make it findable and datable:

db.remember(
    "the user prefers tokio",
    entity="user",                       # the subject
    tags=["pref", "runtime"],            # filters
    links=[("works_on", "plugmem")],     # typed edges from the subject
    metadata={"src": "chat-2026-08-05"}, # opaque to the engine
    valid_from=1_767_225_600_000,        # when it became true (unix ms)
)

metadata is a string-to-string map the engine never interprets. It is where a URI to the real payload goes, or a mime type, or a key in your own system — the fact stays short and searchable while the bulk stays wherever you keep bulk.

remember stores and returns the new id plus any live facts that look like duplicates or contradictions. Use remember_guarded when finding one must prevent the write:

entity is what makes the guard a guard. The detector compares the new text against that entity's most recent live facts and against nothing else, so a remember_guarded call with no entity has no candidates and always returns stored - it does not fail, it simply has nothing to compare against. Pass an entity whenever avoiding a duplicate is the reason for the call.

decision.checked says whether a comparison happened at all: False is a fact stored exactly as remember would have stored it. Do not read status == "stored" as "checked and clear" without it.

decision = db.remember_guarded("the user prefers async-std", entity="user")
if decision.status == "blocked":
    for hint in decision.similar:
        print(hint.id, hint.score, hint.reason)   # 0 0.87 LexicalOverlap

The database holds one write scope across the similarity check and conditional insertion, so two concurrent preflights cannot both pass. blocked has no outcome and creates no id, index entry or journal record. Ordinary remember is also an safe complete write; it simply always stores. You decide: revise if the old fact changed, forget if it was wrong, or call ordinary remember if both are true. Do not use recall as a preflight: it ranks the best context available and its fused score is not a similarity threshold.

Two clocks

This is what separates plugmem from a store that overwrites. Every fact carries two independent intervals:

  • recorded_at — when this memory learned it. Immutable.
  • valid_from / valid_to — when the statement was true in the world.

revise closes the old interval instead of deleting the old row:

JAN = 1_767_225_600_000   # 2026-01-01
JUL = 1_782_864_000_000   # 2026-07-01

berlin = db.remember("the user lives in berlin", entity="user", valid_from=JAN)
db.revise(berlin.id, "the user lives in lisbon", entity="user", valid_from=JUL)

db.recall("lives", entities=["user"])
# → [the user lives in lisbon]

db.recall("lives", entities=["user"], closed=True)
# → [the user lives in berlin, the user lives in lisbon]

The Berlin fact is still there, with valid_to now set to JUL — the instant its successor took over. Nothing was overwritten, so "where did the user live in March" remains answerable.

as_of asks the bitemporal question, and it filters on both axes:

db.recall("lives", entities=["user"], as_of=FEBRUARY)
# → []

Empty, and that is the correct answer rather than a bug. Both facts were recorded today, so as of February this memory did not know either of them. A memory that answered would be claiming knowledge it did not have. Ask as of an instant the memory had already reached and you get whatever was true then.

How recall works

Four sources run and are fused with reciprocal-rank fusion, then a recency boost is applied. Tags filter; they are not a source.

Source What it matches Needs an embedder
lexical BM25 over the fact text no
graph facts reachable from the anchors in entities no
time facts inside the range window no
vector cosine over embeddings yes
res = db.recall(
    "who owns the deploy",
    tags=["ops"],                  # a filter, not a source
    entities=["release"],          # graph anchors
    range=(FROM_MS, TO_MS),        # window over recorded_at
    k=8,                           # facts to return
    token_budget=512,              # size of `rendered`
    graph_depth=2,                 # hops from the anchors, this call only
)

for fact in res.facts:
    print(fact.id, fact.score, fact.sources)
for edge in res.edges:
    print(edge.src, edge.rel, edge.dst)
print(res.rendered)                # the bounded block
print(res.truncated)               # True if something was left out

res.facts and res.edges are the structured answer; res.rendered is a convenience for callers that want a block of text under a token budget. Neither is more real than the other.

graph_depth is per call because how wide a net to cast belongs to the question: "what is this person's stated preference" wants fewer hops than "what is known around this person". There is no ceiling — the walk is bounded by its own entity and edge caps.

API

Everything is synchronous. See Threads and the GIL for why that is the right shape and not a limitation.

Verb Does
Plugmem.open(path=None, *, dim=None, read_only=False, config=None) open or create; resolves PLUGMEM_DB, then [database].path, then the platform data path
remember(text, *, entity, tags, links, metadata, valid_from, vector) store one fact
remember_guarded(text, *, entity, tags, links, metadata, valid_from, vector) check similarity and store only if clear, without a check/write race
remember_many(facts) store a batch — one journal write, one embedding round trip
revise(id, text, ...) close a fact's interval and record the successor
recall(query=None, *, tags, entities, as_of, range, k, closed, token_budget, ef, graph_depth, vector) the ranked answer
forget(id) tombstone a fact; maintain purges it later
forget_many(ids) tombstone a batch — one journal write, one post-write pass
remove_tag(tag) remove a tag from every current fact while preserving facts/history
link(src, rel, dst, *, provenance) / unlink(src, rel, dst) open or close a typed edge
get(id) / tags_of(id) / stats() one fact's card, its tags, engine counters
list_tags(*, prefix=None, cursor=None, limit=0) bounded lexical page of current tags and counts
export() / export_page(cursor) / export_edges(on_batch) dump facts, dump them in pages, stream edges
verify() / scrub(budget=None) logical check; byte-level check
maintain(mode="auto") / checkpoint() housekeeping; publish a snapshot
reembed(batch_size=128) explicitly recompute every retained vector with the configured model and publish atomically; maintain("auto") never invokes it
generation() / refresh() read-only handles: which snapshot, and move to the newest
config_warnings() / path() / close() config typos, the resolved file, release it

Module level: version(), about(), settings_help(), skill(), skill_full(), skill_version(), recover(src, dst), and the export_pages(db) generator.

Bringing your own embedding

Pass vector and nothing is sent to a provider — it replaces the embedder for that call. This is the route for a local model, or one that is not an OpenAI-shaped HTTP endpoint:

db = plugmem.Plugmem.open("agent.plugmem", dim=384)
db.remember("the user prefers tokio", entity="user", vector=my_model.encode(text))
db.recall(vector=my_model.encode("which runtime?"), k=5)

The length must equal the configured dim, which is fixed when the database is created.

Backing up: facts are only half of it

A fact names its own tags and metadata, but an edge is a statement between two entities and outlives any single fact. A complete dump is both streams:

facts = []
for page in plugmem.export_pages(db):      # bounded pages, not one big list
    facts.extend(page.facts)

edges = []
db.export_edges(edges.extend)              # called with a list at a time

export_edges hands over batches rather than one edge per call, because each call has to reacquire the interpreter; the walk itself runs with the GIL released. It returns the total.

Checking a file has not rotted

verify() asks whether the indexes agree with the facts. scrub() asks whether the bytes on disk are the bytes that were written — it recomputes the stored checksums, which is what catches a flipped bit that the structure happily accepts.

with db.scrub() as scan:
    for progress in scan:
        print(f"{progress.done_bytes}/{progress.total_bytes}")

It is paced by you rather than run to completion, so it is affordable on a live database. Holding the object holds a lock on the snapshot generation it is scanning, so the writer cannot recycle that file underneath it — finish the scan or close it, which the with block does for you.

Repairing a damaged file

report = plugmem.recover("damaged.plugmem", "clean.plugmem")
print(report.kept, report.dropped_text, report.dropped_vector, report.dropped_metadata)

It reads the source fact by fact, writes what survives to a new file, and reports what it had to drop. The source is left untouched as evidence. This is not a repair for structural damage: a snapshot that will not parse cannot be walked, and that case is a restore from backup.

The one thing the Rust library has and this does not

import is not an engine verb — JSONL is a format the CLI defines. If you need it, remember_many plus link is the whole of it, in a dozen lines of Python you can shape to your own file.

Errors

Every failure this binding decides raises a subclass of PlugmemError carrying a stable code. The codes are the same strings the Node binding puts on a thrown Error, so cross-language documentation stays one table.

try:
    db = plugmem.Plugmem.open("agent.plugmem")
except plugmem.LockedError as e:
    print(e.code)          # PLUGMEM_LOCKED — another process holds the writer
Class code Means
LockedError PLUGMEM_LOCKED another process holds the writer lock
NeedsCheckpointError PLUGMEM_NEEDS_CHECKPOINT read_only on a database nobody has checkpointed
ConfigError PLUGMEM_CONFIG the config.toml could not be read or is invalid
OpenError PLUGMEM_OPEN any other failure to open
InvalidArgError PLUGMEM_INVALID_ARG an argument refused before it reached the engine
InvalidNameError PLUGMEM_INVALID_NAME not a usable memory name
ClosedError PLUGMEM_CLOSED close() was already called
ReadOnlyError PLUGMEM_READ_ONLY a write verb on a read-only handle
WriterOnlyError PLUGMEM_WRITER_ONLY generation/refresh on a writer
BusyError PLUGMEM_BUSY another operation holds this handle
EngineError PLUGMEM_ENGINE the engine failed; the message is its own

Configuration and embeddings

Without a config, plugmem answers from text, tags, the graph and time. Add an [embedder] section and remember/recall also embed, giving the vector source something to work with — see Do you need an embedder? for the trade.

The file resolves the same way on every surface: an explicit path, then $PLUGMEM_CONFIG, then the platform config directory.

# plugmem.toml
[engine]
dim = 1536                      # 0 (the default) stores no vectors

[embedder]                      # omit for lexical, tag, graph and time only
url = "https://api.openai.com/v1/embeddings"
model = "text-embedding-3-small"
api_key_env = "OPENAI_API_KEY"
on_error = "degrade"            # keep answering when the provider is down
db = plugmem.Plugmem.open("agent.plugmem", config="plugmem.toml")

Every other key, its default and the OS-specific paths live in one place:

  • config.example.toml — every key with its default, commented out, ready to copy.
  • SETTINGS.md — the reference, including which sections are safe to change on an existing database.
  • plugmem.settings_help() — the same catalogue, without opening anything.

What is specific to this binding:

  • dim= is an open option too, for callers with no config file. If the config built an embedder, that embedder's dimension governs and dim must agree with it.

  • embedder_state() answers "absent", "active" or "suspended", and suspend_embedder() / resume_embedder() are the manual switches. With on_error = "degrade" the binding does it for itself: a failed call costs the vector rather than the verb, and reembed() fills the missing vectors in later. Both work on a read-only handle, which is the one that embeds its own queries, and on a WorkspaceMemory — one shared provider, but a gate per memory, so suspending one leaves its siblings answering with vectors.

  • Unknown keys are a value, not a printed warning — a library has nowhere sensible to print. Read them once after opening:

    db = plugmem.Plugmem.open("agent.plugmem", config="plugmem.toml")
    for warning in db.config_warnings():
        print(warning)
    # [recall] unknown key `w_vector` — did you mean `w_vec`?
    

A vector space that no longer matches

Changing model, space_id or dim on a database that already holds vectors does not stop it opening, on a writer or a read-only handle, and loses nothing. What fails is exactly two things: recall with a query and remember with text. Everything else — stats, get, tags_of, list_tags, entity/graph recall, export_page, forget, link, verify, maintain, checkpoint, reembed — keeps answering. So the content is safe and recovery is always available, and a consumer only finds out at its first lookup after the change: detect it by making the cheapest text recall and watching for the error, rather than from a note of what was configured last time.

reembed is idempotent; it rebuilds ONE database, so a workspace needs a pass over every memory in it. On an EMPTY database it still makes one request whose input is the empty string — a provider that rejects empty input fails a rebuild that had nothing to rebuild. And switching an embedder on over a database built without one breaks nothing and warns about nothing: compare stats().vectors with stats().facts to notice the facts that have no vectors yet.

Threads and the GIL

The Python API is deliberately synchronous. In ordinary Python code, call every method directly. Every verb releases the GIL while native work runs, so other Python threads can continue and a shared handle is safe to use from a ThreadPoolExecutor.

Releasing the GIL does not move the call to another thread. If a synchronous method is called directly from an async def, that event-loop thread remains occupied until the method returns. Async applications should send operations that can wait on I/O, an embedder, a lock, or database-sized work to asyncio.to_thread:

import asyncio

res = await asyncio.to_thread(db.recall, "tokio", k=5)
decision = await asyncio.to_thread(db.remember_guarded, "prefers tokio", entity="user")
page = await asyncio.to_thread(db.list_tags, prefix="project:", limit=64)
report = await asyncio.to_thread(db.remove_tag, "obsolete")
vectors = await asyncio.to_thread(db.reembed, 128)

Use this practical split inside an async application:

Call directly Use await asyncio.to_thread(...)
get, tags_of, stats, generation, path, config_warnings open, remember, remember_guarded, remember_many, revise, recall, forget, forget_many, remove_tag, list_tags, link, unlink
simple property/result access export, export_page, export_edges, verify, scrub, maintain, reembed, checkpoint, refresh, recover

The left column only performs bounded in-memory reads and normally returns in microseconds. The right column may open files, wait for another process, call an embedding provider, flush storage, or scan data. Using to_thread is the async bridge; the binding does not add a second runtime or pretend native work can be cancelled after it has started.

A handle is safe to share across threads. Reads can overlap; refresh and close take the handle exclusively, so a reader never observes it half-swapped.

from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=8) as pool:
    results = list(pool.map(lambda q: db.recall(q, k=4), queries))

Writes serialize inside the engine, which is a property of the engine and not of this binding — one writer per file is the design. Embedding happens outside that lock, so several remember calls do reach the provider at once.

Reading while another process writes

reader = plugmem.Plugmem.open("agent.plugmem", read_only=True)
print(reader.generation())      # which published snapshot this is
if reader.refresh():            # move to the newest one
    print("moved to", reader.generation())

A read-only handle maps a published snapshot without taking the writer's lock, so it coexists with a live writer in another process. It needs the database to have been checkpointed at least once; otherwise NeedsCheckpointError.

Typing

The package ships py.typed and generated stubs, so an editor completes the API and a type checker checks it. The stubs are generated from the same macros the binding is written with and gated in CI against the Rust surface, so they cannot describe a method that does not exist.

res: plugmem.RecallResult = db.recall("tokio", k=5)
first: plugmem.RecalledFact = res.facts[0]

Results are frozen: they are what the engine said, and mutating a copy of that is never what anyone means.

Many memories in one directory

Optional. If you want one memory, point Plugmem.open at a file and skip this.

For a process serving many independent memories — one per conversation, per tenant, per project — address them by name instead:

ws = plugmem.Workspace("~/bot-data")

memory = ws.memory("conversation-42")      # opens nothing and holds no lock
memory.remember("the user prefers dark mode", entity="user")  # first write creates

ws.describe("conversation-42", "support thread about billing", owner="ann")
for entry in ws.find("billing"):
    print(entry.db, entry.description)

A name ([a-z0-9][a-z0-9_-]*) is not a path and cannot become one, so it resolves to exactly one database inside the directory. describe is what makes find useful when the caller does not know the name; owner is recorded as an edge, so find("ann") returns what Ann owns even though no description mentions her.

The workspace owns a bounded pool of real database handles. A WorkspaceMemory owns only its name plus a weak reference to the workspace; each verb obtains a scoped handle while the GIL is released. An inactive least-recently-used database is closed to make room, while active verbs are never evicted. If every max_open slot is active, a call for a different memory raises BusyError immediately instead of waiting or opening a hidden extra handle.

release(name) closes one inactive pooled handle without invalidating logical references; their next verb reopens it. close_idle() closes entries idle past the configured timeout. Both matter because a pooled writer holds that database's exclusive OS lock. Workspace.close() invalidates every WorkspaceMemory; garbage-collecting a logical reference closes nothing.

This does not change the ordinary API: Plugmem.open(path) is still an explicitly owned database handle, and its close() releases that direct lock.

This replaces the old handle-returning call and is intentionally breaking:

# before
memory = ws.open("conversation-42")
memory.close()

# now
memory = ws.memory("conversation-42")
ws.release("conversation-42")  # optional immediate release when inactive

WorkspaceMemory has no close() because it owns no native resource. All its verbs are ordinary synchronous Python calls whose engine work runs without the GIL.

What it is not for

plugmem is for local-first application and agent memory: one process, one local database, no service to operate. Its design centre is around 100 000 active facts on one machine, and the benchmarks track 1M-operation profiles to show how the same engine behaves under heavier local load.

It is not a vector database and not built for multi-million vector workloads, cluster sharding, multi-tenant serving or managed nearest-neighbour search. For those, use a dedicated system — Qdrant, Milvus, Weaviate, Pinecone or pgvector.

Other ways in

plugmem also ships interfaces for Rust, Node, agents and the terminal.

You are Use
writing Python this package
writing JavaScript / TypeScript for Node plugmem on npm
writing Rust plugmem-host — the engine in your process
an agent, or another language plugmem-mcp — a stdio JSON-RPC sidecar
a person at a terminal plugmem-cli

Working with an LLM agent? There is a companion skill describing the remember/recall loop, the contradiction workflow and the verbs. This package ships it: skill() returns the text and skill_version() the version it was written against.

License

MIT. Source: https://github.com/m62624/plugmem

Release files for plugmem 0.12.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plugmem 0.12.0
File Size Uploaded
plugmem-0.12.0.tar.gz 680.4 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for plugmem 0.12.0
File
plugmem-0.12.0-cp314-cp314t-win_arm64.whl CPython 3.14 CPython 3.14 free-threading Windows ARM64 Details
plugmem-0.12.0-cp314-cp314t-win_amd64.whl CPython 3.14 CPython 3.14 free-threading Windows x86-64 Details
plugmem-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ x86-64 Details
plugmem-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64 Details
plugmem-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64 Details
plugmem-0.12.0-cp314-cp314t-macosx_10_12_x86_64.whl CPython 3.14 CPython 3.14 free-threading macOS 10.12+ x86-64 Details
plugmem-0.12.0-cp310-abi3-win_arm64.whl CPython 3.10 abi3 Windows ARM64 Details
plugmem-0.12.0-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
plugmem-0.12.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.10 abi3 Linux glibc 2.17+ x86-64 Details
plugmem-0.12.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.10 abi3 Linux glibc 2.17+ ARM64 Details
plugmem-0.12.0-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
plugmem-0.12.0-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 49.2 MB

Release files / plugmem-0.12.0.tar.gz

Download URL plugmem-0.12.0.tar.gz
Size 680.4 kB
Tags Source
SHA-256 checksum
How to use checksums
69911cba7461efc1a8cb9c37b1fc9567ccd170b611cc17038cbb548c0e6eba8e
BLAKE2b-256 checksum
How to use checksums
f17902585a6f912ff0382f6eadab8f99f3de23bd50c74d5d729a88786673eed9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-win_arm64.whl

Download URL plugmem-0.12.0-cp314-cp314t-win_arm64.whl
Size 3.9 MB
Tags CPython 3.14 CPython 3.14 free-threading Windows ARM64
SHA-256 checksum
How to use checksums
4428df9d498cc6b99438e53532aa1c3230606f8187fc121c980e667848d4ec2b
BLAKE2b-256 checksum
How to use checksums
3c21563945d8ae8e62c26ef11876ae2a4257841fbc15db4891417b0eae33b9ad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-win_amd64.whl

Download URL plugmem-0.12.0-cp314-cp314t-win_amd64.whl
Size 4.0 MB
Tags CPython 3.14 CPython 3.14 free-threading Windows x86-64
SHA-256 checksum
How to use checksums
7d335cc6eb5d8b46362cc8f73ba4876605a36b425c93c415a812bf895adcf29b
BLAKE2b-256 checksum
How to use checksums
57ce54bdcdd17d3fd692ad3ac6b71307fe0ab3a270affc9571b864146f8de115
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL plugmem-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.2 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ x86-64
SHA-256 checksum
How to use checksums
d457d85e7dd473b8eabee6b5ccdbc1f8b5c43a058f6c2a99f4dfb98b76c44109
BLAKE2b-256 checksum
How to use checksums
fb2dc2ae1073e90eb065115427ee30eb6110c10c96afedb70d5b3fd1a8893cd9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL plugmem-0.12.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.1 MB
Tags CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64
SHA-256 checksum
How to use checksums
dbef6136b13d1897dfe0c351aeb64b892ba51d6d02a3ac960957515de5bab580
BLAKE2b-256 checksum
How to use checksums
25e14c81915aa823ba430b57695da812f9e213192f98aa6161344963d41eb182
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl

Download URL plugmem-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl
Size 4.0 MB
Tags CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
b20e537e14855f80243f0f21c7986b041ac4d926f1c1d2faeb1ccd1a1fc098e0
BLAKE2b-256 checksum
How to use checksums
cfe7b09cb8378c3757386728d3b3744afd5bcb016be70b885f5c30167c810d8f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp314-cp314t-macosx_10_12_x86_64.whl

Download URL plugmem-0.12.0-cp314-cp314t-macosx_10_12_x86_64.whl
Size 4.1 MB
Tags CPython 3.14 CPython 3.14 free-threading macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
23fa4afb1a80b9e78baa37d5d149c5c684e19f37f9ee6d6984717983108756d3
BLAKE2b-256 checksum
How to use checksums
cdd310b03aa1007b27ca26759f3a780861d7a116bf53eab26d1bb567aa71a0c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-win_arm64.whl

Download URL plugmem-0.12.0-cp310-abi3-win_arm64.whl
Size 3.9 MB
Tags CPython 3.10 Windows ARM64 abi3
SHA-256 checksum
How to use checksums
a38114239742e7ff1d16aab2663825ee9abdc6a0504850f7ee1da5381ab7d59b
BLAKE2b-256 checksum
How to use checksums
184687dbd8363255855f6aa540b90319c39f9bb2892444090a32eb68b1fa93e2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-win_amd64.whl

Download URL plugmem-0.12.0-cp310-abi3-win_amd64.whl
Size 4.0 MB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
3fe1d85e925505136ff8eb052d34faaeceff2a37c50326f81500671f06f8b52e
BLAKE2b-256 checksum
How to use checksums
7b2ebd65db704a9b95d1892aad0079d4706993abb6e031ddac28fe1c695bc855
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL plugmem-0.12.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 4.2 MB
Tags CPython 3.10 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
32e3536eb4753dfc1e120935f1d92fc05bc68a88fa899a0175b94c1c22c2fdce
BLAKE2b-256 checksum
How to use checksums
3ce3f00c3e06c1f051ba449379d641db346f8b14fccc8b128d6db0c68869028c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL plugmem-0.12.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 4.1 MB
Tags CPython 3.10 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
d920b49d641f969c46af7bb396752317bf067bdc06ac527c401e9d856a018b2d
BLAKE2b-256 checksum
How to use checksums
37ddd05e399fa8315da76eea7bdcdf55ca944855044aa09deac90926cc2bf8c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-macosx_11_0_arm64.whl

Download URL plugmem-0.12.0-cp310-abi3-macosx_11_0_arm64.whl
Size 4.0 MB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
bb9fc77b741f10f67b6f11916aa96a639f538860acbdc2e83fb256c9b9184bcd
BLAKE2b-256 checksum
How to use checksums
bfd9f109358c514a07809434a9b99f0b954278732e65cf1adcd7ffff2beed386
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release files / plugmem-0.12.0-cp310-abi3-macosx_10_12_x86_64.whl

Download URL plugmem-0.12.0-cp310-abi3-macosx_10_12_x86_64.whl
Size 4.1 MB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
b3ec9f51b6a8d11abc2279850a2840ddae987b565e8857e47a8e8a8b0c35d2ee
BLAKE2b-256 checksum
How to use checksums
1e189d564cb50be45a9e82acb6a92a925ef456f052eff1c735991bb40bb54d72
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.12.0 This release

13 release 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