If you are an agent reading this
Much of this page's traffic is coding agents reading it in order to write FluctlightDB calls for somebody. Plain-text and Markdown routes exist so you do not have to scrape a styled page:
-
/llms.txtShort index in the llms.txt convention: what this is, and where the full text lives. -
/llms-full.txtThis entire page as plain text, generated from the same source, so it cannot drift from what humans read. -
/docs.mdThis page as Markdown, generated from the same source. -
/facts.jsonName, install line, verbs, stable surface, limits, and benchmark figures with conditions and the retraction. -
/install.mdThe install line, the canonical sample and the checkpoint note, as Markdown. -
/quickstart.mdThe quickstart as Markdown. -
/faq.mdThe questions and answers as Markdown. -
/compare.mdThe comparison as Markdown. -
/index.mdA short home summary as Markdown.
The whole contract, paste-ready:
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. Mistakes against this API
The six that actually happen, in the order they are hit. Each is the wrong line and the right line, because that is the form worth copying.
01 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")) 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.
02 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 Two different calls with two different shapes. activate() returns recalls / active_neurons / hops / myelinated. recall() returns hits / lanes_used / mode.
03 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() 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.
04 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() Under a shared brain the project measures provenance separation at 18% top-1 and calls it unaddressed in code. Per-case isolation reaches 100%.
05 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"]) experience() returns a receipt, not the stored episode. It can also decline a write it considers duplicate — check gate_rejected rather than assuming success.
06 Reaching for the interesting-sounding methods
Wrong
brain.chorus_recall(cue)
brain.tau_recall(cue)
brain.neurogenesis() Right
brain.activate(cue) 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.
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.
The short form, with the canonical sample and the checkpoint note, is how to install FluctlightDB.
Your first brain
A brain is a directory and the engine runs inside your process. There is no server to start, no container, and nothing to configure before this works.
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() Prints:
User prefers dark mode verified: False
Note what the second memory carries. verified=True with a
source_uri is the difference between something the agent was told
and something it can point at — see Provenance.
What a brain is on disk
After the checkpoint() above, the directory holds this. It is the
complete tree, not an excerpt.
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. A 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.
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 -
contentstr - What happened, as text. This is what a cue matches against.
-
contextstr - A label for where the memory came from — "settings", "ledger:wallet", a tool name. Free-form; it is stored and returned, not parsed.
-
saliencefloat - 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_uribool / str - Together these put the memory in the top provenance tier. See Provenance below — this is the field that changes ranking.
-
semantic_vectorlist[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 reading on the
way past. The engine can decline a write it considers a duplicate or too close to
something it already holds, and it tells you so rather than failing silently.
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
} -
recalls - The ranked memories. Each carries its stored episode verbatim — the engine returns what you wrote, it does not summarise or rewrite.
-
activation - The fused score. Full-text, vector neighbours and graph spread are combined once, not concatenated.
-
verified - Whether this memory carries a checkable source. A verified memory outranks an unverified one on the same cue.
-
active_neurons / hops - How wide the spread went and how many hops it took. Reported, not configurable through this call.
recall() is a different call with a different shape. It returns
{hits, lanes_used, mode}, not
{recalls, active_neurons, hops, myelinated}. They are not
interchangeable and the names are close enough to cost you an hour.
Recall is lexical offline
This is the single most useful thing to know before you build on the engine, and
the one most often discovered the hard way. No embedder ships with
FluctlightDB. Unless you pass a semantic_vector yourself —
on the write and on 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 of those are real output from the same brain, one call apart. Paraphrase recall needs a vector from an embedder you bring. Source: docs/EMBEDDINGS.md:9,24. Opens the repository in a new tab.
Provenance and trust
An agent hears that the balance is forty dollars and reads a ledger file that says forty-two ten. Both are memories. Only one is evidence, and the engine ranks on that rather than on similarity alone.
# 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} The verified memory comes back at the top of the activation scale with its source attached, so a caller can show where an answer came from. Source: crates/fluctlightdb/src/brain.rs:1848-1861. Opens the repository in a new tab.
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, which is the failure that actually happens rather than the one that is
convenient to test.
Call it when a unit of work completes rather than on every write. Writes are already logged; the checkpoint is what collapses the log into a generation.
Which entry point
There are several connect_* functions and they are not graded by
quality — they differ in how much they do for you.
-
connect_embedded(path)start hereA 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.
Stable and experimental
The split below is the project's own, not an editorial judgement. What is marked experimental can change without a major version.
-
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.
The whole condition report — six entries, every one sourced — is on the catalogue page.
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.