Prose with no way back.
The answer names a value, a date, a decision. Nothing in it says which file, let alone which bytes of that file.
Your Markdown vault is the trust root. Okto Neuron derives a knowledge graph from it, splits every file into byte-anchored blocks, and returns answers where each claim carries a pointer back to the exact bytes it came from.
Free to run locally · No account required · Works with Claude Code
Start with the question nobody can answerMost memory tools for agents hand back prose. The paragraph sounds settled, the source is gone, and a confident wrong number reads exactly like a right one. You cannot check what you cannot locate.
Worse, retrieval can fail quietly. The model was unreachable, the context was cut short, the answer was assembled from nothing, and the paragraph still arrives looking finished.
The answer names a value, a date, a decision. Nothing in it says which file, let alone which bytes of that file.
A fluent sentence carries the same weight whether it was read from your notes or extrapolated to fill a gap.
When the model behind the tool is down or truncates, most tools return the same shape of text. You find out later, if at all.
Okto Neuron is a standalone, local-first knowledge graph usable as a Python library, CLI, and authenticated MCP server. Your Markdown vault is the trust root; the graph is derived from it and can always be rebuilt.
Init a vault, ingest files, ask and explore from the terminal. The same code path the server uses.
Import it. Every surface below is a thin wrapper over the same library calls.
Five bearer-authenticated tools. Claude Code is the wired client; the protocol is open to any MCP client.
GET /api/v1/nodes/{id} returns a node, its in and out edges, its provenance record, and the block it came from.
Query, Add, Logs, Browse, Graph, Curation, Config. The Query view exposes 13 retrieval controls.
Runs on Okto Grafx, the OktoLabs embedded graph database. Ladybug and Neo4j are selectable backends.
Each claim is anchored by PROV-O edges to the block it was read from, the extractor activity that
produced it, and the agent responsible. Ask with include_sources=true and the pointer comes
back with the answer.
// the excerpt line the model sees, so it can say the sources do not cover a period
[EXCERPT source=cnpj/guias/catalogo.md bytes=0-5959,14643-15070 of 32111]
// what comes back (abridged)
{
"text": "The catalog lists guia 21 as a monthly obligation ...",
"citations": ["..."],
"subgraph_evidence_ids": [],
"retrieval": {
"synthesis_status": "ok",
"finish_reason": "stop",
"vault": "cnpj"
},
"sources": [
{
"block_id": "blk_9f2c7a",
"path": "cnpj/guias/catalogo.md",
"byte_start": 14643,
"byte_end": 15070,
"content_hash": "sha256:3b1e...d04a"
}
]
}
explore returns
block_id on claims and relationships too, so you can walk the graph without an LLM in the loop.
A degraded answer never looks like a successful one.
Two fields ride along in retrieval on every ask, and a third appears whenever a vault override
is discarded. None of them is optional to read. They are how the tool tells the truth about itself when the
model behind it fails.
ok empty provider_error truncated abnormal_stop
Empty text plus provider_error means the model was unreachable, not that the graph lacks the answer.
“A degraded answer must never look like a successful one.” ADR 0043, D6
Whatever the provider returned, you see. Anything other than stop is suspect, and the tool does not smooth it over.
“Every source block declares which bytes of which file it is.” ADR 0043, D7
When a per-call vault override is discarded, the response says so. You never answer from a different vault than you think you did.
Files or raw text go in. Blocks, claims, and entities come out the other side, but only after deduplication, reconciliation, and a confidence gate. Extraction only proposes candidates; the gate is the only thing that commits.
A file path or raw text is chunked into byte-anchored blocks: path, byte_start, byte_end, content_hash.
An LLM extracts atomic claims and entities from each block. These are candidates, not facts.
Candidates are held with their provenance edges: which block, which extractor run, which agent.
Duplicates are collapsed and entities reconciled against what the graph already holds.
Candidates at or above confidence 0.75 auto-commit. Below that, or when a candidate contradicts an existing claim at any confidence, it is held for review in Curation instead. The threshold is configurable.
Committed to the graph store (Okto Grafx by default) and embedded locally with fastembed, in-process.
Five primitives: Agent, Activity, InformationObject, Concept, Place. Six support types: Document, Identifier, Annotation, Claim, Block, Finding. Vocabulary follows PROV-O, SKOS, Dublin Core, BIBFRAME, and CiTO.
When you edit a note and a fact changes, the old claim is dated and dropped from recall, with a
supersedes edge to the new one. Automatically, on every ingest.
The vault is the trust root. The graph is a projection of it. Delete the graph and rebuild it from the same files; nothing of record lives only in the store.
The MCP server listens on 127.0.0.1:8201 and authenticates every call. Each tool's first docstring line is reproduced below unchanged.
Answer a question grounded in the knowledge graph, with citations.
Seeds wide, synthesizes once, and returns synthesis_status, finish_reason, and optional byte-exact sources.
Drill into the graph around a topic, then walk outward by node id.
Returns graph structure, not prose. Makes no LLM call at all, so it works fully offline.
Ingest a source and autonomously curate it into the graph.
sensitivity="local_only" refuses any hosted LLM for that ingest.
List the vault NAMES this server can reach, so you can pick one.
Returns names, deliberately not paths.
Create one application-managed named vault without selecting it.
Writes are refused from anything but the local machine, even when remote serving is allowed.
Four things, and only when you configure them: LLM calls to the endpoint you set, an optional remote
Neo4j you explicitly allow, the installer's downloads, and LLM call traces if you opt in to MLflow
tracing. There is no analytics in the shipped package, and tracing is off until you set
OKTO_NEURON_MLFLOW_TRACKING_URI to a tracking server you choose.
Binds 127.0.0.1. Remote serving is withdrawn until TLS and a proxy land; use an SSH tunnel in the meantime.
Every tool call carries a token. Writes (remember, init_vault) stay loopback-only even under --allow-remote.
The default LLM endpoint is http://127.0.0.1:8123/v1, a server you run. Non-loopback endpoints require explicit remote-egress confirmation.
The default embedder is fastembed, running inside the daemon. Nothing about your text is sent anywhere to be embedded.
API keys are never written into vault YAML. Stored with POSIX owner-only permissions, or Windows DPAPI. Data lives in ~/.okto-neuron/vaults/<name>.
Prerelease 0.3.0. The upgrade from 0.2.0 to 0.3.0 has been rehearsed on Linux. macOS and Windows have not yet been verified on 0.3.0. The installer sets up everything below.
claude mcp add --scope user.Pass --no-onboard to skip the first-run prompt.
Free to run locally. No account required.
curl -fsSL https://raw.githubusercontent.com/OktoLabsAI/okto-neuron/main/install.sh | bash
Up to 0.2.0 this project was called Marginalia. Okto Neuron is not on PyPI yet; the PyPI packages named
neuron and marginalia are unrelated projects.