No config file needed. Just run graphmemory serve in your project directory — the current directory becomes the project with sensible defaults (BGE-M3 q8 model, all graphs enabled).
For multi-project setups, custom models, auth, or workspaces — create graph-memory.yaml. See graph-memory.yaml.example for a full annotated reference.
projects:
my-app:
projectDir: "/path/to/my-app"Only projects.<id>.projectDir is required. Everything else has sensible defaults.
# Root author (default for all projects)
author:
name: "Your Name"
email: "you@example.com"
# Users (for REST API / UI authentication)
users:
alice:
name: "Alice"
email: "alice@example.com"
apiKey: "mgm-key-abc123"
passwordHash: "$scrypt$65536$8$1$salt$hash" # generated by CLI
# Server settings
server:
host: "127.0.0.1"
port: 3000
sessionTimeout: 3600
modelsDir: "~/.graph-memory/models"
corsOrigins: ["http://localhost:5173"]
defaultAccess: "rw"
access:
alice: rw
jwtSecret: "your-secret-key-here"
cookieSecure: true
accessTokenTtl: "15m"
refreshTokenTtl: "7d"
oauth:
enabled: false
accessTokenTtl: "1h"
refreshTokenTtl: "7d"
authCodeTtl: "10m"
maxFileSize: 1048576
exclude: "**/vendor/**"
rateLimit:
global: 600
search: 120
auth: 10
model:
name: "Xenova/bge-m3"
pooling: "cls"
normalize: true
dtype: "q8"
queryPrefix: ""
documentPrefix: ""
codeModel:
name: "jinaai/jina-embeddings-v2-base-code"
pooling: "mean"
normalize: true
dtype: "q8"
embedding:
batchSize: 1
maxChars: 24000
cacheSize: 10000
remote: "http://gpu-server:3000/api/embed"
remoteApiKey: "emb-secret-key"
remoteModel: "default"
embeddingApi:
enabled: false
apiKey: "emb-secret-key"
maxTexts: 100
maxTextChars: 10000
redis:
enabled: false
url: "redis://localhost:6379"
prefix: "mgm:"
embeddingCacheTtl: "30d"
# Projects
projects:
my-app:
projectDir: "/path/to/my-app"
graphMemory: ".graph-memory"
chunkDepth: 4
maxFileSize: 1048576
author:
name: "Project Bot"
email: "bot@example.com"
model:
name: "Xenova/bge-m3"
embedding:
maxChars: 24000
access:
bob: r
graphs:
docs:
enabled: true
include: "**/*.md"
exclude: "**/drafts/**"
model:
name: "Xenova/bge-m3"
pooling: "cls"
normalize: true
access:
charlie: rw
code:
enabled: true
include: "**/*.{js,ts,jsx,tsx,mjs,mts,cjs,cts}"
knowledge:
enabled: true
tasks:
enabled: true
files:
enabled: true
skills:
enabled: true
# Workspaces (optional — share knowledge/tasks/skills across projects)
workspaces:
backend:
projects: [api-gateway, catalog-service]
graphMemory: "./.workspace-backend"
mirrorDir: "./.workspace-backend"
author:
name: "Backend Team"
email: "backend@example.com"
access:
alice: rw
model:
name: "Xenova/bge-m3"
embedding:
maxChars: 24000| Field | Type | Default | Description |
|---|---|---|---|
host |
string | 127.0.0.1 |
HTTP server bind address |
port |
number | 3000 |
HTTP server port |
sessionTimeout |
number | 3600 |
Idle MCP session timeout (seconds) |
modelsDir |
string | ~/.graph-memory/models |
Local model cache directory |
corsOrigins |
string[] | * (all) |
Allowed CORS origins |
defaultAccess |
string | rw |
Default access for unknown/anonymous users: deny, r, rw |
access |
object | — | Server-level per-user access overrides |
jwtSecret |
string | — | Required when users are defined. Secret for signing JWT tokens (minimum 32 characters) |
cookieSecure |
boolean | auto | Set cookie Secure flag explicitly. Defaults to true unless NODE_ENV=development |
accessTokenTtl |
string | 15m |
JWT access token lifetime |
refreshTokenTtl |
string | 7d |
JWT refresh token lifetime |
model |
object | (see below) | Default model config for all graphs |
codeModel |
object | (see below) | Default model config for code graph (overrides model for code) |
embedding |
object | (see below) | Default embedding config for all graphs |
embeddingApi |
object | — | Expose embedding model via POST /api/embed |
redis |
object | — | Redis configuration for session store and embedding cache (see below) |
rateLimit |
object | — | Rate limiting: global (default 600), search (default 120), auth (default 10) requests/min |
maxFileSize |
number | 1048576 |
Max file size in bytes for indexing (1 MB default). Also settable at workspace/project level |
exclude |
string | — | Additional glob to exclude (merged with default **/node_modules/**, **/dist/**) |
oauth |
object | (see below) | OAuth 2.0 configuration |
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable/disable OAuth 2.0 endpoints. When false, discovery and all OAuth routes return 404 |
accessTokenTtl |
string | 1h |
OAuth access token lifetime (separate from UI accessTokenTtl) |
refreshTokenTtl |
string | 7d |
OAuth refresh token lifetime (separate from UI refreshTokenTtl) |
authCodeTtl |
string | 10m |
Authorization code lifetime for PKCE flow |
Can be set at four levels: server.model, projects.<id>.model, projects.<id>.graphs.<name>.model, workspaces.<id>.model.
Resolution order (first-defined-wins, whole object, no field merge):
graph.model → project.model → server.model → defaults
The code graph has its own inheritance chain via codeModel:
graphs.code.model → project.codeModel → server.codeModel → code defaults
| Field | Type | Default (general / code) | Description |
|---|---|---|---|
name |
string | Xenova/bge-m3 / jinaai/jina-embeddings-v2-base-code |
HuggingFace model ID |
pooling |
string | cls / mean |
Pooling strategy: mean or cls |
normalize |
boolean | true |
L2-normalize output vectors |
dtype |
string | q8 |
Quantization: fp32, fp16, q8, q4 |
queryPrefix |
string | "" |
Prefix prepended to search queries |
documentPrefix |
string | "" |
Prefix prepended to documents during indexing |
Can be set at four levels: server.embedding, projects.<id>.embedding, projects.<id>.graphs.<name>.embedding, workspaces.<id>.embedding.
Resolution order (field-by-field merge — each field individually inherits up the chain):
graph.embedding → project.embedding → server.embedding → defaults
| Field | Type | Default | Description |
|---|---|---|---|
batchSize |
number | 1 |
Texts per ONNX forward pass |
maxChars |
number | 24000 |
Max chars fed to embedder per node |
cacheSize |
number | 10000 |
Embedding cache size (0 = disabled) |
remote |
string | — | Remote embedding API URL (replaces local ONNX) |
remoteApiKey |
string | — | API key for remote embedding endpoint |
remoteModel |
string | — | Which model to request from remote API: "default" or "code". Auto-set to "code" for code graph |
| Field | Type | Default | Description |
|---|---|---|---|
projectDir |
string | (required) | Root directory to index |
graphMemory |
string | {projectDir}/.graph-memory |
Where to store graph JSON files |
exclude |
string | — | Additional glob to exclude (merged with server default **/node_modules/**,**/dist/**) |
chunkDepth |
number | 4 |
Max heading depth for chunk boundaries |
maxFileSize |
number | (server default) | Max file size in bytes for indexing |
model |
object | (server default) | Project-level model config |
codeModel |
object | (server default) | Project-level code model config |
embedding |
object | (server default) | Project-level embedding config |
access |
object | — | Per-user access overrides for this project |
author |
object | (root author) | Author for notes/tasks/skills in this project |
graphs |
object | — | Per-graph configuration (see below) |
Each graph (docs, code, knowledge, tasks, files, skills) supports:
| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | true |
Set false to disable the graph entirely |
readonly |
boolean | false |
When true, graph is loaded and searchable but mutations are blocked |
include |
string | (depends on graph) | Glob for file matching (docs/code only) |
exclude |
string | (project fallback) | Additional exclude (merged with project + server) |
model |
object | (project/server fallback; code graph uses codeModel chain) |
Full model config — first-defined-wins, no merge |
embedding |
object | (project/server fallback) | Embedding config — field-by-field merge |
access |
object | — | Per-user access overrides for this graph |
Default patterns:
docs:**/*.mdcode:**/*.{js,ts,jsx,tsx,mjs,mts,cjs,cts}
Setting readonly: true on a graph keeps it loaded and searchable but blocks all mutations:
graphs:
knowledge:
readonly: true # loaded + searchable, mutations blockedHow readonly, enabled, and access interact:
| Setting | Graph loaded? | Read tools | Mutation tools | REST mutations |
|---|---|---|---|---|
enabled: false |
No | Hidden | Hidden | N/A |
enabled: true (default) |
Yes | Visible | Visible | Allowed |
readonly: true |
Yes | Visible | Hidden from MCP | 403 Forbidden |
readonlyis a global setting (not per-user) — it overrides per-userrwaccess. Even users withrwaccess cannot mutate a readonly graph.enabled: falsedisables the graph entirely — it is not loaded and no tools are registered.accesscontrols per-user permissions but is capped byreadonly. A user withrwon a readonly graph effectively hasr.- In the UI, write buttons (create, edit, delete) are hidden for readonly graphs.
users:
alice:
name: "Alice"
email: "alice@example.com"
apiKey: "mgm-key-abc123"
passwordHash: "$scrypt$65536$8$1$salt$hash"| Field | Description |
|---|---|
name |
Display name |
email |
Email address (used for password login in UI) |
apiKey |
API key for programmatic access (REST API, MCP HTTP) |
passwordHash |
Scrypt hash for UI login (generated by graphmemory users add CLI) |
Use the CLI to add users interactively:
graphmemory users add --config graph-memory.yamlWorkspaces group projects that share a single KnowledgeGraph, TaskGraph, and SkillGraph. Each project keeps its own DocGraph, CodeGraph, and FileIndexGraph.
| Field | Type | Description |
|---|---|---|
projects |
string[] | List of project IDs that share this workspace |
graphMemory |
string | Where shared graph JSON files are stored |
mirrorDir |
string | Where shared .notes/, .tasks/, .skills/ are written |
author |
object | Author for shared notes/tasks/skills |
access |
object | Per-user access overrides for workspace graphs |
model |
object | Model config for workspace shared graphs |
codeModel |
object | Model config for code graph in workspace projects |
embedding |
object | Embedding config for workspace shared graphs |
maxFileSize |
number | Max file size (overrides server default) |
exclude |
string | Additional exclude (merged with server default) |
graphs |
object | Per-graph config for shared graphs (knowledge, tasks, skills). Supports enabled, readonly, model, embedding, access |
Expose the server's embedding model as a REST endpoint:
server:
embeddingApi:
enabled: true
apiKey: "emb-secret-key" # optional, separate from user apiKeys
maxTexts: 100 # max texts per request (default 100)
maxTextChars: 10000 # max chars per text (default 10000)See Embeddings for details.
When redis.enabled is true, Redis is used as a shared backend for:
- Session store — auth codes and OAuth sessions (replaces in-memory Map)
- Embedding cache — persistent LRU-equivalent cache keyed by SHA-256 hash of the input text (replaces in-memory LRU cache)
When redis.enabled is false (the default), both fall back to in-memory implementations with no external dependency.
server:
redis:
enabled: true
url: "redis://localhost:6379"
prefix: "mgm:"
embeddingCacheTtl: "30d"| Field | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | false |
Enable Redis backend. When false, in-memory implementations are used |
url |
string | redis://localhost:6379 |
Redis connection URL |
prefix |
string | mgm: |
Key prefix applied to all Redis keys |
embeddingCacheTtl |
string | 30d |
TTL for cached embeddings (e.g. 30d, 24h, 0 = no expiry) |
See Authentication for the full ACL resolution chain.
Restart the server process to apply changes to graph-memory.yaml.