A local-first context graph memory for LLMs — backed by permanent Arweave storage.
Your LLM's entire knowledge graph lives in-memory for instant search, and syncs to Arweave as a single transaction. One txid = your entire memory. Import it anywhere, anytime.
┌─────────────────────────────────────────────────────┐
│ MemoryClient SDK │
│ addMemory() → search() → link() → sync() → txid │
├────────────────────┬────────────────────────────────┤
│ LocalEmbeddingService │ ArweaveStorageProvider │
│ (transformers.js) │ ┌──────────────────────┐ │
│ 384-dim vectors │ │ In-Memory Graph │ │
│ all-MiniLM-L6-v2 │ │ (cosine similarity) │ │
│ │ └────────┬─────────────┘ │
│ │ │ │
│ │ Turbo Bundler │
│ │ (free < 100 KiB) │
│ │ │ │
│ │ arweave.net │
└─────────────────────────┴───────────┴───────────────┘
Key features:
- Local-first — everything runs in-memory, no external databases
- Semantic search — 384-dim embeddings via transformers.js (no API keys)
- Permanent storage — sync to Arweave, get a txid, reload anywhere
- Free uploads — Turbo bundler subsidizes uploads under 100 KiB
- Portable — one txid = your entire knowledge graph
cd arweave-memory
npm installnode -e "
const Arweave = require('arweave');
const fs = require('fs');
Arweave.init({}).wallets.generate().then(jwk => {
fs.writeFileSync('wallet.json', JSON.stringify(jwk, null, 2));
return Arweave.init({}).wallets.jwkToAddress(jwk);
}).then(addr => console.log('Wallet address:', addr));
"import { MemoryClient } from './src/index.js';
const client = new MemoryClient({
arweave: {
connection: { host: 'arweave.net', port: 443, protocol: 'https' },
wallet: JSON.parse(fs.readFileSync('wallet.json', 'utf-8')),
},
});
// Add memories
await client.addMemory('TypeScript adds static typing to JavaScript', 'language', 'typescript');
await client.addMemory('Arweave provides permanent storage', 'blockchain', 'arweave');
// Link them
await client.link('typescript', 'arweave', 'integrates_with');
// Search semantically
const results = await client.search('web programming');
// → [{ entity: { name: 'typescript', ... }, score: 0.82 }]
// Sync to Arweave (free if < 100 KiB)
const txid = await client.sync();
console.log(`Memory stored permanently: https://arweave.net/${txid}`);
// Later — reload from any machine
const client2 = new MemoryClient({ arweave: { connection, wallet } });
await client2.load(txid);
const graph = await client2.getGraph();
// → { entities: [...], relations: [...] }| Method | Description | Returns |
|---|---|---|
addMemory(content, type?, name?, metadata?) |
Add a memory with auto-generated embedding | Entity |
search(query, limit?, minSimilarity?) |
Semantic search across all memories | MemorySearchResult[] |
link(from, to, relationType, strength?, confidence?) |
Create a relation between two memories | Relation |
sync() |
Upload current state to Arweave via Turbo | string (txid) |
load(txid) |
Restore memory from an Arweave transaction | void |
getEntity(name) |
Get a specific entity | Entity | null |
getGraph() |
Get the full knowledge graph | KnowledgeGraph |
addObservations(entityName, contents) |
Add facts to an existing memory | string[] |
deleteMemories(names) |
Delete memories (cascades to relations) | void |
getStats() |
Entity/relation/embedding counts + txid | object |
getTxId() |
Current Arweave transaction ID | string | null |
interface Entity {
name: string;
entityType: string;
observations: string[];
createdAt?: number;
updatedAt?: number;
}
interface Relation {
from: string;
to: string;
relationType: string;
strength?: number;
confidence?: number;
}
interface MemorySearchResult {
entity: Entity;
score: number; // cosine similarity (0–1)
}Your memory graph is a JSON object:
{
"version": 1,
"timestamp": 1708200000000,
"entities": [ ... ],
"relations": [ ... ],
"embeddings": { "entity_name": { "vector": [...], "model": "...", "lastUpdated": ... } }
}This gets uploaded as a single Arweave transaction. The txid is your portable handle to the entire memory state.
client.sync()serializes the graph to JSON- JSON is uploaded via Turbo bundler — free if under 100 KiB
- Returns a txid (e.g.
EVEjUngdu2BqbA5FvvhXbWKRLeyJVTt1MH_7jxsfEHo) - Data is permanently available at
https://arweave.net/{txid}
client.load(txid)fetches JSON fromarweave.net(orgateway.irys.xyzfallback)- Deserializes entities, relations, and embeddings into memory
- All search/CRUD operations work instantly on the in-memory graph
Uses transformers.js with the Xenova/all-MiniLM-L6-v2 model:
- 384 dimensions — compact but effective
- Runs locally — no API keys, no network after first model download (~25MB)
- Cosine similarity — search results ranked by vector similarity
- Auto-embedded —
addMemory()andaddObservations()generate embeddings automatically
# All tests (including embedding model load — ~25s first run)
npm test
# Fast tests only (skip embedding + live network)
SKIP_EMBEDDING_TESTS=true SKIP_LIVE_TESTS=true npm test
# Live Arweave roundtrip (requires wallet.json + internet)
npx vitest run src/live.test.ts
# Verbose output
npx vitest run --reporter verbose| Suite | Tests | What It Covers |
|---|---|---|
ArweaveStorageProvider.test.ts |
26 | Entity/relation CRUD, keyword search, cosine similarity, embeddings |
MemoryClient.test.ts |
20 | SDK lifecycle, search, link, delete, complex graph scenario |
LocalEmbeddingService.test.ts |
6 | Vector dims, normalization, semantic similarity ranking |
live.test.ts |
1 | Real upload to arweave.net via Turbo → read back → verify |
| Variable | Default | Description |
|---|---|---|
ARWEAVE_HOST |
localhost |
Arweave gateway host |
ARWEAVE_PORT |
1984 |
Arweave gateway port |
ARWEAVE_PROTOCOL |
http |
http or https |
ARWEAVE_WALLET_PATH |
— | Path to JWK wallet file |
ARWEAVE_TXID |
— | Existing memory txid to auto-load |
For live arweave.net usage:
ARWEAVE_HOST=arweave.net
ARWEAVE_PORT=443
ARWEAVE_PROTOCOL=https
ARWEAVE_WALLET_PATH=./wallet.json
arweave-memory/
├── package.json # standalone deps
├── tsconfig.json # TypeScript config
├── vitest.config.ts # test config
├── wallet.json # your Arweave JWK (gitignored)
└── src/
├── index.ts # barrel export
├── types.ts # Entity, Relation, KnowledgeGraph, EntityEmbedding
├── ArweaveConfig.ts # connection config + wallet loading
├── LocalEmbeddingService.ts # transformers.js embeddings
├── ArweaveStorageProvider.ts # in-memory graph + Turbo upload
├── MemoryClient.ts # high-level SDK
├── ArweaveStorageProvider.test.ts
├── MemoryClient.test.ts
├── LocalEmbeddingService.test.ts
└── live.test.ts # live arweave.net roundtrip
MIT