Documentation

NylonME Docs

From one-command Docker to a two-minute MCP setup, from the web console to the Python SDK — your first long-term memory in five minutes.

Overview

NylonME is a single-machine memory engine for AI agents. It models one memory as a weave of multiple strands — fact, affect, time, relation, confidence, frequency — with nodes connected by weighted edges into a mesh, not a hierarchy.

Retrieval is not top-k similarity but contextual resonance: starting from seed nodes, activation spreads along association strength, context match and live tension, bounded by a global budget. Unused memories settle along a tension-forgetting curve instead of piling up.

v0.4.1 ships the full production surface: REST/OpenAPI gateway with a built-in web console, Python SDK, LangChain / LlamaIndex integrations, tenant isolation with API-key auth, an audit event stream, TLS-encrypted transport, periodic snapshots, one-command Docker / Helm deploys, and a mobile SDK that runs inside Android / iOS app processes.

86.0%LoCoMo recall@10
82.9%LoCoMo end-to-end QA
99%LongMemEval-S any-hit
83.0%LongMemEval-S end-to-end

Concepts: spaces, libraries, keys

Three words are all you need — everything else is plumbing:

  • Space (tenant) — the security boundary. A key opens one space; a request naming another space is rejected;
  • Library (owner) — a memory partition inside a space. Memories in the same library link and resonate together; libraries stay fully isolated. A library name is just a folder name with no permissions — use a project slug, or dev-yourname for personal cross-project memory;
  • Key — the only credential; there are no accounts or passwords. Scope is one of read / write / admin. Paste a key into the console's key box and the chip next to it answers "who am I" (powered by GET /v1/whoami).
You want to…Do this
Separate projects' memoriesOne library per project (owner = project slug)
Share a project's memory with a teammateSame space + tell them the library name
Grant / revoke accessIssue a write key on your space / keys revoke (hot reload, instant)
Hard-isolate a customer or teamTheir own space + keys bound to it
Build a multi-user app on topYour app holds one space key; one library per end-user; your app enforces user auth

Small team sharing one engine? One space (default) + one library per project/person is the intended setup. Full version: docs/CONCEPTS.md in the repo.

Fastest start: no-Docker install

Windows users don't need Docker — or a Rust toolchain. Grab the archive for your platform from GitHub Releases, unzip, and run:

ArchivePlatform
nylonme-windows-x64.zipWindows x86_64
nylonme-linux-x64.tar.gzLinux x86_64
nylonme-macos-arm64.tar.gzApple Silicon
bash
# Windows: unzip, cd into the folder, one command to start
# (data lands in %USERPROFILE%\.nylonme\data automatically, v0.4.1+)
nylon-engine.exe serve 127.0.0.1:50051

# Open the console in your browser
start http://127.0.0.1:50052

# Same on Linux / macOS:
./nylon-engine serve 127.0.0.1:50051

Three steps: unzip → run → open the console. Since v0.4.1, data always lives in ~/.nylonme/data, so moving folders or upgrading never "loses" your memories. Each archive ships three binaries: nylon-engine (the engine), nylon_cli (write/recall from the shell), and smoke_client (self-check, prints SMOKE_OK when done).

No key needed at this step — a single machine runs in open mode by default, and the console, CLI, and MCP all connect directly. Keys only matter once you share the engine with a team and enable auth (see "Tenancy & auth" below); every client example marks where the key goes. For semantic recall (ollama embeddings), see "Run the engine".

Just want memory for Claude Code / Cursor / Codex? You can skip serve entirely — MCP stdio mode embeds the engine in-process, so one config snippet is all it takes. See "MCP setup".

Docker quickstart

No Rust toolchain needed: one command starts the engine plus ollama (which pulls the bge-m3 embedding model automatically). First start takes a few minutes (release build + model download):

bash
git clone https://github.com/nylon-memory/NylonME.git
cd NylonME
docker compose up -d

# Skip the local build and pull prebuilt images instead
docker compose -f docker-compose.prebuilt.yml up -d

Then open the web console at http://localhost:50052 — gRPC is on :50051, and REST/OpenAPI shares the console's HTTP port. Data persists in the nylon-data named volume. Set NYLON_LLM_API_KEY (DeepSeek by default) before enabling the LLM weaving layer. For Kubernetes, use the bundled Helm chart (helm/ directory).

MCP setup

nylon-engine mcp speaks Model Context Protocol over stdio with the engine embedded in-process — no daemon, no port, data lands in ~/.nylonme/data. Works with every MCP client: Claude Code, Cursor, Codex, VS Code Copilot, and more. The embedded mode needs no key either — it's normal that the config has no key field:

json
{
  "mcpServers": {
    "nylonme": {
      "command": "/path/to/nylon-engine",
      "args": ["mcp"],
      "env": { "NYLON_OWNER": "my-project" }
    }
  }
}

Codex configuration

toml
[mcp_servers.nylonme]
command = "/path/to/nylon-engine"
args = ["mcp"]

[mcp_servers.nylonme.env]
NYLON_OWNER = "my-project"

Once connected, the agent gets four tools: memory_weave (persist facts), memory_resonate (recall related memories), memory_get (read by ID) and memory_feedback (report failed answers, driving targeted reflection).

Zero binary: the /mcp HTTP endpoint

When the engine runs as a shared daemon, its HTTP port also serves a Streamable HTTP MCP endpoint at http://<host>:50052/mcp. Clients that support remote MCP servers connect with nothing but a URL — no local binary, no bridge process:

json
{
  "mcpServers": {
    "nylonme": {
      "url": "http://192.168.1.5:50052/mcp",
      "headers": { "x-api-key": "nyl_..." }
    }
  }
}

The endpoint shares the in-process engine with gRPC/REST (same data), is stateless (no session affinity), requires a write-scope key when auth is enabled, and validates the key against the server tenant. For public deployments pin it with NYLON_MCP_ALLOWED_HOSTS.

To share one memory store across machines, use the remote bridge mode: set NYLON_SERVER=host:50051 (optionally NYLON_API_KEY) and stdio MCP calls forward to the remote engine instead of embedding one locally. The repo also ships a DSH plugin (plugins/dsh-nylonme-memory) that gives every DSH session long-term memory out of the box.

Upgrading

One golden rule: swap the binary, never move the data. Memories live in the data directory (graph.snp + wal.log + audit.jsonl), not in the executable. The on-disk format is forward-compatible (snapshot magic check + WAL CRC self-healing), so upgrades never need an export/import cycle.

Ninety percent of "it broke after upgrading" comes from three causes:

  • Unzipping to a new folder and launching there — serve mode's default data dir ./nylon-data is relative to the launch directory, so a new folder starts an empty store (your old data is still sitting in the old folder). Fixed in code since v0.4.1: ./nylon-data is only used when it already exists; otherwise the engine falls back to ~/.nylonme/data and prints a startup warning instead of silently starting empty;
  • Overwriting a running nylon-engine.exe, or an old process still holding ports 50051/50052;
  • Env vars (NYLON_DATA_DIR, NYLON_EMBED_URL, ...) stranded in the old folder's launcher script.

The right moves: stop the old process → confirm the data dir (the data= path in the startup log) → copy one backup → extract the new binaries into the same folder → restart and verify data= is unchanged. Pin NYLON_DATA_DIR to an absolute path and upgrades become pure drop-in binary swaps. Full five-step checklist and the symptom/cause table: docs/UPGRADE.md in the repo.

Run the engine

Grab a prebuilt binary from GitHub Releases, or build from source:

bash
git clone https://github.com/nylon-memory/NylonME.git
cd NylonME/engine
cargo build --release -p nylon-engine --example nylon_cli
bash
# Minimal run (embeddings off, LLM off)
NYLON_DATA_DIR=./data ./nylon-engine serve 0.0.0.0:50051

# Recommended: ollama embeddings for semantic recall
ollama pull bge-m3
NYLON_DATA_DIR=./data \
NYLON_EMBED_URL=http://localhost:11434 \
NYLON_EMBED_MODEL=bge-m3 \
NYLON_EMBED_DIMS=1024 \
./nylon-engine serve 0.0.0.0:50051

# Optional: understanding layer (session-level fact weaving)
NYLON_LLM_URL=https://api.deepseek.com/v1/chat/completions \
NYLON_LLM_MODEL=deepseek-v4-flash \
NYLON_LLM_API_KEY=<your-key> \
NYLON_LLM_THINKING_OFF=1 \
./nylon-engine serve 0.0.0.0:50051

At startup the engine probes embedding-channel health: GET /v1/stats reports embedder_status as one of three states — ok / degraded (with an embed_failures counter and embedder_last_error) / disabled — so a dead embedding service no longer silently degrades recall to lexical-only.

Windows gotcha: with a system proxy enabled, the HTTP client may route even http://127.0.0.1:11434 through the proxy and ollama answers 502 Bad Gateway — use http://localhost:11434 instead (proxy bypass lists match localhost).

Console & REST

The engine binary ships a zero-install web console and REST API (default http://127.0.0.1:50052, disable with NYLON_HTTP_ADDR=off).

Seven views: Overview (engine stats, latest memories, activity stream, tension histogram), Memories (browse/filter with live tension), Graph (a dependency-free force-directed map of the memory web, colored by tension or owner), Resonate (debug queries: seeds, scores, adaptive depth — plus a one-click answer-quality feedback bar that drives idle reflection), Weave (single + session batch), Audit and Team (issue/revoke keys, per-member activity). The node drawer offers a Forget action (DELETE /v1/nodes/{id} writes a tombstone). Same engine, same write path as gRPC/MCP, with dark/light themes and an EN/中文 toggle built in.

NylonME web console: overview dashboard

REST endpoints map one-to-one to the gRPC contract; the OpenAPI spec lives at docs/api/openapi.json in the repo.

Python SDK & integrations

nylon-sdk wraps the gRPC contract in sync + async clients:

bash
pip install ./sdk/python    # install from this repo
python
from nylon_sdk import NylonClient

with NylonClient("127.0.0.1:50051", owner="alice") as client:
    client.weave("Alice prefers window seats on business trips")
    for node in client.resonate("flight seat preference").activated:
        print(node.filaments.fact)

LangChain / LlamaIndex

bash
pip install "nylonme-integrations[langchain]"   # or [llamaindex]
python
from nylonme_integrations.langchain import NylonMeRetriever

retriever = NylonMeRetriever(target="127.0.0.1:50051", owner="alice")
docs = retriever.invoke("flight seat preference")

Mobile SDK (Android / iOS, on-device)

nylon-mobile is the UniFFI binding of the in-process engine: no port, no gRPC, no network calls — memories stay in the app sandbox. It depends only on the four pure-Rust core crates (nylon-core / nylon-graph / nylon-storage / nylon-embed) and shares the same nylon-service implementation for weaving, resonance and retrieval as the server — identical behavior on both ends, including CJK query segmentation. Kotlin / Swift bindings are UniFFI-generated; releases ship .so / xcframework artifacts plus binding packages per tag.

MethodWhat it does
MobileEngine.new(config)Open/create the local store; pass embed_model_path + embed_vocab_path together to enable on-device semantics
weave(fact, task?, emotion?)Weave a memory, returns node_id
resonate(query, budget)Contextual resonance recall
get(local_id)Read one memory by ID
node_count()Memory count
checkpoint()Flush to disk (call when the app goes to background)
shutdown()Compact + flush (call before releasing the instance; process kills are covered by the WAL)

Lifecycle hooks (Kotlin)

kotlin
// Hook up once via ProcessLifecycleOwner in Application.onCreate
class MemoryEngineHolder(context: Context) : DefaultLifecycleObserver {
    private var engine: MobileEngine? = null

    @Synchronized
    fun engine(): MobileEngine = engine ?: MobileEngine(
        MobileConfig(
            dataDir = File(appContext.filesDir, "nylon/db").absolutePath,
            owner = "me",                // single user on-device; fixed is fine
            embedModelPath = model,      // pass null (with vocab) to skip semantics
            embedVocabPath = vocab,
        )
    ).also { engine = it }

    override fun onStop(owner: LifecycleOwner) { engine?.checkpoint() } // backgrounded
    fun destroy() { engine?.shutdown(); engine = null }                 // release
}

Data-safety contract

WAL-first writes (group commit + fsync) mean a successful write is already durable; a killed process replays the WAL on next start. Automatic checkpoints fire every 32 writes or 300s (when dirty) by default — tune with autoCheckpointWrites / autoCheckpointSecs. Checkpoints only affect recovery speed, never correctness.

Device benchmarks (Honor NTH-AN00 / aarch64)

Lexical channel: with 20 mixed Chinese/English memories after a cross-process WAL recovery, all 8 Chinese queries hit top-1, ~55µs per resonate. On-device semantic channel (quantized bge-small-zh, 24MB, pure-Rust tract inference): 10/10 top-1, including a zero-lexical-overlap semantic probe; ~0.3–1.2s per resonate including query embedding (Snapdragon 778G-class CPU). Quantized models must have value_info stripped first (scripts/prepare_onnx_model.py in the repo). Deployment and iOS details: engine/crates/nylon-mobile/ README and INTEGRATION.md.

Tenancy & auth

The engine supports tenant isolation and API-key auth with three scopes: read < write < admin:

  • No keys configured: open mode (single-machine default), same behavior as older versions;
  • Set NYLON_API_KEYS_FILE (or inline NYLON_API_KEYS) to enable auth: HTTP requests carry an x-api-key header or Authorization: Bearer, gRPC calls carry x-api-key metadata;
  • Each key is bound to one tenant and mismatched tenants are rejected; admin may use the wildcard * tenant.
bash
# First start generates an admin key and prints it once
# Hand out keys to teammates afterwards (hot-reloaded, no restart)
nylon-engine keys add --tenant prod --scope write --name alice   # -> nyl_... (shown once)
nylon-engine keys list        # masked overview, with member aliases
nylon-engine keys revoke nyl_aaa    # by full key or unique prefix

The --name alias attaches a member identity to a key: with NYLON_AUDIT on, every audit event carries actor=alice, and GET /v1/audit?actor=alice shows per-person activity. Admins can also manage keys over REST — GET /v1/keys (masked list), POST /v1/keys (full key returned exactly once), DELETE /v1/keys/{prefix} — and the console's Team view wraps it with issue/revoke buttons. The MCP bridge and CLI forward NYLON_API_KEY automatically.

TLS transport

Plaintext by default (acceptable when the engine only listens on loopback). Set two env vars and both the gRPC port and the HTTP/UI gateway switch to TLS — no nginx/Caddy in front required. Setting only one is a startup error: no half-encrypted state.

bash
# The two variables must be set together; both the gRPC port and
# the HTTP/UI gateway switch to TLS at once
export NYLON_TLS_CERT=/etc/nylonme/tls/server.pem   # PEM certificate chain
export NYLON_TLS_KEY=/etc/nylonme/tls/server.key    # PEM private key
nylon-engine serve 0.0.0.0:50051
# -> gRPC listening on 0.0.0.0:50051 (grpcs, ...)
# -> HTTP/UI listening on https://0.0.0.0:50052 (TLS)

# Quick self-signed certificate for a LAN:
openssl req -x509 -newkey rsa:2048 -nodes -days 825 \
  -keyout server.key -out server.pem \
  -subj "/CN=192.168.1.5" -addext "subjectAltName=IP:192.168.1.5,DNS:nylonme.lan"

# Clients opt in with an https:// URL; trust the CA for self-signed:
export NYLON_SERVER="https://192.168.1.5:50051"
export NYLON_TLS_CA=/path/to/server.pem

The web console works as-is at https://host:50052 (accept the certificate warning once for self-signed certs). The DSH plugin takes the same https:// base URL; if Node's fetch does not pick up a self-signed CA, install the CA into the OS trust store or set NODE_EXTRA_CA_CERTS. Plaintext clients hitting a TLS port fail at handshake (by design), and vice versa. At-rest encryption is on the roadmap (L2.6); the honest security model lives in SECURITY.md in the repo.

Backup & restore

RocksDB supports periodic snapshots with WAL truncation (NYLON_CHECKPOINT_SECS, default 600s), or trigger one manually with POST /v1/checkpoint for hot backups. Full procedure: docs/BACKUP_RESTORE.md in the repo.

CLI quickstart

--owner isolates memory namespaces; --tenant defaults to codex.

bash
export NYLON_SERVER=http://127.0.0.1:50051
export NYLON_API_KEY=nyl_...   # only when the engine has auth enabled; not needed in single-machine open mode

# Weave your first memory
./nylon_cli weave --owner alice --task travel \
  --fact "Alice prefers window seats on business trips"
# -> NODE_ID=0

# Resonate related memories
./nylon_cli resonate --owner alice \
  --query "flight seat preference" --budget 5
# -> ACTIVATED=1
# -> 0  0.812  Alice prefers window seats on business trips

A good fact is a self-contained sentence with names, numbers and paths. Never weave secrets or passwords into memory.

Tuning knobs

VariableDefaultEffect
NYLON_MAX_SEEDS20Seed set cap (lexical + vector channels)
NYLON_RERANK_VEC0Weight of query-vector cosine mixed into resonance ranking
NYLON_TENSION_FLOOR0Tension floor during ranking (does not mutate node state)
NYLON_SEED_QUOTA0Reserved top slots for direct seed hits in output
NYLON_LLM_MAX_TOKENS4096LLM output budget; raise when session-split JSON gets truncated
NYLON_ECHO_DEMOTEoff(0,1) coefficient demoting short query-echo nodes below explanatory content
NYLON_SESSION_DEDUPoffweave_session reuses nodes for identical (tenant, owner, text) events; retry/replay safe
NYLON_DERIVED_EDGESoffAbstract-layer to leaf edges (measured net-negative; keep off)
NYLON_WORLD_BRIDGES / _ASYNCoffCommonsense world-knowledge bridging (sync / async reflection)
NYLON_PERSONA_REFLECToffPersona node extraction during async reflection
NYLON_REFLECT_IDLE_SECS—Idle interval for async reflection
NYLON_HTTP_ADDR127.0.0.1:50052HTTP / UI listen address (off to disable)
NYLON_DATA_DIRsee noteRocksDB data directory: ./nylon-data if it exists, else ~/.nylonme/data (since v0.4.1)
NYLON_CHECKPOINT_SECS600Periodic snapshot interval in seconds (0 to disable)
NYLON_API_KEYS_FILE / NYLON_API_KEYS—API key table (file path or inline JSON)
NYLON_TLS_CERT / NYLON_TLS_KEY—PEM cert chain / private key; set together to switch both gRPC and HTTP ports to TLS
NYLON_TLS_CA—Client-side CA trust for self-signed certs (MCP bridge / Python SDK)
NYLON_MCP_ALLOWED_HOSTS—Pin the Host allowlist for the /mcp endpoint on public deployments
NYLON_AUDIToffAudit event stream switch
NYLON_SERVER—MCP remote-bridge target (host:50051)
NYLON_API_KEY—Client key for a remote engine (bridge / CLI)
NYLON_OWNER / NYLON_TENANTdefaultDefault MCP owner / tenant

Repository layout

text
NylonME/
├── proto/            # nylon/v1 gRPC contract
├── engine/           # Rust workspace (9 crates)
├── sdk/python/       # Python SDK (nylon-sdk)
├── integrations/     # LangChain / LlamaIndex adapters
├── plugins/          # Agent-platform plugins (DSH, ...)
├── docs/             # Guides & API specs
└── helm/             # Kubernetes deploy

nylon-core

Filament data model + tension decay

nylon-graph

CSR main graph + delta buffer + resonance walk

nylon-vector

HNSW vector index

nylon-embed

Embedding client (ollama / OpenAI-compatible)

nylon-llm

Understanding layer: fact weaving, conflict detection

nylon-storage

RocksDB persistence (WAL + snapshots, crash recovery)

nylon-service

Shared core for server / mobile

nylon-engine

Engine entrypoint + gRPC services (tonic)

nylon-mobile

UniFFI bindings: in-process engine for Android / iOS

Roadmap

  • Done: two-layer write (WeaveSession), lexical + vector hybrid seeds, adaptive depth, query-vector rerank, async commonsense reflection, HNSW, RocksDB persistence, gRPC services, LoCoMo recall 86.0% / QA 82.9%, LongMemEval-S any-hit 99% / end-to-end 83.0%, REST/OpenAPI gateway + web console (seven views + graph + feedback bar + Team key management), Python SDK, LangChain/LlamaIndex, tenant isolation, API-key auth (hot-reloaded via CLI/REST/console), audit stream, TLS transport, periodic snapshots & backup, Docker one-command deploy, Helm chart, DSH plugin.
  • Next: at-rest encryption (L2.6), cross-encoder reranker, million-node memory profiling, paper & blog series.