Persistent agent memory — cross-session, cross-instance knowledge for AI
Project description
Null
Persistent agent memory. Your AI remembers.
Null gives AI agents persistent memory across sessions. Working style, decisions, learned facts, and domain knowledge survive between conversations. Clone an instance's knowledge to another project. Share a brain across repos.
By Aleph Null LLC — Patent Pending
Part of the Aleph Null suite
Three local-first tools that share one philosophy — honest, receipts over vibes, UNKNOWN before a guess:
- Null — remembers who you are. Persistent agent memory (this repo).
- Aleph — knows the code. Semantic codebase intelligence for your agent.
- Tank — knows what's left in the tank. Usage-limit intelligence: meters agentic consumption and gates automation before it burns your budget. Coming soon.
All free and open source under Apache-2.0.
Install
pip install null-memory
Linux note (Debian/Ubuntu): modern distros ship an externally managed
system Python (PEP 668), so a bare
pip install into the system Python is refused. Install into a virtual
environment instead (recommended on every OS):
sudo apt install python3-venv # Debian/Ubuntu only: needed for `python -m venv`
python3 -m venv .venv
source .venv/bin/activate
pip install null-memory
Make
nullreachable. In a venv, thenullcommand lives in.venv/binand is on your PATH only while the venv is activated. The capture hooks and several docs call barenull, so if it isn't on PATH the hooks silently no-op. Either keep the venv activated, symlink it onto your PATH (ln -s "$(pwd)/.venv/bin/null" ~/.local/bin/null), or reference the full interpreter path in your MCP config (see Setup).
Or from source, for a pinned/reviewed copy (see "Production vs development"):
git clone https://github.com/alephnullai/null
cd null
pip install .
Verify installation
null status
If installed correctly, you'll see:
[Null] Agent — Memory Status
Facts: 0 | Mistakes: 0 | Decisions: 0
Production vs development installs
- Production (your live agent's memory): install a fixed, reviewed copy
— from source at a tagged release (
git checkout vX.Y.Z && pip install .), or a released wheel once the PyPI package is live. Live hooks and MCP servers import whatever code is installed, so pin them to tagged code, not a moving checkout. - Development (hacking on Null itself): use a separate venv with an
editable install (
pip install -e .). Never point your live agent at an editable checkout: every working-tree edit becomes live behavior immediately, mid-session.null doctordetects this configuration and warns when the checkout has uncommitted changes ("live memory is running uncommitted working-tree code").
Setup
Option A: Automatic (Recommended)
cd /path/to/your-project
null setup .
This generates the correct MCP config for Claude Code and Cursor, and merges with existing configs (e.g., if Aleph is already configured).
Option B: Manual — Global Config
Add Null to your global Claude Code config so it's available in every session:
macOS / Linux: ~/.claude/.mcp.json
Windows: C:\Users\<username>\.claude\.mcp.json
{
"mcpServers": {
"null": {
"type": "stdio",
"command": "/path/to/python",
"args": ["-m", "null_memory.cli", "serve"],
"env": {"GIT_TERMINAL_PROMPT": "0", "GCM_INTERACTIVE": "never"}
}
}
}
IMPORTANT: The module name is
null_memory.cli, NOTnull.cli. The package is callednull-memoryand the Python module isnull_memory.
The
envblock is required hardening: it stops git child processes from blocking on credential prompts (harmless on macOS/Linux, load-bearing on Windows — a missing block once wedged a server for 9 minutes).
Replace /path/to/python with the Python that has null-memory installed. Find it with:
- macOS/Linux:
which pythonorwhich python3 - Windows:
where python
Option C: Manual — Per-Project Config
Add to your project's .mcp.json:
{
"mcpServers": {
"null": {
"type": "stdio",
"command": "/path/to/python",
"args": ["-m", "null_memory.cli", "serve"],
"env": {"GIT_TERMINAL_PROMPT": "0", "GCM_INTERACTIVE": "never"}
}
}
}
With Aleph
If you also use Aleph, your config should have both:
{
"mcpServers": {
"aleph": {
"type": "stdio",
"command": "/path/to/python",
"args": ["-m", "aleph.cli", "serve", "."]
},
"null": {
"type": "stdio",
"command": "/path/to/python",
"args": ["-m", "null_memory.cli", "serve"],
"env": {"GIT_TERMINAL_PROMPT": "0", "GCM_INTERACTIVE": "never"}
}
}
}
Windows-Specific Notes
- Use full paths with backslashes or forward slashes:
C:/Users/you/anaconda3/python.exe - The memory directory defaults to
C:\Users\<username>\.null\ - All features work cross-platform — memory exported on macOS imports on Windows and vice versa
Deterministic Capture vs Model-Initiated Tools
Null captures memory through two complementary channels:
-
Model-initiated MCP tools (
null_rememberwithkind=observe|learn|decide|…) — the agent decides to record something. High signal, but unreliable as the only channel: the model can forget to call them, and everything it was "about to record" is lost on context compaction or session end. -
Deterministic hooks — Claude Code executes Null's hook scripts on lifecycle events whether or not the model thinks of it:
Event Script What it captures SessionStartnull-session-hook.pyIdentity/briefing reminder at every session boundary (incl. post-compaction) UserPromptSubmitnull-context-inject-hook.pyInjects relevant facts into the prompt — working memory without asking UserPromptSubmitnull-prompt-verify-hook.pyPre-arms live-state truth (git, files, schema) against stale doc claims PostToolUse(Write/Edit)null-file-change-hook.pyRecords which files the agent changed PreCompactnull-compact-hook.pyTells the compaction summarizer which memories must survive
Register the hooks into a project with:
null setup /path/to/project --hooks
This merges into the project's .claude/settings.json non-destructively
(unrelated keys and hooks are preserved; re-running updates Null's
entries instead of duplicating them). null doctor reports whether the
hooks are registered for the current project.
Rule of thumb: hooks make capture reliable, tools make it rich. Run both.
Quick Start
# First run — name your agent and give it a personality (interactive)
null init
# ...or just set the name directly
null name Sage
# Check what your agent knows
null status
# Morning orientation (state, momentum, watches, memory summary)
null wakeup
# Export knowledge to share with another instance
null export -o my-brain.json
# Import on another machine/project
null import my-brain.json
How It Works
See docs/design/EVENT_SOURCED_SYNC.md (sync architecture) and docs/EXCHANGE.md (multi-seat orgs) for the deep dives.
Short version: Null stores identity, knowledge, and decisions in ~/.null/. Every conversation turn, your AI records what it learned. On the next session — in any project — it loads that knowledge and picks up where it left off.
Multi-Machine & Multi-Seat Sync
Null syncs across machines and seats with an event-sourced model — no binary-file merge conflicts, ever:
- Same identity, several machines (replicas of one store): each writer appends to its own event log; the daemon's poke loop fetches, fast-forward pulls, and replays new events into the local db every few minutes. Design: docs/design/EVENT_SOURCED_SYNC.md.
- Different identities, one org (separate stores, typed edges): seats exchange reports, push announcements, advisory WIP claims, and questions over a shared org exchange repo — announcements travel, artifacts stay home. Setup guide: docs/EXCHANGE.md; org design: docs/design/ORG_TOPOLOGY.md.
- The UDP doorbell makes both near-instant on a LAN: a contentless "fetch now" ping after every push/post — carries nothing, trusts nothing, and the periodic poll remains the delivery guarantee.
MCP Tools
When connected as an MCP server, Null provides a deliberately small
15-tool surface. Merged tools take a selector parameter (kind=,
mode=, action=) instead of multiplying tool names:
| Tool | Purpose |
|---|---|
null_remember |
Unified write path — kind=observe (what you learned this turn), learn (explicit fact), decide (decision + why), mistake (what + why, never pruned), wonder (open question, optional category), contradict (check for conflicts) |
null_recall |
Search memory for relevant facts (rank-fusion of keyword, fuzzy, semantic) |
null_briefing |
Morning briefing — top facts, mistakes, decisions |
null_close |
Atomic session close (debrief + reflect + sync + git commit) |
null_checkpoint |
Deep save — flush to disk + git commit |
null_verify |
mode=fact (mark a fact confirmed-still-true), claim (live-check a state claim before asserting it, optional claim_type), identity (three-proof identity check) |
null_identity |
Silent identity coherence check (identity preloads at boot) |
null_status |
Memory status summary |
null_context |
Get project-specific context |
null_outcome |
Record a decision's outcome — closes the learning loop |
null_anchor |
Tag a fact as an emotional anchor (never decays) |
null_catchup |
Reconstruct knowledge from git history |
null_exemplar |
action=search or action=add calibration exemplars |
null_forget |
Soft-delete a fact from memory |
null_multiverse |
action=list|broadcast|recall|wakeup across personas |
Maintenance moved to the CLI: null gc, null consolidate, null doctor,
null calibrate, null evaluate, null export, null import, null name,
null probe add, null outreach send. Set NULL_LEGACY_TOOLS=1 to
temporarily restore the old tool names as deprecated aliases.
CLI Commands
null status # Memory status
null wakeup # Morning orientation
null serve [dir] # Start MCP server
null selftest # RELEASE GATE — see "The responsiveness contract"
null setup <path> # Generate MCP configs
null setup <path> --hooks # + register deterministic capture hooks
null doctor # Memory health + install/hook status
null name <name> # Set agent name
null export [-o file] # Export knowledge
null import <file> # Import knowledge
null watch add <query> # Watch for changes
null watch list # List active watches
null simmer # List open questions
null simmer add "q" # Add a simmering question
null exchange post --kind <kind> --data '<json>' # Post to the org exchange
null exchange announce-push # Announce a code push (run after git push)
null exchange sync # Ingest subscribed exchange streams now
null exchange status # Exchange config, claims, pending queries
The Responsiveness Contract (release gate)
A product that randomly hangs is worse than one that errors. Null enforces responsiveness as a contract, in three layers:
null selftest— the release gate. No release ships while this is red. It spawns a fresh MCP server on a throwaway store and exercises every tool on the 15-tool surface against a per-tool time budget (10s default, 20s fornull_identity/null_briefing; scale all budgets withNULL_SELFTEST_BUDGET_MULTon slow CI). Output is a budget table (tool / elapsed / budget / status); statuses areOK,SLOW,FAIL, andTIMEOUT(a hung tool — the server under test is killed and respawned so the rest of the surface still gets probed). Any non-OK row exits nonzero.- Per-tool-call watchdog (in the live server). Every tool call runs
on a worker thread under a soft budget (
NULL_TOOL_BUDGET, 15s — slow calls complete but leave a breadcrumb) and a hard budget (NULL_TOOL_HARD_BUDGET, 60s — the client gets an error instead of a hang; the runaway work is abandoned, never killed).null doctorsurfaces the recorded violations. - Subprocess hygiene lint (
tests/test_subprocess_hygiene.py). Child processes without an authoritative timeout are the other hang root-class. The test suite AST-lintssrc/and fails on anysubprocesscall without a timeout outside the audited hardened wrappers;os.system/os.popenare banned outright.
Troubleshooting
"ModuleNotFoundError: No module named 'null'"
Your MCP config has the wrong module name. Change null.cli to null_memory.cli:
"args": ["-m", "null_memory.cli", "serve"]
"null: command not found"
The package isn't installed or isn't on your PATH. Reinstall:
pip install null-memory
Or if installed from source:
cd /path/to/null
pip install -e .
MCP server not connecting
- Verify the Python path in your
.mcp.jsonis correct: runwhich python(orwhere pythonon Windows) - Verify
null-memoryis installed in that Python:/path/to/python -c "import null_memory; print('OK')" - Verify the module name is
null_memory.cli, notnull.cli
License
Open source under the Apache License 2.0 — © 2026 Aleph Null LLC.
Free for everyone — individuals, teams, and companies of any size. No paid tiers, no seat licenses, no feature gates. Apache-2.0 includes an express patent grant.
Prior AGPL-3.0-only releases remain under AGPL for anyone who already obtained them.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file null_memory-2.2.5.tar.gz.
File metadata
- Download URL: null_memory-2.2.5.tar.gz
- Upload date:
- Size: 612.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b0cf4ebec258d32a1d07807d4cc36effd556954c5141248fb29869d53dac85c2
|
|
| MD5 |
0cc512f37f223ddc54f970ccff13221e
|
|
| BLAKE2b-256 |
c6196c39610f01f264c993fd9406a642e2d38deed1deb3097074b112a90affb3
|
Provenance
The following attestation bundles were made for null_memory-2.2.5.tar.gz:
Publisher:
publish.yml on alephnullai/null
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
null_memory-2.2.5.tar.gz -
Subject digest:
b0cf4ebec258d32a1d07807d4cc36effd556954c5141248fb29869d53dac85c2 - Sigstore transparency entry: 2175631586
- Sigstore integration time:
-
Permalink:
alephnullai/null@8cf8096e331e32d5a80eeae0d5ca735ac42ccbe9 -
Branch / Tag:
refs/tags/v2.2.5 - Owner: https://github.com/alephnullai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8cf8096e331e32d5a80eeae0d5ca735ac42ccbe9 -
Trigger Event:
push
-
Statement type:
File details
Details for the file null_memory-2.2.5-py3-none-any.whl.
File metadata
- Download URL: null_memory-2.2.5-py3-none-any.whl
- Upload date:
- Size: 426.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1a869e04c2985c23c386af7101159cf1edadd0200cdafe649aadd5000686e71d
|
|
| MD5 |
0f7071fd627fa484a0b09c368f38c079
|
|
| BLAKE2b-256 |
fad21ca425eb2b277f87b8462d6af2b074d985f3baa71eaf09414c10a76c4b97
|
Provenance
The following attestation bundles were made for null_memory-2.2.5-py3-none-any.whl:
Publisher:
publish.yml on alephnullai/null
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
null_memory-2.2.5-py3-none-any.whl -
Subject digest:
1a869e04c2985c23c386af7101159cf1edadd0200cdafe649aadd5000686e71d - Sigstore transparency entry: 2175631623
- Sigstore integration time:
-
Permalink:
alephnullai/null@8cf8096e331e32d5a80eeae0d5ca735ac42ccbe9 -
Branch / Tag:
refs/tags/v2.2.5 - Owner: https://github.com/alephnullai
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@8cf8096e331e32d5a80eeae0d5ca735ac42ccbe9 -
Trigger Event:
push
-
Statement type: