FluctlightDB

Documentation

Three verbs, and what they actually return

Everything below was read off the published package: the signatures with inspect.signature, the outputs from a real brain in a temporary directory. Where the engine is narrower than you would assume, the narrow version is what is written down.

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.txt Short index in the llms.txt convention: what this is, and where the full text lives.
  • /llms-full.txt This entire page as plain text, generated from the same source, so it cannot drift from what humans read.
  • /docs.md This page as Markdown, generated from the same source.
  • /facts.json Name, install line, verbs, stable surface, limits, and benchmark figures with conditions and the retraction.
  • /install.md The install line, the canonical sample and the checkpoint note, as Markdown.
  • /quickstart.md The quickstart as Markdown.
  • /faq.md The questions and answers as Markdown.
  • /compare.md The comparison as Markdown.
  • /index.md A 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
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 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 here

    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.

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.