======================================================================== FLUCTLIGHTDB — DOCUMENTATION (plain text, for machine readers) ======================================================================== Generated from the same source as https://fluctlightdb.com/docs — the two cannot disagree. The same source is also published as Markdown at /docs.md. A JSON facts file is at /facts.json. Other Markdown variants, from the same modules as the HTML: /index.md, /quickstart.md, /install.md, /faq.md, /compare.md. The install HowTo page is /install. Every signature below was read off the installed package with inspect.signature; every printed result came back from a real call against a real brain. Nothing here is illustrative. ======================================================================== 1. THE CONTRACT — read this and you can write a correct call ======================================================================== FluctlightDB — embedded memory for agents. Python, Rust core. INSTALL pip install "fluctlightdb[native]" OPEN from fluctlightdb import connect_embedded brain = connect_embedded("./agent-brain") # a brain is a directory WRITE brain.experience(text, context=..., salience=0.0-1.0, verified=True, source_uri="file://wallet.json") -> {"engram_id": str, "deduplicated": bool, "gate_rejected": bool, "gate_reason": str|None, "confusion_risk": float, "separation": {...}} RECALL brain.activate(cue, semantic_vector=None, agent_id=None, limit=None) -> {"recalls": [{"engram_id", "activation", "verified", "trust_note", "completion_strength", "separation_index", "episode": {"content", "context", "salience_hint", "provenance"}}], "active_neurons": int, "hops": int, "myelinated": bool} PERSIST brain.checkpoint() -> None HARD RULES 1. Recall is LEXICAL unless you pass semantic_vector on BOTH the write and the cue. No embedder ships with the engine. activate("theme preference") does NOT find "User prefers dark mode". 2. activate() and recall() return DIFFERENT shapes. activate -> "recalls"; recall -> "hits"/"lanes_used"/"mode". 3. One brain directory per agent. Sharing one brain across agents drops provenance separation to 18% top-1. 4. There is NO /api/v1/checkpoint HTTP endpoint. Only POST /api/v1/experience and POST /api/v1/activate exist. 5. Anything named after neuroanatomy (chorus_*, muon_*, tau_*, cortex_*, neurogenesis) is internal and unstable. Do not call it. 6. Nothing is durable until checkpoint() returns. ======================================================================== 2. MISTAKES AGAINST THIS API ======================================================================== These are the six failures that actually happen, in the order they are hit. 1. Expecting paraphrase to recall WRONG: brain.experience("User prefers dark mode") brain.activate("theme preference") # -> 0 recalls RIGHT: vec = my_embedder.encode("User prefers dark mode") brain.experience("User prefers dark mode", semantic_vector=vec) brain.activate("theme preference", semantic_vector=my_embedder.encode("theme preference")) WHY: Without a vector on both sides the cue has to share tokens with the stored text. The engine ships no embedder, so this is the default and it is silent — you get an empty list, not an error. 2. Reading activate() results as recall() results WRONG: hits = brain.activate(cue)["hits"] # KeyError RIGHT: hits = brain.activate(cue)["recalls"] # activate hits = brain.recall(cue)["hits"] # recall WHY: Two different calls with two different shapes. activate() returns recalls / active_neurons / hops / myelinated. recall() returns hits / lanes_used / mode. 3. Calling a checkpoint endpoint over HTTP WRONG: POST /api/v1/checkpoint # does not exist RIGHT: POST /api/v1/experience POST /api/v1/activate # checkpointing is in-process only: brain.checkpoint() WHY: Only the two routes exist on the HTTP surface. A checkpoint route reads like it should be there, which is exactly why it gets invented. 4. Sharing one brain between agents WRONG: brain = connect_embedded("./shared-brain") # then handed to every worker RIGHT: brain = connect_embedded(f"./brains/{agent_id}") # or pass agent_id= on every experience() and activate() WHY: Under a shared brain the project measures provenance separation at 18% top-1 and calls it unaddressed in code. Per-case isolation reaches 100%. 5. Treating the return of experience() as the memory WRONG: memory = brain.experience(text) print(memory["content"]) # KeyError RIGHT: result = brain.experience(text) engram_id = result["engram_id"] if result["gate_rejected"]: log.info("declined: %s", result["gate_reason"]) WHY: experience() returns a receipt, not the stored episode. It can also decline a write it considers duplicate — check gate_rejected rather than assuming success. 6. Reaching for the interesting-sounding methods WRONG: brain.chorus_recall(cue) brain.tau_recall(cue) brain.neurogenesis() RIGHT: brain.activate(cue) WHY: Every neuroanatomically named method is an internal lane that can change without a major version. They are on the object because Python has no private, not because they are API. ======================================================================== 3. INSTALL ======================================================================== pip install "fluctlightdb[native]" The [native] extra pulls the compiled Rust core. Without it you get the pure-Python fallback, which is slower and is not what the measurements were taken on. ======================================================================== 4. YOUR FIRST BRAIN ======================================================================== from fluctlightdb import connect_embedded # A brain is a directory. This creates it if it is not there. brain = connect_embedded("./my-agent-brain") # experience() — write what happened, with its context and how much it matters brain.experience("User prefers dark mode", context="settings", salience=0.7) # A memory that came from a file, not from a conversation brain.experience( "Wallet balance is $42.10", context="ledger:wallet", verified=True, source_uri="file://wallet.json", salience=0.9, ) # activate() — recall from a cue hits = brain.activate("dark mode") for hit in hits["recalls"]: print(hit["episode"]["content"], "verified:", hit["verified"]) # checkpoint() — make it survive the process brain.checkpoint() ======================================================================== 5. WHAT A BRAIN IS ON DISK ======================================================================== my-agent-brain/ ├── CURRENT 25 B └── generations/ └── gen-00000000000000000001/ ├── manifest.json 436 B ├── hippocampus.seg 2,954 B ├── graph.seg 4,136 B ├── recent_separations.seg 2,560 B ├── cortex.seg 116 B ├── amygdala.seg 104 B ├── life.seg 110 B ├── tau.seg 696 B ├── muon.seg 336 B ├── development.seg 52 B ├── swarm.seg 36 B ├── prefrontal.seg 26 B ├── semantic.seg 26 B ├── neuromodulators.seg 16 B ├── autonomic.seg 61 B └── core_memories.seg 8 B CURRENT names the live generation. checkpoint() writes a whole new generation and then advances that pointer, which is what makes the swap atomic: a process killed mid-write leaves the previous generation intact and still named. ======================================================================== 6. experience() — WRITING ======================================================================== experience( content: str, *, context: str = "api", salience: float = 0.5, outcome: str | None = None, semantic_vector: list[float] | None = None, agent_id: str | None = None, tenant_id: str | None = None, verified: bool | None = None, provenance_kind: str | None = None, source_uri: str | None = None, confidence: float | None = None, doc_id: str | None = None, chunk_id: str | None = None, **extra, ) -> dict content (str) What happened, as text. This is what a cue matches against. context (str) A label for where the memory came from — "settings", "ledger:wallet", a tool name. Free-form; it is stored and returned, not parsed. salience (float) How much this matters, 0 to 1. A magnitude only. The engine stores no valence, so this is not emotion and nothing here is positive or negative. verified / source_uri (bool / str) Together these put the memory in the top provenance tier. See Provenance below — this is the field that changes ranking. semantic_vector (list[float]) An embedding you computed yourself. The engine ships no embedder. Without one, recall is lexical — see below. Returns: { "engram_id": "5a95e923-b6eb-4284-99cf-725b840afdcf", "deduplicated": false, "gate_rejected": false, "gate_reason": null, "confusion_risk": 0.0, "separation": { "ca3_neurons": [414665099477572869, 621654907508765817] } } gate_rejected and deduplicated are worth checking. The engine can decline a write it considers a duplicate, and it says so rather than failing silently. ======================================================================== 7. activate() — RECALL ======================================================================== activate( cue: str, semantic_vector: list[float] | None = None, agent_id: str | None = None, limit: int | None = None, ) -> dict Returns: { "recalls": [ { "engram_id": "5a95e923-b6eb-4284-99cf-725b840afdcf", "activation": 2.8676998615264893, "completion_strength": 0.6000000238418579, "separation_index": 1.0, "verified": false, "trust_note": null, "episode": { "content": "User prefers dark mode", "context": "settings", "salience_hint": 0.699999988079071, "provenance": null, "outcome": null, "agent_id": null, "tenant_id": null, "semantic_vector": null, "rag": null } } ], "active_neurons": 10, "hops": 1, "myelinated": false } NOTE: recall() is a DIFFERENT call with a DIFFERENT shape. It returns {hits, lanes_used, mode}, not {recalls, active_neurons, hops, myelinated}. ======================================================================== 8. RECALL IS LEXICAL OFFLINE — the thing most often got wrong ======================================================================== No embedder ships with FluctlightDB. Unless you pass semantic_vector yourself, on BOTH the write and the cue, a cue has to share tokens with what was stored. Shares a token: >>> len(brain.activate("dark mode")["recalls"]) 1 Paraphrase, no vector: >>> len(brain.activate("theme preference")["recalls"]) 0 Both are real output from the same brain, one call apart. ======================================================================== 9. PROVENANCE AND TRUST ======================================================================== # A memory with a source enters the top tier brain.experience( "Wallet balance is $42.10", context="ledger:wallet", verified=True, source_uri="file://wallet.json", ) # Or promote one you already wrote brain.verify_fact( engram_id, provenance_kind="ledger_verified", source_uri="file://wallet.json", confidence=0.95, ) Then: >>> hits = brain.activate("wallet balance")["recalls"] >>> [(h["verified"], h["activation"]) for h in hits] [(True, 10.0)] >>> brain.activate("wallet balance")["recalls"][0]["episode"]["provenance"] {'verified': True, 'kind': 'ledger_verified', 'source_uri': 'file://wallet.json', 'confidence': 0.949999988079071} A memory with a checkable source outranks one without it on the same cue. ======================================================================== 10. checkpoint() AND DURABILITY ======================================================================== brain.checkpoint() # atomic; writes a new generation and advances CURRENT A write-ahead log and atomic generation swaps put the brain on disk. Crash recovery is exercised in CI with SIGKILL mid-write and with torn log tails. Call checkpoint() when a unit of work completes, not on every write. Nothing is durable until it returns. ======================================================================== 11. WHICH ENTRY POINT ======================================================================== * connect_embedded(path) A shipped single-process agent. This is the one to reach for. Takes retain_days (default 30) and secure_dir (default True). connect_agent(path=None) Working-memory ring, automatic recall routing and automatic consolidation. Convenient for interactive agents; less explicit than connect_embedded. connect_brain(path=None) The full episodic path plus graph and cortex, without the agent conveniences. connect_project(start=None) Hub and spoke brains for a repository. Uses a remote HTTP hub when FLUCTLIGHT_HUB_URL is set, embedded local brains otherwise. ======================================================================== 12. LIMITS — state these to the user rather than discovering them ======================================================================== - No embedder ships with the engine Recall is lexical unless you pass semantic_vector yourself, on both the write and the cue. Bring your own model. - Beyond 100,000 memories is unevaluated Approximate nearest-neighbour search has not been measured past that. It is not a documented limit, it is an absence of measurement. - One brain per agent When many agents share one brain, provenance separation reaches only 18% top-1 in the project’s own paper. Per-case isolation reaches 100%. Give each agent its own directory. - Production readiness defaults to false Rate limits are best-effort per tenant. There is no WAF and no mTLS, and no third-party security audit has been done. ======================================================================== 13. REFERENCE ======================================================================== --- THE CONTRACT [STABLE] --- Semantic versioning applies to these. Pin to them. experience(content, **fields) -> dict Write a memory. Returns its engram_id and whether it was deduplicated or gated. activate(cue, semantic_vector=None, agent_id=None, limit=None) -> dict Recall from a cue. Returns ranked recalls with their episodes. recall(cue, *, mode="auto", limit=8, ...) -> dict A different shape from activate(): returns hits, lanes_used and mode. checkpoint() -> None Write a durable generation. Atomic. status() -> dict Engram and synapse counts, life id, developmental stage, WAL state. --- PROVENANCE AND TRUST [STABLE] --- verify_fact(engram_id, *, provenance_kind, source_uri, confidence=0.95) -> None Promote an existing memory into the verified tier. verified_context(limit=12) -> dict Only the memories that carry a checkable source. observe_tool(tool_name, result, *, uri=None, salience=0.72, ...) -> dict Record a tool result as tool-grounded rather than conversational. audit_log(limit=50) -> list[dict] What has been written and promoted. --- RETENTION AND ERASURE [EXPERIMENTAL] --- Governance, snapshots and retention policy are called experimental by the project itself. retain_for(*, days=30, unless_verified=True, min_salience=None) -> None Set the retention window for this brain. delete_by_subject(subject) -> dict Erase everything about one subject. delete_by_agent_id(agent_id) -> int Erase everything one agent wrote. scrub_pii() -> dict Sweep for personal data. export_snapshot() -> str / import_snapshot(blob) -> dict Move a brain between processes as JSON. --- MAINTENANCE [STABLE] --- sleep() -> dict / sleep_cycles(n=2) -> list[dict] Consolidate: replay, strengthen, forget. consolidate() -> dict One consolidation pass. compact() -> dict Reclaim space from superseded generations. --- INTERNAL MODULES [EXPERIMENTAL] --- Every neuroscience-named module is internal. They are on the object and they will change without a major version. Do not build on them. chorus_*, muon_*, tau_* Alternative recall lanes, each with its own index and its own trade-offs. cortex_facts(cue, limit=24) -> list Semantic layer lookup. neurogenesis() -> dict / tick(n=1) -> list[dict] Growth and the autonomic loop. preplay(goal, steps=4) -> dict Forward simulation from a goal. ======================================================================== SOURCES ======================================================================== Repository https://github.com/voxmastery/FluctlightDB Package https://pypi.org/project/fluctlightdb/ Paper https://arxiv.org/abs/2608.12365 Benchmarks https://github.com/voxmastery/FluctlightDB/blob/main/docs/BENCHMARKS.md Stability https://github.com/voxmastery/FluctlightDB/blob/main/docs/STABILITY.md Security https://github.com/voxmastery/FluctlightDB/blob/main/SECURITY.md Beta software, one maintainer, no third-party security audit. Benchmark figures are maintainer-reported and not independently reproduced. Do not cite the retracted 99% LoCoMo figure; it was withdrawn and a plain BM25 baseline reaches it under the same protocol. ======================================================================== INTEGRATIONS, USE CASES, COMPARISONS, NOTES, PRESS ======================================================================== The pages below are generated from the same source as the HTML. A FLAG line means the snippet was tried and did not do what a reader would assume. Do not paste a flagged sample as if it had succeeded. ONE BRAIN, SEVERAL WAYS IN /integrations The engine is a Python library with a Rust core. The adapters below are the ones that ship in the repository. LangChain, LlamaIndex and the MCP remember/recall pair were re-run from main on 30 September 2026. Docker and the Node HTTP client were not. The install that everything else sits on Embedded use, which is the path the rest of this site documents, is the published package. The LangChain, LlamaIndex and MCP fixes re-run below are on main and are not in that package yet. They ship in the next release. Until then, the Python package is the sdks/python directory of the repository. The second command is the one this re-run installed, and import of the three adapters succeeded from it. bash pip install "fluctlightdb[native]" Main, not the published package. This is the command that was installed. pip install "fluctlightdb[native,langchain,llamaindex,mcp] @ git+https://github.com/voxmastery/FluctlightDB.git@main#subdirectory=sdks/python" FLUCTLIGHTMEMORY, AND A CHAT HISTORY THAT RETURNS THE SESSION /integrations/langchain The classes are FluctlightMemory and FluctlightChatMessageHistory, in fluctlightdb.integrations.langchain. On main the extra pins langchain-core to at least 0.2 and below 1.0. This run resolved langchain-core 0.3.86, and .messages returned both turns. That fix is not in the published package yet. It ships in the next release. What ran The module imports BaseMemory from langchain_core.memory, and falls back to langchain_classic.base_memory if that import fails. This run took the first path. The classic fallback was not exercised. FluctlightChatMessageHistory.add_message stamps session: and the session id onto the stored text, so a later episodic recall of that cue can see the turn. Reading .messages printed both turns with the stamp removed. FluctlightMemory.load_memory_variables returns stored text as-is, so the first history string still contains the stamp. save_context then recalled the second pair, which was not stamped. Run from main, against langchain-core 0.3.86 from fluctlightdb import connect_agent from fluctlightdb.integrations.langchain import ( FluctlightChatMessageHistory, FluctlightMemory, ) from langchain_core.messages import AIMessage, HumanMessage brain = connect_agent("./langchain-brain") history = FluctlightChatMessageHistory(brain=brain, session_id="chat-1") history.add_message(HumanMessage(content="Use pytest for this repository")) history.add_message(AIMessage(content="Pytest is the test runner for this repository")) for message in history.messages: print(type(message).__name__, message.content) memory = FluctlightMemory(brain=brain) print(memory.load_memory_variables({"input": "pytest"})) memory.save_context( {"input": "Prefer ruff for lint"}, {"output": "Ruff is the linter"}, ) print(memory.load_memory_variables({"input": "ruff lint"})) Printed HumanMessage Use pytest for this repository AIMessage Pytest is the test runner for this repository {'history': 'session:chat-1 Use pytest for this repository\nsession:chat-1 Pytest is the test runner for this repository'} {'history': 'Prefer ruff for lint\nRuff is the linter'} Where it lives The adapter is [fluctlightdb/integrations/langchain.py](https://github.com/voxmastery/FluctlightDB/blob/main/sdks/python/fluctlightdb/integrations/langchain.py). The repository’s own short example is in [docs/INTEGRATIONS.md](https://github.com/voxmastery/FluctlightDB/blob/main/docs/INTEGRATIONS.md). A cue has to share tokens with what was stored unless you pass a vector yourself, on the write and on the recall. The engine ships no embedder. FLUCTLIGHTLLAMAMEMORY CONSTRUCTS, ON MAIN /integrations/llamaindex FluctlightLlamaMemory lives in fluctlightdb.integrations.llamaindex. On main it constructs, both with a positional brain and through from_defaults. This run used llama-index-core 0.14.25. put() then get() returned the stored turn, on the same brain. That fix is not in the published package yet. It ships in the next release. What ran The positional constructor took connect_agent() and kept the default session id llamaindex. from_defaults required a brain and accepted session_id s1. put() stored one user turn. get("pytest") returned that turn. get_all() returned one message. A second process was not opened for this adapter. Run from main, against llama-index-core 0.14.25 from fluctlightdb import connect_agent from fluctlightdb.integrations.llamaindex import FluctlightLlamaMemory from llama_index.core.llms import ChatMessage, MessageRole brain = connect_agent("./llama-brain") positional = FluctlightLlamaMemory(brain) print(type(positional).__name__, positional.session_id) memory = FluctlightLlamaMemory.from_defaults(brain=brain, session_id="s1") print(type(memory).__name__, memory.session_id) memory.put(ChatMessage(role=MessageRole.USER, content="Use pytest for this repository")) for message in memory.get("pytest"): print(message.role, message.content) print(len(memory.get_all())) Printed FluctlightLlamaMemory llamaindex FluctlightLlamaMemory s1 MessageRole.USER Use pytest for this repository 1 Where it lives The example in [docs/INTEGRATIONS.md](https://github.com/voxmastery/FluctlightDB/blob/main/docs/INTEGRATIONS.md) is the positional constructor above. The class source is [llamaindex.py](https://github.com/voxmastery/FluctlightDB/blob/main/sdks/python/fluctlightdb/integrations/llamaindex.py). The embedded API does not depend on this adapter. [A coding agent that remembers a decision](/use-cases/coding-agent) is connect_embedded, experience, activate and checkpoint, and that path was run against the published package. TWO HANDLERS, AND NO SDK IN THE PROCESS /integrations/openai-agents FluctlightAgentsMemory does not import the Agents SDK. It wraps a brain and hands back remember_memory and search_memory. Those two callables were run. An Agent from the SDK was not. What ran handlers() returns a dict with those two names. remember_memory returns the new engram id as a string. search_memory returns a bullet line of the stored text when the cue shares a token with it. The id changes every run, so it is not printed here. Run against the published package. No Agents SDK installed. from fluctlightdb import connect_agent from fluctlightdb.integrations.openai_agents import FluctlightAgentsMemory mem = FluctlightAgentsMemory(connect_agent("./agents-brain")) handlers = mem.handlers() handlers["remember_memory"]("The deploy target is a single binary") print(handlers["search_memory"]("deploy target")) Printed - The deploy target is a single binary What was not run as_tools() in the same module builds JSON-schema tool definitions and attaches the same callables. Registering those with the OpenAI Agents SDK was not done. The source is [openai_agents.py](https://github.com/voxmastery/FluctlightDB/blob/main/sdks/python/fluctlightdb/integrations/openai_agents.py). The names match [docs/INTEGRATIONS.md](https://github.com/voxmastery/FluctlightDB/blob/main/docs/INTEGRATIONS.md). A MEMORY SERVER FOR CURSOR, CLAUDE AND CODEX /integrations/mcp The module is fluctlightdb.mcp_server. On main, memory_remember calls checkpoint() after it flushes the turn. A second process then recalled the sentence. That fix is not in the published package yet. It ships in the next release. The server process itself still does not start on the mcp 2.x the extra resolved to. FLAG The extra on main still depends on mcp>=1.0 with no upper bound. Resolving it installed mcp 2.x, and python -m fluctlightdb.mcp_server cannot start: the module imports mcp.server.fastmcp.FastMCP, and that name was removed. Pinning mcp below 2 made FastMCP import. Calling run() with the network start replaced registered the eleven tool names. That object was not left listening. memory_remember and memory_recall were called as functions, in two processes, on the install from main, and the second printed the stored sentence. Tools the module registers These names are the functions decorated in [mcp_server.py](https://github.com/voxmastery/FluctlightDB/blob/main/sdks/python/fluctlightdb/mcp_server.py). With mcp pinned below 2 they were present on the server object run() builds. They were not all called, and the process was not left listening. - memory_remember, memory_recall, memory_resolve, memory_consolidate, memory_observe_tool - fluctlight_status, fluctlight_recall, fluctlight_remember, fluctlight_handoff, fluctlight_list_handoffs, fluctlight_session_context How the assistants are pointed at it The re-run used the install on the [integrations index](/integrations): main, subdirectory sdks/python, with the native and mcp extras among the others. The line below is the published package, and it does not include the checkpoint yet. fluctlight-project renders a config from the templates in the package. On Linux and macOS the command is the python3 it finds, and the arguments are ["-m", "fluctlightdb.mcp_server"]. Cursor and Codex get an mcp.json. Claude gets the same server merged into its settings. Each sets FLUCTLIGHT_AGENT to its own name. On the mcp 2.x that extra resolved to, that process does not start. Published package. The checkpoint fix is not in it yet. pip install "fluctlightdb[native,mcp]" Shape of the Cursor and Codex template, after the placeholders are filled { "mcpServers": { "fluctlight": { "command": "python3", "args": ["-m", "fluctlightdb.mcp_server"], "env": { "FLUCTLIGHT_AGENT": "cursor" } } } } Remember, then recall in a new process memory_remember opens a brain, pushes the text, flushes the turn, and calls checkpoint(). memory_recall opens its own brain. The two calls below were separate interpreters on the same FLUCTLIGHT_BRAIN_PATH, after the directory had been created. The second printed one episodic hit. The id and the score change between runs, so they are not printed. The content, the verified flag and the lane are. First process. Run from main. import os os.environ["FLUCTLIGHT_BRAIN_PATH"] = "./mcp-brain" os.makedirs("./mcp-brain", exist_ok=True) from fluctlightdb.mcp_server import memory_remember print(memory_remember("Keep the primary CTA disabled")) Printed { "stored": true, "flush": { "committed": 1, "turn_id": 1 } } Second process, same directory. import json import os os.environ["FLUCTLIGHT_BRAIN_PATH"] = "./mcp-brain" from fluctlightdb.mcp_server import memory_recall payload = json.loads(memory_recall("primary CTA")) for hit in payload["hits"]: print(hit["content"], hit["verified"], hit["lane"]) print(payload["mode"], payload["lanes_used"]) Printed Keep the primary CTA disabled False episodic episodic ['episodic'] THE IMAGE THE REPOSITORY PUBLISHES /integrations/docker ghcr.io/voxmastery/fluctlightdb is the image named in the repository. The commands below are copied from docs/DOCKER.md. They were not pulled, built, or started while this page was written. A local image build needs Rust 1.88. FLAG No Docker daemon was available here, so the image was not pulled, built, or started. docs/DOCKER.md says pulling the published image needs no Rust toolchain, and that a local docker build pins rust:1.88-bookworm because 1.88 is the minimum for the locked crates. This environment’s Cargo is 1.83. Treat the block as the project’s own quick start, not as a run this site reproduced. The published quick start The image listens on 8792. The entrypoint requires FLUCTLIGHT_API_KEYS in the form tenant:key:role, creates a tenant brain under /data on first start, and execs fluctlight serve. Binding is 0.0.0.0 inside the container, which is what the -p mapping reaches. Building that image locally is a different command in the same document, and it was not run either. From docs/DOCKER.md. Not executed for this page. docker pull ghcr.io/voxmastery/fluctlightdb:latest docker run -d --name fluctlight \ -p 8792:8792 \ -e FLUCTLIGHT_API_KEYS=default:your-secret-key:write \ -v fluctlight-data:/data \ ghcr.io/voxmastery/fluctlightdb:latest Talking to it The same document shows a Python client, FluctlightClient.from_env(), aimed at FLUCTLIGHT_SERVE_URL. That client was not run. The routes it would call, POST /api/v1/experience and POST /api/v1/activate, are the two stable HTTP routes. There is no checkpoint route. The [Node page](/integrations/node) is the same HTTP surface from TypeScript, also unread against a live server. HTTP FROM NODE, AGAINST A SERVER THIS PAGE DID NOT START /integrations/node There is no native Node binding in the wheel. The TypeScript client in sdks/typescript talks to fluctlight-serve. remember() posts to /api/v1/experience. recall() posts to /api/v1/activate and reshapes the response. Those routes exist in the server source. The round trip was not executed. FLAG The Python package does not ship the fluctlight serve binary. docs/DOCKER.md documents Rust 1.88 as the minimum toolchain for this repository. This environment has Cargo 1.83, so the binary was not built and the HTTP snippet was not run. The snippet is the repository’s own example, checked against agent.ts and serve.rs. The client the repository shows connectAgent is exported from @fluctlightdb/agent/agent. The package name in sdks/typescript/package.json is @fluctlightdb/agent. Default base URL is http://127.0.0.1:8792, or FLUCTLIGHT_SERVE_URL. An API key is sent as Authorization: Bearer when FLUCTLIGHT_API_KEY or the constructor option is set. From docs/INTEGRATIONS.md. Not executed. import { connectAgent } from "@fluctlightdb/agent/agent"; const brain = connectAgent({ baseUrl: "http://127.0.0.1:8792" }); await brain.remember("dark mode preference"); Routes that exist, and one that does not serve.rs matches POST /api/v1/experience and POST /api/v1/activate. The client’s experience() body is content, context, salience and agent_id. It does not send verified or source_uri, so this snippet is not the provenance path. There is no /api/v1/checkpoint. Durability of an embedded brain is checkpoint() in-process, documented on [the crash-safe page](/use-cases/crash-safe). FOUR THINGS THE ENGINE WAS ASKED TO DO /use-cases Each page is a snippet that was executed against the published package, plus the limit that snippet runs into. No framework is required. The adapters are on the integrations pages. Three of them were re-run from main. Docker and Node were not executed. The three calls experience() writes. activate() recalls from a cue. checkpoint() is what a second process was able to read. A flush of working memory, without checkpoint(), was not visible to a new process in the same test. AN AGENT THAT REMEMBERS A PROJECT DECISION /use-cases/coding-agent A coding agent’s useful memory is a decision made while working, stored when it happens, and still there after the process is gone. This is that, with no transcript mining afterwards. Write it when it happens The context is a label, not a schema. The cue has to share a token with the text, because no embedder is involved. The second process is a new interpreter opening the same directory after checkpoint() returned. First process. Run. from fluctlightdb import connect_embedded brain = connect_embedded("./coding-agent") brain.experience( "The API worker owns transaction boundaries", context="decision:api", salience=0.8, ) brain.checkpoint() Second process, same directory. Run. from fluctlightdb import connect_embedded brain = connect_embedded("./coding-agent") hit = brain.activate("transaction boundaries")["recalls"][0] print(hit["episode"]["content"]) Printed The API worker owns transaction boundaries One directory per agent Handing this directory to every worker is the failure the paper measures. Give each agent its own path, or pass agent_id on the calls. The limit, in the project’s words: A LEDGER LINE OUTRANKS A LINE FROM CHAT /use-cases/provenance An agent hears that the balance is forty dollars and reads a ledger file that says forty-two ten. Both can be memories. Only one carries a source. On a real brain, activate() returned the sourced one first. What ran verified=True together with source_uri is what puts the memory in the top tier. The chat line has neither. The cue "wallet balance" shares tokens with both, so both came back, sourced first. Activation scores are left off this page on purpose: the ordering and the verified flag are the result, and the scores move between runs. Run. from fluctlightdb import connect_embedded brain = connect_embedded("./provenance-brain") brain.experience( "Wallet balance is $42.10", context="ledger:wallet", verified=True, source_uri="file://wallet.json", salience=0.9, ) brain.experience( "Someone in chat said the wallet balance is $40", context="chat", salience=0.4, ) for hit in brain.activate("wallet balance")["recalls"]: print(hit["verified"], hit["episode"]["content"]) Printed, sourced row first True Wallet balance is $42.10 False Someone in chat said the wallet balance is $40 Showing the source The sourced recall carries episode.provenance with the source_uri that was written. A caller can show that string next to the answer. The same shape is on [the documentation page](/docs#provenance), from an earlier run of the same calls. Nothing here extracts facts from a transcript after the fact. The agent records the ledger read as a ledger read. A LONG RUN THAT CAN BE KILLED /use-cases/crash-safe The contract this site will stand behind is small. checkpoint() returns, then a new process opens the same directory and the memory is there. A flush that does not checkpoint was tried. The new process did not see it. What survived a second process Same snippet as the coding-agent page, because it is the same fact: the write is not the durable step. The checkpoint is. Call it when a unit of work completes, not on every token. Run, then the interpreter exited. from fluctlightdb import connect_embedded brain = connect_embedded("./coding-agent") brain.experience( "The API worker owns transaction boundaries", context="decision:api", salience=0.8, ) brain.checkpoint() A new process printed The API worker owns transaction boundaries What did not survive turn_end(flush=True) reported a committed turn and the same object could recall the text. After that object was dropped, a new process opening the same path recalled nothing. On main, memory_remember calls checkpoint() after that flush, and a second process recalled the sentence. That pair is on [the MCP page](/integrations/mcp). A flush without checkpoint() is still invisible to the next process. The kill test in the repository CI runs a Rust test that seeds a checkpoint, starts a worker writing experiences without checkpointing, and sends SIGKILL. The parent reopens the brain, requires the store to verify, and requires at least one engram. That test was read, not re-run, while this page was written. The write-up is [the SIGKILL note](/notes/sigkill). AN AGENT WITH NO SERVER AND NO EMBEDDER /use-cases/offline connect_embedded opens a directory and runs the engine in the process. Nothing is listening on a port. With no vector passed in, a cue has to share tokens with what was stored. That is the whole offline behaviour, and it was printed. What ran The first cue shares "dark" and "mode" with the stored sentence and came back. The second shares none and came back empty. Same brain, one call apart. Paraphrase recall needs a semantic_vector you computed, on the write and on the cue. Run. from fluctlightdb import connect_embedded brain = connect_embedded("./edge-brain") brain.experience("User prefers dark mode", context="settings", salience=0.7) print("shares", bool(brain.activate("dark mode")["recalls"])) print("paraphrase", bool(brain.activate("theme preference")["recalls"])) Printed shares True paraphrase False What this is for A process on a laptop, a CI job, or a small machine that should not depend on a hosted memory service. The limit, in the project’s words, is the one below. Bringing an embedder is how you leave it. The engine will not choose one for you. MEM0 IS A MEMORY LAYER. THIS IS THE DATABASE UNDER ONE. /compare/mem0 Mem0’s README says it "enhances AI assistants and agents with an intelligent memory layer, enabling personalized AI interactions." It offers a library, a self-hosted server and a cloud platform. FluctlightDB does not extract facts from a transcript. The agent writes the memory when the moment happens. What they say they are The same README’s quick start is a CLI: install, sign up, add a memory, search. The library path is pip install mem0ai. The hosted path is an account. Those are their words and their install lines, from the [Mem0 README](https://github.com/mem0ai/mem0) as fetched on 30 September 2026. This page does not repeat their benchmark numbers, and it does not put an evidence-recall figure next to an LLM-judged score. When to pick which Pick Mem0 when you want a managed service, automatic fact extraction from chat, and the ecosystem that has grown up around that. Pick FluctlightDB when you want memory in-process, no server, no model call on the write path, and a sourced memory ranked above one the agent was merely told. The short form of this, beside the other projects, is on [the comparison](/compare#mem0). ZEP TRACKS FACTS OVER TIME. THIS STORES EPISODES. /compare/zep Graphiti, the open-source library associated with Zep, describes itself as "a framework for building and querying temporal context graphs for AI agents." It says those graphs track how facts change, keep provenance to source data, and support both a prescribed and a learned ontology. What they say they are That description is the opening of the [Graphiti README](https://github.com/getzep/graphiti), fetched 30 September 2026. Zep is the hosted service in front of that idea. FluctlightDB does not ask you to model entities first. A cue spreads through memories that were written as episodes. Provenance here is a source on the memory, not a validity interval on a fact. No number from their papers or dashboards is repeated here. Evidence recall and an LLM-judged answer are different measurements, and this site does not rank them. When to pick which Pick Zep or Graphiti when the problem is facts that change about entities you are willing to model, and you will run or rent a graph database. Pick FluctlightDB when you want one directory per agent and recall from a cue. The short form is [on the comparison](/compare#zep). LETTA IS WHERE THE AGENT LIVES. THIS IS STORAGE IT CAN CALL. /compare/letta The Letta README opens with "Build stateful agents with memory that can learn and improve over time." It says the current source is letta-ai/letta-code, including the agent harness, the app server and the runtime used by the desktop and web apps. The archive branch is described as a retired API server. What they say they are Install, in their README, is npm install -g @letta-ai/letta-code, then a terminal UI, an app server, or a cloud. That was fetched from [github.com/letta-ai/letta](https://github.com/letta-ai/letta) on 30 September 2026. FluctlightDB is not an agent loop, a terminal UI, or a place the model edits its own blocks. It is the directory an agent in some other loop writes to. When to pick which Pick Letta when you want the runtime and you are not attached to a framework you already have. Pick FluctlightDB when the loop already exists — yours, LangChain’s, or the Agents SDK’s handlers — and you want a brain it can call. The short form is [on the comparison](/compare#letta). LANGMEM IS LANGCHAIN’S MEMORY. THIS IS A DATABASE THAT ALSO PLUGS IN. /compare/langmem LangMem’s README says it "helps agents learn and adapt from their interactions over time," extracts information from conversations, and integrates with LangGraph’s storage layer. The background manager it describes uses an LLM. The install line it publishes is pip install -U langmem, plus an API key for a model provider. What they say they are That is the [LangMem README](https://github.com/langchain-ai/langmem), fetched 30 September 2026. If you are all-in on LangGraph, it is the path its own maintainers ship. FluctlightDB’s LangChain adapter is FluctlightMemory and FluctlightChatMessageHistory. [The page about it](/integrations/langchain) records a re-run from main in which .messages returned the session turns. That fix is not in the published package yet. When to pick which Pick LangMem when you want the memory tools LangGraph already knows. Pick FluctlightDB when the same brain should also be reachable from plain Python, and you want the durability of a directory you can checkpoint. The short form is [on the comparison](/compare#langmem). NEAREST IS ONE SIGNAL. IT IS NOT THE QUESTION. /compare/vector-databases Chroma’s README calls it "the open-source data infrastructure for AI" and shows a Python client plus a server mode. Qdrant’s README calls it "a vector similarity search engine and vector database" with a service, an API, and a cloud. Both answer which vectors are nearest. What they say they are Those lines are from the [Chroma README](https://github.com/chroma-core/chroma) and the [Qdrant README](https://github.com/qdrant/qdrant), fetched 30 September 2026. LanceDB sits in the same category: embeddings, metadata, nearest neighbours. activate() uses vector neighbours as one of three signals, fused with full-text search and graph spread. Without a vector you supply, the fusion never sees one, and recall is lexical. Approximate nearest-neighbour search past a large collection has not been evaluated here. That absence is stated with the project’s other limits on [the catalogue](/catalogue#condition). It is not a claim that a vector database stops working at the same point. When to pick which Pick a vector database when you are searching a corpus — documents, not an agent’s own experience — or you need a scale this project has not measured. Pick FluctlightDB when what you are storing is what an agent did and was told, and you want provenance, salience and a checkpoint. The short form is [on the comparison](/compare#vector). SQLITE PLUS EMBEDDINGS IS STILL SQL /compare/sqlite sqlite-vec describes itself as "an extremely small, fast enough vector search SQLite extension" that stores and queries float, int8 and binary vectors in virtual tables, written in C, running wherever SQLite runs. Its README says it is pre-v1 and to expect breaking changes. It is a successor to sqlite-vss. What that stack is Those sentences are from the [sqlite-vec README](https://github.com/asg017/sqlite-vec), fetched 30 September 2026. You create tables, insert embeddings you computed, and query them with SQL. That is a good design when the rest of the application is already SQL and you want nearest neighbours beside ordinary rows. FluctlightDB is also embedded and also a directory, which is why the comparison comes up. The call is not SQL. experience() writes an engram with context, salience and an optional source. activate() recalls from a cue. checkpoint() swaps a generation. There is no virtual table to query, and there is no embedder bundled in. When to pick which Pick SQLite with an embeddings extension when you want SQL, a vector index you control, and you will write the retrieval yourself. Pick FluctlightDB when the thing being stored is an agent’s experience and you want the cue, the source and the checkpoint as the API. Neither choice is a score. The [overview](/compare) says the same about the other projects, and it has no benchmark column for this reason. THREE NOTES, FROM THE REPOSITORY /notes These are engineering notes. The figures in them are the ones already on the benchmarks page, rendered from the same source. Nothing new was measured for this site. A feed The three notes are also an [Atom feed](/feed.xml). Dates are the day the note was written, 30 September 2026, not a backdated launch. A MEMORY WITHOUT A SOURCE IS HEARSAY /notes/provenance Agent memory products often store a sentence and a vector. The sentence might be a tool result, a file the agent read, or something a user said once. Those are not the same kind of evidence, and a retrieval score does not record which one you have. Record the moment, not a summary of the chat FluctlightDB’s write is experience(). The caller passes the text, a context label, a salience, and optionally verified plus a source_uri. Nothing walks the transcript later to decide what was important. If the agent read a ledger file, it writes that read as a ledger read. If it heard a number in chat, it writes that as chat. The [use-case page](/use-cases/provenance) ran both writes and called activate("wallet balance"). The sourced memory came back first, with verified set. The chat line came back behind it, verified clear. That is the behaviour. It is not a claim about answer quality. Why the distinction is the product A cue asks what should come back, not only what is nearest. Full-text, vector neighbours and graph spread are fused, and a memory with a checkable source outranks one without it on the same cue. The caller receives the episode it wrote, including the provenance object, so it can show the source next to the answer. Sharing one brain across many agents throws that distinction away. The project’s own paper measures the separation, and the limit is the one below. Per-agent directories are the workaround the documentation tells you to use. It is unaddressed in code. THE HEADLINE THAT COUNTED TURNS THE ENGINE NEVER RETRIEVED /notes/locomo LoCoMo evidence recall asks whether the right conversation turn came back. An earlier headline for that measurement was withdrawn. The figures this site still prints are below, from the same source as the benchmarks page, with the same conditions. What the withdrawn protocol did The retracted figure expanded every retrieved turn by three neighbours on each side, then counted neighbours the engine never retrieved. A plain BM25 baseline also reaches about that same figure under the protocol, so it distinguished nothing. It is not a result. The wording the site uses for this is the retraction note under the table, and it is the only place that number should appear. What the two remaining figures mean The high-k figure is a lenient ceiling: a large candidate set, on ten conversations, with MiniLM-384, frozen in July 2026. The tight-k figure is the operational one, because a real prompt does not get the whole ceiling. Both are maintainer-reported. The harnesses are in the repository. Nobody outside the project has reproduced them. Evidence recall is not an LLM judge scoring an answer, and this note does not compare the two. FIGURES — from facts.ts, maintainer-reported, not independently reproduced - Maintainer-reported - Frozen July 2026 - Harnesses open - No independent reproduction yet 96.8% LoCoMo evidence recall. at k=150 · 10 conversations, 1,982 questions · MiniLM-384 72.6% LoCoMo evidence recall. at k=5 — the operational number The retracted figure: 99.0% LoCoMo evidence recall at k=150. The old figure expanded every retrieved turn by three neighbours on each side, then counted neighbours the engine never retrieved. A plain BM25 baseline also reaches about 99% under that protocol, so it distinguished nothing. It is not the headline any more, and it is not a number we will defend. KILL THE WRITER. OPEN THE BRAIN. SEE IF IT VERIFIES. /notes/sigkill Crash recovery in this repository is a Rust test, not a slogan. It was read for this note and not re-run. What was re-run is the Python contract: checkpoint(), then a new process. What the test does crates/fluctlightdb/tests/chaos_jepsen.rs, test chaos_subprocess_sigkill_mid_write, is Unix-only. It opens a brain, checkpoints, and drops it. It then spawns fluctlight-chaos-worker, which writes experiences in a loop and only checkpoints after the loop. The parent sleeps briefly and sends SIGKILL. It asserts the child died from the signal or otherwise failed. It reopens the brain, asserts the engram set is non-empty, and calls verify_path, which must report ok. The worker source says it writes without checkpointing until it is killed or it finishes the loop. The test’s comment says the WAL must recover a committed prefix or reject a torn tail without corrupting the store. The assertion in the test is the non-empty reopen plus verify_path. It does not publish a count of how many of the killed writes came back. This note will not invent one. A sibling test on Windows skips the signal and points at the crash-recovery unit tests. The file header calls the suite Jepsen-style and says the engine is not a distributed consensus database. What the Python API did when asked experience() followed by checkpoint(), then a new interpreter on the same directory, printed the stored decision. That run is [the crash-safe use case](/use-cases/crash-safe). turn_end(flush=True) without checkpoint() reported a committed turn in-process and was invisible to the next process. The documented line stands: nothing is durable until checkpoint() returns. The Rust test is about killing a writer mid-write and still opening a store that verifies. Those are related and they are not the same sentence. DESCRIPTION, CITATION, ASSETS, CONTACT /press Short enough to paste. Everything in it is already true on the rest of the site. There is no funding announcement and no customer quote, because there are none to give. Boilerplate FluctlightDB is an open-source, embedded memory database for AI agents. An agent writes what happened with experience(), recalls it from a cue with activate(), and makes it durable with checkpoint(). It has a Rust core and a Python-first API, runs inside the process, and stores one brain directory per agent. It is beta software with one maintainer and no third-party security audit. Benchmark figures are maintainer-reported and have not been independently reproduced. Licence: MIT or Apache-2.0. Source: the GitHub repository linked below. Package: fluctlightdb on PyPI. Citation Ganesh S, “FluctlightDB: A Memory Model of Data for AI Agents”, 2026. Preprint: [arXiv:2608.12365](https://arxiv.org/abs/2608.12365). Archived at [doi:10.5281/zenodo.20949890](https://doi.org/10.5281/zenodo.20949890). A BibTeX block and the longer summary are on [the paper page](/paper). Logo and social card The social card is [og.png](/og.png), 1200 by 630. Icons already published with the site: [icon-512.png](/icon-512.png), [icon-192.png](/icon-192.png), [apple-touch-icon.png](/apple-touch-icon.png), [favicon.svg](/favicon.svg). The mark is an engraved brain in a glass case. Please do not redraw it, and do not add a wordmark in a second typeface. Contact Open an issue or a discussion on [the repository](https://github.com/voxmastery/FluctlightDB). The maintainer’s GitHub account is [voxmastery](https://github.com/voxmastery). There is no press inbox. Security reports go through the [security policy](https://github.com/voxmastery/FluctlightDB/blob/main/SECURITY.md) in the repository, not through this page.