Local-first knowledge graph Prerelease 0.3.0 · macOS · Linux

Okto Neuron. Your notes, as a graph your agents can cite.

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 answer

Your agent answered. Which line of which file did that come from?

Most 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.

01

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.

02

Confidence is not evidence.

A fluent sentence carries the same weight whether it was read from your notes or extrapolated to fill a gap.

03

Failure looks like success.

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.

A knowledge graph derived from your notes.

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.

CLI

okto-neuron / kg

Init a vault, ingest files, ask and explore from the terminal. The same code path the server uses.

Library

Python

Import it. Every surface below is a thin wrapper over the same library calls.

MCP

Port 8201

Five bearer-authenticated tools. Claude Code is the wired client; the protocol is open to any MCP client.

REST

Port 7777

GET /api/v1/nodes/{id} returns a node, its in and out edges, its provenance record, and the block it came from.

UI

Seven views

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.

5 + 6
primitives + support types
5
MCP tools
7
UI views
1
process, two ports

Every claim carries its byte range home.

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"
    }
  ]
}
Paths are vault-relative. Absolute paths are never returned. 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.

synthesis_status

Always present. Five values.

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
finish_reason

Surfaced as-is.

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
vault_override_ignored

Reported, not swallowed.

When a per-call vault override is discarded, the response says so. You never answer from a different vault than you think you did.

Extraction proposes. The gate commits.

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.

  1. 01

    Intake

    A file path or raw text is chunked into byte-anchored blocks: path, byte_start, byte_end, content_hash.

  2. 02

    Propose

    An LLM extracts atomic claims and entities from each block. These are candidates, not facts.

  3. 03

    Stage

    Candidates are held with their provenance edges: which block, which extractor run, which agent.

  4. 04

    Resolve

    Duplicates are collapsed and entities reconciled against what the graph already holds.

  5. 05

    Gate auto-commit or hold for review

    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.

  6. 06

    Commit

    Committed to the graph store (Okto Grafx by default) and embedded locally with fastembed, in-process.

Closed schema

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.

Supersession

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.

Derived, always rebuildable

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.

Five tools. One bearer token.

The MCP server listens on 127.0.0.1:8201 and authenticates every call. Each tool's first docstring line is reproduced below unchanged.

ask one-shot · k = 20 · 17 parameters

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.

explore structured · k = 12 · no LLM call

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.

remember file path or raw text · loopback-only

Ingest a source and autonomously curate it into the graph.

sensitivity="local_only" refuses any hosted LLM for that ingest.

list_vaults names only

List the vault NAMES this server can reach, so you can pick one.

Returns names, deliberately not paths.

init_vault loopback-only

Create one application-managed named vault without selecting it.

Writes are refused from anything but the local machine, even when remote serving is allowed.

What leaves the machine, exactly.

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.

  • LLM calls, to the endpoint you configure
  • A remote Neo4j, only if you explicitly allow one
  • The installer's downloads
  • LLM call traces, only to an MLflow server you name

Loopback by default

Binds 127.0.0.1. Remote serving is withdrawn until TLS and a proxy land; use an SSH tunnel in the meantime.

Bearer-authenticated MCP

Every tool call carries a token. Writes (remember, init_vault) stay loopback-only even under --allow-remote.

Local model by default

The default LLM endpoint is http://127.0.0.1:8123/v1, a server you run. Non-loopback endpoints require explicit remote-egress confirmation.

Embeddings in-process

The default embedder is fastembed, running inside the daemon. Nothing about your text is sent anywhere to be embedded.

Secrets at rest

API keys are never written into vault YAML. Stored with POSIX owner-only permissions, or Windows DPAPI. Data lives in ~/.okto-neuron/vaults/<name>.

One command. One daemon, two ports.

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.

  1. Installs uv and pins Python 3.12. Your system Python is untouched.
  2. Installs the Okto Neuron wheel.
  3. Starts one daemon: UI and REST on 7777, MCP on 8201.
  4. Opens the UI.
  5. Lets you create, select, configure, and delete managed vaults inside the application.
  6. Registers Claude Code with 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
macOS · Linux

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.