Configuration
Engram is configured entirely through environment variables. There is no config file to manage — set the variables you need and start the server.
Environment variables
| Variable | Default | Description |
|---|---|---|
| PORT | 4901 | HTTP API port the server listens on. |
| HOST | 0.0.0.0 | Bind address. Use 127.0.0.1 to restrict to localhost only. |
| ENGRAM_DATABASE | sqlite | Database dialect: sqlite (default) or postgresql. |
| ENGRAM_DB_PATH | ./engram.db | Path to the SQLite database file (sqlite mode). |
| DATABASE_URL | none | PostgreSQL connection URL (postgresql mode). Example: postgresql://user:pass@localhost:5432/engram |
| NODE_ENV | development | Set to production for production deployments. |
| ENGRAM_DECAY_INTERVAL | 3600000 | Time between automatic decay sweeps in milliseconds. Set to 0 to disable auto-decay. Default: 1 hour. |
| ENGRAM_DECAY_THRESHOLD | 0.05 | Retention score below which memories are archived during a decay sweep (0–1). |
| ENGRAM_NAMESPACE | none | Optional namespace for memory isolation. When set, all operations are scoped to this namespace. |
| ENGRAM_INDEX_PATH | {dbPath}.index | Path to persist the vector index for fast startup. Default: same path as DB with .index suffix. |
| ENGRAM_EMBEDDING_MODEL | Xenova/all-MiniLM-L6-v2 | Embedding model for vectorization. Supported: Xenova/all-MiniLM-L6-v2 (384-dim), Xenova/bge-small-en-v1.5 (384), Xenova/bge-base-en-v1.5 (768). |
| OLLAMA_PROXY_PORT | 11435 | Port for the Ollama memory injection proxy. |
| OLLAMA_TARGET | http://localhost:11434 | Real Ollama server URL the proxy forwards to. |
| ENGRAM_MAX_TOKENS | 1500 | Max context tokens injected per Ollama proxy request. |
Starting with environment variables
Pass variables inline or export them before starting the server:
# Inline — start with a custom DB path PORT=4901 ENGRAM_DB_PATH=/data/engram/prod.db node apps/server/dist/index.js # Or export first export PORT=4901 export ENGRAM_DB_PATH=/var/lib/engram/engram.db export NODE_ENV=production node apps/server/dist/index.js # PostgreSQL mode export ENGRAM_DATABASE=postgresql export DATABASE_URL=postgresql://user:pass@localhost:5432/engram node apps/server/dist/index.js
Running with PM2 (production)
PM2 keeps Engram alive across restarts and provides log management. Create an ecosystem.config.js or pass env vars directly:
# Install PM2 globally
npm install -g pm2
# Start with environment variables
pm2 start server.js --name engram \
--env PORT=4901 \
--env ENGRAM_DB_PATH=/var/lib/engram/engram.db \
--env NODE_ENV=production
# Or use an ecosystem file
# ecosystem.config.js
module.exports = {
apps: [{
name: 'engram',
script: 'server.js',
env: {
PORT: 4901,
ENGRAM_DB_PATH: '/var/lib/engram/engram.db',
NODE_ENV: 'production',
},
}],
}
# Then start with:
pm2 start ecosystem.config.js
pm2 save # persist across reboots
pm2 startup # generate system startup scriptRunning with systemd
For servers managed by systemd, create a unit file:
# /etc/systemd/system/engram.service [Unit] Description=Engram Memory Server After=network.target [Service] Type=simple User=engram WorkingDirectory=/opt/engram ExecStart=/usr/bin/node server.js Restart=on-failure RestartSec=5 Environment=PORT=4901 Environment=ENGRAM_DB_PATH=/var/lib/engram/engram.db Environment=NODE_ENV=production [Install] WantedBy=multi-user.target
# Enable and start sudo systemctl daemon-reload sudo systemctl enable engram sudo systemctl start engram # Check status and logs sudo systemctl status engram journalctl -u engram -f
Database defaults
Engram uses SQLite with the following pragmas applied automatically at startup. These are hardcoded for optimal performance and cannot be changed.
| Pragma | Value | Effect |
|---|---|---|
| journal_mode | WAL | Write-Ahead Logging enables 10k+ writes/sec and concurrent reads. |
| synchronous | NORMAL | Balanced durability — safe with WAL mode, faster than FULL. |
| cache_size | 10000 | ~40 MB page cache for faster repeated queries. |
| foreign_keys | ON | Enforces referential integrity across tables. |
The default embedding model is Xenova/all-MiniLM-L6-v2 (384 dimensions, FP16 storage). Override with ENGRAM_EMBEDDING_MODEL. After switching models, use the re-embedding pipeline to update existing vectors.
PostgreSQL (optional)
For team use or cloud deployment, switch from SQLite to PostgreSQL:
# 1. Set the dialect and connection URL export ENGRAM_DATABASE=postgresql export DATABASE_URL=postgresql://user:pass@localhost:5432/engram # 2. Ensure pgvector extension is installed (for native vector search) psql -d engram -c "CREATE EXTENSION IF NOT EXISTS vector;" # 3. Run drizzle migrations pnpm db:generate && pnpm db:migrate # 4. Start the server node apps/server/dist/index.js
The pg package is an optional dependency — it only loads when PostgreSQL mode is active. SQLite remains the default for local/single-user use.
Internal scoring algorithm
When recalling memories, Engram ranks results using a weighted composite score. These weights are hardcoded in the core scoring engine and provided here for reference.
| Factor | Weight | Details |
|---|---|---|
| Similarity | 45% | Cosine similarity between the query embedding and stored memory embedding. |
| Recency | 25% | Exponential decay with a 7-day half-life. Recently accessed memories score higher. |
| Importance | 20% | User-assigned or auto-computed importance value (0–1). |
| Access frequency | 10% | How often a memory has been recalled. Frequently accessed memories are boosted. |
Recall defaults
| Parameter | Default | Context |
|---|---|---|
| Similarity threshold | 0.3 | Minimum cosine similarity to include a result. |
| topK (search) | 10 | Maximum results for /api/search. |
| topK (recall) | 20 | Maximum results for /api/recall. |
| maxTokens (recall) | 2000 | Token budget for recall response content. |
| Graph expansion depth | 2 | Hops traversed in the knowledge graph when expanding results. |
Decay & garbage collection
Engram includes an automatic memory decay and garbage collection system inspired by the Ebbinghaus forgetting curve. Memories that are old, rarely accessed, and low-importance are progressively decayed and eventually archived.
Retention score formula
retentionScore = importance × recencyFactor × accessFactor Where: recencyFactor = exp(−ln(2) × daysSinceLastTouch / halfLifeDays) accessFactor = min(1.0, 0.3 + 0.7 × log10(accessCount + 1) / 3) When retentionScore < archiveThreshold → memory is archived (soft-deleted)
Default decay policy
| Parameter | Default | Description |
|---|---|---|
| halfLifeDays | 7 | Ebbinghaus half-life — how many days until recency factor halves. |
| archiveThreshold | 0.05 | Retention score below which a memory is archived. |
| decayIntervalMs | 3600000 | Auto-sweep interval (1 hour). Set to 0 to disable. |
| batchSize | 200 | Memories evaluated per batch during a sweep. |
| importanceDecayRate | 0.01 | Daily importance reduction (1% per day without access). |
| importanceFloor | 0.05 | Minimum importance value after decay. |
Protection rules
The following memories are never archived by the decay engine, regardless of their retention score:
- Semantic memories with importance ≥ 0.8
- Procedural memories with confidence ≥ 0.9
- Memories accessed in the last 24 hours
- Memories tagged with
"pinned"or"protected"
Auto-consolidation
After each decay sweep, Engram can automatically consolidate clusters of old episodic memories into semantic summaries. This is enabled by default and only runs on episodes older than 24 hours with at least 3 similar memories (similarity ≥ 0.6).
POST /api/decay with {"dryRun": true} to preview what would be archived before enabling auto-decay.Contradiction detection
When a new memory is stored, Engram checks for conflicts with existing memories using vector similarity and content analysis (negation, value changes, temporal overrides, opposite sentiment). Configuration is managed at runtime via the API:
| Setting | Default | Description |
|---|---|---|
| enabled | true | Toggle contradiction checking on/off. |
| similarityThreshold | 0.65 | Minimum vector similarity to consider two memories as same-topic. |
| confidenceThreshold | 0.4 | Minimum confidence to flag a contradiction. |
| maxCandidates | 10 | Max candidate memories to evaluate per store. |
| defaultStrategy | keep_both | Default resolution: keep_newest, keep_oldest, keep_important, keep_both, manual. |
| autoResolve | false | Automatically resolve using the default strategy. |
Update at runtime: PUT /api/contradictions/config
Index persistence
Engram persists the in-memory vector index to disk on shutdown and reloads it on startup. This enables fast incremental startup — only new memories since the last save are loaded from the database.
| Setting | Default | Description |
|---|---|---|
| ENGRAM_INDEX_PATH | {ENGRAM_DB_PATH}.index | Where to save the index file. |
With a persisted index, startup for 1k memories goes from ~1.2s to ~45ms (27x faster). Use POST /api/index/rebuild to force a full rebuild if the index becomes stale.
Webhooks
Subscribe external systems to memory events. Configure via the API:
curl -X POST http://localhost:4901/api/webhooks \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.slack.com/...","events":["stored","contradiction"]}'Events: stored, forgotten, decayed, consolidated, contradiction. Deliveries include HMAC-SHA256 signing when a secret is configured. Failed deliveries retry 3 times with exponential backoff. After 10 consecutive failures, the webhook is auto-disabled.
Plugins
Extend Engram with lifecycle hooks. Plugins run in-process and fire at 6 lifecycle points:onStore, onRecall, onForget, onDecay, onStartup, onShutdown.
// Register a plugin programmatically
brain.registerPlugin({
id: 'my-org/logger',
name: 'Memory Logger',
version: '1.0.0',
hooks: {
onStore: async (ctx) => console.log('Stored:', ctx.memory.id),
},
});Plugin errors are isolated — a failing plugin never breaks core operations. See Plugin API for the full manifest format.