Introduction
Zenspace is your personal AI agentic workspace — a self-learning knowledge system that builds your wiki, remembers across sessions, and evolves with your decisions.
Most AI tools are chat boxes. You talk, they respond, and tomorrow it's gone. Zenspace takes a different path: your notes grow into a wiki. Your sessions build memory. Your patterns become wisdom.
The 10 Things That Make Zenspace Different
1. 📝 Notes That Build Themselves
Write normally — agents extract entities, auto-generate wiki pages, detect contradictions, and weave everything into a knowledge graph. Your notes become a living wiki, not a pile of files.
2. 🧠 Three Brains, One System
Short-term memory ("what just happened?") → Mid-term knowledge ("what do I know?") → Long-term wisdom ("what have I learned?"). Session context flows naturally upward. No cold starts.
3. 📈 It Learns From You
Daily reflection, weekly synthesis, Bayesian belief updates. The system spots recurring patterns, surfaces blind spots, and adapts to your workflow. Every session makes the next one sharper.
4. 🔄 Information → Knowledge → Wisdom
5-stage pipeline: Capture → Tidy → Organize → Distill → Fuse. From raw notes to polished wiki to deep wisdom. Data in, decisions out.
5. 🏛️ 13 Agents, Each an Expert
A Greek pantheon of AI specialists: Sisyphus orchestrates, Prometheus plans, Momus gatekeeps quality, Hephaestus executes, Hermes validates, Zeus judges. Each with the right model for the job.
6. 🎯 Smart Model Routing — No Manual Picking
Private data → local Ollama. Complex reasoning → Anthropic. Cost-sensitive → DeepSeek. Per-task fallback chains so nothing fails silently. One config, zero guesswork.
7. 💡 Built-in Decision Engine
Log decisions, compute expected value, set stop-loss lines, detect anti-patterns. The system doesn't just remember what you decided — it helps you decide better next time.
8. 📚 Seed Wisdom — 12 Mental Models + 21 Anti-Patterns
Pre-loaded thinking frameworks: Map ≠ Territory, Circle of Competence, Second-Order Thinking, Hanlon's Razor. Plus behavioral anti-pattern detection. Thinking tools, not just storage.
9. 🔗 Obsidian + OKF Dual Format
All notes are Obsidian-compatible Markdown with [[wikilinks]] and YAML frontmatter — open ~/.zen/vault/ directly in Obsidian, edit in both directions, no import/export. Under the hood, wiki pages follow OKF v0.1 (Open Knowledge Format): typed frontmatter (type: concept|reference|tool|...), bundle-relative links, and structured index files. Two formats, one knowledge base.
10. 🏠 Your Data, Your Rules
Local-first by default. macOS Keychain for secrets. Sensitive data never touches the cloud unless you explicitly allow it. Zero vendor lock-in.
How It Works (The 5-Stage Pipeline)
RAW NOTES ──► TIDY ──► ORGANIZE ──► DISTILL ──► FUSE
│
Entity Extraction
Wiki Generation
Contradiction Detection
│
WISDOM
(MEMORY.md + beliefs)
- Capture — Notes, RSS feeds, and raw files all go into inbox (PDF/Office/ebooks auto-convert to Markdown)
- Tidy — Clean, chunk, normalize into structured Markdown
- Organize — Embed, index, classify — make everything searchable
- Distill — Extract entities, compile wiki pages, detect contradictions
- Fuse — Synthesize wisdom, update beliefs, promote to long-term memory
Who Is It For?
- Knowledge workers who want their notes to actively work for them
- Thinkers who value structured decision-making and mental models
- Privacy-conscious users who want AI without surrendering data
- Power users who want to customize model routing across providers
Next: Installation
Installation
Prerequisites
macOS
- macOS (Apple Silicon or Intel) — primary supported platform
- Rust toolchain 1.80+ (only for building from source)
- No additional dependencies —
sandbox-execis built-in
Linux
- Rust toolchain 1.80+ (only for building from source)
- bubblewrap (
bwrap) — required for sandbox isolation
# Ubuntu/Debian
sudo apt install bubblewrap
# Fedora
sudo dnf install bubblewrap
# Arch
sudo pacman -S bubblewrap
Why bubblewrap? Zen uses Linux namespaces for sandbox isolation. Bubblewrap provides unprivileged sandboxing without requiring root access.
Homebrew (macOS & Linux, Recommended)
brew install savechina/tap/zenspace
Note: On Linux, Homebrew (Linuxbrew) also handles bubblewrap dependency automatically.
From Source
git clone https://github.com/savechina/zenspace.git
cd zenspace
bin/build
./target/release/zen --help
Binary Download
Download pre-built binaries from GitHub Releases:
macOS:
zen-{version}-aarch64-apple-darwin.tar.gz(Apple Silicon)zen-{version}-x86_64-apple-darwin.tar.gz(Intel)
Linux:
zen-{version}-x86_64-unknown-linux-gnu.tar.gz(x86_64)zen-{version}-aarch64-unknown-linux-gnu.tar.gz(ARM64)
Linux Note: After downloading, install bubblewrap:
sudo apt install bubblewrap # Ubuntu/Debian
Post-Installation
Verify the installation:
zen version
Initialize your workspace:
zen workspace init
This creates the ~/.zen/ directory structure and default configuration.
Next: Quick Start
Quick Start
1. First Run — The TUI
zen
That's it. The first run initializes ~/.zen/ with the default directory structure and embedded configuration, then drops you into the interactive TUI — a chat session with your personal agent team.
2. Talk to Your Agent
Type naturally and hit Enter. The agent routes your request across 13 specialists (search, execution, planning, review) and streams the answer back.
| Key | Action |
|---|---|
| Enter | Send message |
| Shift+Enter | Insert newline (multi-line input) |
| Ctrl+J | Insert newline — tmux/SSH-safe fallback |
| Ctrl+D | Exit |
| PageUp / PageDown | Scroll history |
Slash commands: type / to open the command popup. It filters as you type (matched characters highlighted, exact matches sorted first), groups commands by category, and Esc dismisses it. Use /tools to expand or collapse tool-call cards and /thinking to show reasoning blocks.
Rich output: tool calls stream in as collapsible cards (🔧 started / ✅ done with timing and hit counts), the footer shows ● working Ns while a turn runs, large pasted text collapses into a [Pasted N lines / M chars] pill, and diff/patch snippets render with red/green deltas.
3. Feed the Pipeline
Notes don't need a command — just drop files into the inbox. Markdown and plain text pass through as-is; PDF, Office documents (docx/xlsx/pptx), and ebooks (epub/odt/rtf) are converted to Markdown automatically on ingestion:
mkdir -p ~/.zen/vault/inbox
$EDITOR ~/.zen/vault/inbox/q3-planning.md
cp ~/Downloads/annual-report.pdf ~/.zen/vault/inbox/ # converted at intake
The background distill loop picks them up automatically: extracts entities, links them into the knowledge graph, compiles wiki pages, and archives the original. It runs while the TUI is open (or via the zen serve daemon) — no manual step.
Format notes:
- PDF / Office convert in-process — no extra installs.
- epub/odt/rtf convert via pandoc if it's on your
PATH(brew install pandoc); without it those files stay queued and are picked up once installed — nothing is lost. - Files with no extractable text (e.g. scanned image-only PDFs) are quarantined rather than re-processed every cycle; originals are never modified.
4. Check System Health
zen doctor
Runs 8 liveness probes (config, database, memories, daemon, loop, provider, vault, outbox) and exits non-zero if anything needs attention. Add --json for machine-readable output.
5. See What It Built
zen wiki list
Browse the self-built wiki:
zen wiki show <page>
zen wiki lint # orphan pages, broken wikilinks
Next Steps
- Configure LLM providers to unlock AI features
- Set up model routing for agent tasks
- Explore the CLI reference for all available commands
CLI Commands Reference
Zen's CLI surface is intentionally small: the TUI plus 23 subcommands. Heavy capabilities (search, research, dispatch, briefs) run autonomously through the background scheduler and distill loop rather than as manual commands — drop notes into ~/.zen/vault/inbox/ and the system works on them.
General
| Command | Description |
|---|---|
zen | Launch TUI (interactive agent session; first run initializes ~/.zen/) |
zen version | Show version information |
zen --help | Show full help with all commands |
Chat & Sessions
| Command | Description |
|---|---|
zen chat | Interactive LLM chat (non-TUI) |
zen session start | Start an agentic session |
zen session status | Show session status |
zen session list | List sessions |
zen session archive | Archive a session |
Knowledge Pipeline
| Command | Description |
|---|---|
zen wiki list | List wiki pages |
zen wiki show <page> | Show a wiki page |
zen wiki reindex | Rebuild the knowledge index (FTS5 + embeddings; also heals legacy non-NFC alias rows; --dry-run preview, --fts-only resync) |
zen wiki lint | Lint wiki (orphan pages, broken links, stale claims) |
zen wiki distill | Run the consolidation pipeline (inbox → wiki) |
zen wiki rebuild-memory | Rebuild the gateway memory store from Markdown + session archives (requires running daemon) |
zen wiki loop run | Run one self-learning consolidation cycle now |
zen wiki loop status | Loop status (--json includes latency metrics) |
zen wiki loop gaps | Show pending gaps and user questions |
zen wiki loop enable / disable | Toggle the background loop |
Agents, Skills & Self-Learning
| Command | Description |
|---|---|
zen agent list | List registered agents |
zen skill list --json | List learned skills |
zen skill run <name> | Run a skill |
zen skill progress | Show skill learning progress |
zen skill show <name> | Show a skill's details |
zen skill precipitate | Stage a distilled skill draft |
zen skill confirm <name> | Confirm a staged skill |
zen discover run | Run one self-learning cycle (stages 5b/5c/5d) |
zen discover stage / queue | Stage and inspect improvement hypotheses |
zen discover confirm / reject | Review staged hypotheses |
zen discover report | Learning metrics (routing, stalls, calibration, replay) |
zen discover arena | Run distill regression gate vs baselines |
zen discover calibrate --write | Derive decision thresholds from labeled data |
zen discover label status / next / set | Label supply workflow |
System & Daemon
| Command | Description |
|---|---|
zen serve start | Start gateway daemon (--foreground, --http for loopback HTTP carrier, --mcp for stdio MCP mode) |
zen serve status | Daemon health |
zen serve stop | Stop daemon |
zen serve test | Test MCP connectivity |
zen serve install / uninstall | Manage macOS launchd auto-start |
zen doctor | System health: 10 liveness probes (--json for scripts) |
zen clean cache | Clean caches (ungated) |
zen clean all / trash | Destructive cleanup — asks for confirmation unless --yes |
zen logs <service> | View structured logs |
zen audit | Audit log operations |
zen sandbox test | Verify sandbox isolation works |
Workspace & Configuration
| Command | Description |
|---|---|
zen workspace init | Initialize ~/.zen/ workspace structure |
zen config show | Show effective configuration (layers merged) |
zen provider list | List available LLM providers |
zen provider test <name> | Test a provider connection |
zen auth list | List stored credentials |
zen model | Model metadata + routing info |
Utilities
| Command | Description |
|---|---|
zen starter <template> | Generate project scaffold from template |
zen wps <action> | Work process utilities |
zen habit log / list | Habit tracking |
zen goal create / list / status | Goal management |
zen plugin list / install / enable / disable / rehash | Plugin management (WASM tools, MCP) |
Quick Reference
# Daily workflow — all through the TUI
zen
# Or scripted
zen doctor # health check
zen wiki loop status # is the learning loop alive?
zen wiki distill # drain the inbox now
zen discover report # what did the system learn?
# Maintenance
zen clean cache
zen serve status
Full reference: zen --help for the most up-to-date list of commands and flags.
Configuration Overview
Zen uses a 4-layer configuration system that merges settings from multiple sources. Higher-priority layers override lower ones, and you only need to specify the values you want to change.
Configuration Layers
| Priority | Layer | File / Source | Typical Use |
|---|---|---|---|
| 1 (highest) | Environment | ZEN_* environment variables | Temporary overrides, CI/CD, secrets |
| 2 | Global User | ~/.zen/config.toml | User-wide preferences |
| 3 | Embedded Default | config/config.toml (compiled in) | Shipped defaults |
| 4 (lowest) | Built-in Default | Rust Default impls | Last-resort fallbacks |
Configuration is global-only: the
.zen/directory marks a project context (and sandbox allowlist) but is not a configuration layer. All config lives under~/.zen/config.toml.
How Merging Works
Each layer merges cleanly into the previous one. A higher layer only overrides keys it explicitly sets — so you can override just default_model without copying the entire config.
# Example: ~/.zen/config.toml — just override what you need
default_provider = "deepseek"
default_model = "deepseek-v4-flash"
# Only the providers you want to customize
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash"
This minimal config merges with the embedded defaults — all other providers (Ollama, OpenAI, Anthropic, etc.) remain available from the embedded config.
Config Structure
The configuration is organized into these top-level sections:
| Section | Description |
|---|---|
default_provider | Default provider name (references a [providers.*] key) |
default_model | Default model when no task-specific model is set |
[providers.*] | Named provider definitions: connection, model catalog, per-million token costs |
[agents] / [agents.*] | Agent tool grants + per-agent model routing (distill tasks and agent profiles) |
[tui] | TUI theme and knowledge-search mode (fast/full/off) |
[history] | Input history size cap |
[embeddings] | Local embedding provider, model, cache |
[cron] | Background schedules, timezone, LLM cost cap, TUI scheduler kill switch |
[plugin] | Plugin registry (learning, finance, custom plugins) |
[mcp_servers.*] | MCP client server connections |
[sandbox] | Network access + WASM sandbox permissions |
[agentic.*] | Agent engine tuning: loop, tool loop, review, delegation, retention, … |
[channels.qqbot] | QQ official-bot channel (WS gateway + REST sender) |
[skills.auto_route] | Automatic skill routing |
Viewing Effective Configuration
# Show the complete merged configuration
zen config show
# List available providers
zen provider list
# Test a provider connection
zen provider test <provider-name>
The zen config show command displays the fully merged configuration from all layers, so you can always see exactly what's in effect.
Next: Provider Definitions
Provider Definitions
Providers are the connection endpoints to LLM services. Define them once by name in [providers.*] sections, then reference them in agent routing.
Supported Protocol Types
| Type | Auth Required | Description | Examples |
|---|---|---|---|
ollama | No | Local Ollama server | qwen3.6, llama3, mistral (local) |
openai | API key | Native OpenAI API | gpt-4o, gpt-4o-mini |
anthropic | API key | Native Anthropic Messages API | claude-sonnet-4-6, claude-haiku-4-5 |
gemini | API key | Google Gemini API | gemini-2.0-flash |
cohere | API key | Cohere API | command-r |
mistral | API key | Mistral API | mistral-large-latest |
openai-compatible | API key | OpenAI-compatible endpoints | DeepSeek, Groq, Perplexity, Aliyun, xAI |
anthropic-compatible | API key | Anthropic-compatible endpoints | Moonshot, MiniMax |
mock | No | Testing mock (no external calls) | — |
Basic Provider Configuration
# Local Ollama (no API key needed)
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
default_model = "qwen3.6:35b-mlx"
# OpenAI
[providers.openai]
type = "openai"
base_url = "https://api.openai.com/v1"
api_key = { env = "OPENAI_API_KEY" }
default_model = "gpt-4o-mini"
# Anthropic
[providers.anthropic]
type = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
default_model = "claude-haiku-4-5"
# OpenAI-compatible (DeepSeek)
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash"
# OpenAI-compatible (Aliyun/Qwen)
[providers.aliyun]
type = "openai-compatible"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
api_key = { env = "DASHSCOPE_API_KEY" }
default_model = "qwen3.6-plus"
Dual-Protocol Providers
Some providers support both OpenAI-compatible and Anthropic-compatible protocols:
# DeepSeek: OpenAI-compatible (default)
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
# DeepSeek: Anthropic-compatible (alternative)
[providers.deepseek-anthropic]
type = "anthropic-compatible"
base_url = "https://api.deepseek.com/anthropic"
api_key = { env = "DEEPSEEK_API_KEY" }
# Moonshot: either protocol works
[providers.moonshot]
type = "openai-compatible"
base_url = "https://api.moonshot.cn/v1"
api_key = { env = "MOONSHOT_API_KEY" }
default_model = "kimi-k2.5"
API Key Resolution
API keys are resolved lazily at first use (not during startup), following this order per provider:
api_key.env— named environment variable (e.g.,DEEPSEEK_API_KEY)api_key.keychain— macOS Keychain service name (e.g.,zen-deepseek-api-key)api_key_env— legacy env var field (deprecated){PROVIDER}_API_KEY— auto-derived env var (e.g.,OPENAI_API_KEY)- Ollama/local providers — no auth required
# Using environment variable
api_key = { env = "DEEPSEEK_API_KEY" }
# Using macOS Keychain
api_key = { keychain = "zen-deepseek-api-key" }
# Direct env var name (auto-derived if not specified)
# ^ will try: DEEPSEEK_API_KEY automatically
Keychain Integration
On macOS, Zen integrates with the system Keychain for secure credential storage:
# Store an API key in Keychain
security add-generic-password -a "zen" -s "zen-openai-api-key" -w "sk-..."
# Configure provider to use Keychain
[providers.openai]
type = "openai"
api_key = { keychain = "zen-openai-api-key" }
If Keychain is unavailable (non-macOS, headless environment), Zen falls back to environment variables automatically.
Provider Fields Reference
| Field | Required | Description |
|---|---|---|
type | Yes | Protocol type (see table above) |
base_url | For some types | API endpoint URL (Ollama: http://127.0.0.1:11434) |
api_key | For cloud providers | Secret reference (env var or keychain) |
api_key_env | No | Legacy env var name (deprecated) |
default_model | Recommended | Default model name for this provider |
models | No | Per-model catalog with parameters (see Model Catalog) |
wire_api | No | Wire protocol: "completions" (default) or "responses" |
Next: Model Catalog & Parameters
Model Catalog & Parameters
Each provider can define a model catalog — a map of model names to their API identifiers, generation parameters, and named parameter variants.
Defining Models
When a models map is present under a provider, default_model selects a key in this map:
[providers.openai]
type = "openai"
api_key = { env = "OPENAI_API_KEY" }
default_model = "gpt-4o-mini" # selects from [providers.openai.models]
[providers.openai.models.gpt-4o]
model = "gpt-4o"
options = { temperature = 0.7, max_tokens = 4096 }
[providers.openai.models.gpt-4o-mini]
model = "gpt-4o-mini"
options = { temperature = 0.3, max_tokens = 2048, reasoning_effort = "low" }
When models is absent, default_model is used directly as the API model name (backward compatible):
# Simple configuration — no model catalog
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash" # used directly as API model name
Model Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
temperature | float (0.0–2.0) | Sampling temperature. Higher = more random | 0.7 |
max_tokens | integer | Maximum tokens in the response | 4096 |
reasoning_effort | string | Reasoning depth: "low", "medium", "high" | "high" |
top_p | float (0.0–1.0) | Nucleus sampling threshold | 0.9 |
Complete Example
[providers.anthropic]
type = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
default_model = "claude-sonnet-4-6"
[providers.anthropic.models.claude-sonnet-4-6]
model = "claude-sonnet-4-6"
options = { temperature = 0.5, max_tokens = 8192 }
[providers.anthropic.models.claude-haiku-4-5]
model = "claude-haiku-4-5"
options = { temperature = 0.3, max_tokens = 4096 }
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
default_model = "qwen3.6-35b-mlx"
[providers.ollama.models.qwen3.6-35b-mlx]
model = "qwen3.6:35b-mlx"
options = { temperature = 0.6, max_tokens = 4096 }
Named Variants
Named variants allow the same model to be used with different parameters for different agents. This is useful when the same model needs different reasoning depths or creativity levels depending on the task.
[providers.anthropic.models.claude-sonnet-4-6]
model = "claude-sonnet-4-6"
options = { temperature = 0.5, max_tokens = 8192 }
# Variant: creative
[providers.anthropic.models.claude-sonnet-4-6.variants.creative]
temperature = 0.9
# Variant: precise
[providers.anthropic.models.claude-sonnet-4-6.variants.precise]
temperature = 0.1
reasoning_effort = "high"
Agents reference variants via the variant field:
[agents.Prometheus]
provider = "anthropic"
model = "claude-sonnet-4-6"
variant = "precise" # uses temperature=0.1, reasoning_effort="high"
Variant parameters merge into the base model options — they only override the fields specified.
Parameter Resolution Order
When an agent makes a call, parameters are resolved in this order:
- Model-level defaults from the model catalog entry
- Variant overrides (if specified)
- Agent-level overrides (if specified in
[agents.*])
This means an agent can always override temperature or max_tokens at the agent config level, regardless of model defaults.
Next: Agent → Model Routing
Agent → Model Routing
Each agent can be assigned a specific provider and model, with a sequential fallback chain for reliability. Routing is defined in [agents.*] sections — both the knowledge-pipeline tasks (notion_extraction, contradiction_detection, synthesis, dispatch) and the 13 agent profiles (Sisyphus, Prometheus, Explore, …).
Basic Routing
[agents.Sisyphus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o" },
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
The router tries providers in order: primary → first fallback → second fallback → ... → Mock (always available).
Agent Routing Fields
| Field | Required | Description |
|---|---|---|
provider | Yes | Primary provider name (must match a [providers.*] key) |
model | No | Model override (falls back to provider's default_model) |
fallbacks | No | Ordered fallback chain if primary fails |
sensitivity | No | Max data sensitivity this agent may handle: "Public", "Private", or "Confidential" — Private/Confidential data is routed to local providers only |
Agent identity traits (role, LLM preferences, clearance) are registry-hardcoded — they are not configurable per agent. Only the routing fields above are read from config.
Fallback Chain
Each fallback step can specify:
| Field | Description |
|---|---|
provider | Provider name for this fallback step |
model | Override model (optional, uses provider's default if omitted) |
timeout_secs | Timeout for this step (optional) |
[agents.dispatch]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o", timeout_secs = 30 },
{ provider = "ollama", model = "qwen3.6:35b-mlx" }
]
Sensitivity-Aware Routing
When sensitivity is set to "Private" or "Confidential", Zen enforces local-only routing for sensitive data:
- Private/Confidential data is never sent to cloud providers
- If no local LLM is available, the agent returns an error instead of falling back to cloud
# Knowledge pipeline — private data, local-first
[agents.notion_extraction]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = [{ provider = "deepseek", model = "deepseek-v4-flash" }]
sensitivity = "Private"
Complete Agent Configuration Examples
Orchestrator Tier (requires capable models)
[agents.Sisyphus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o" },
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
Knowledge Pipeline (local-first)
[agents.notion_extraction]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = [
{ provider = "deepseek", model = "deepseek-v4-flash" },
{ provider = "openai", model = "gpt-4o-mini" }
]
sensitivity = "Private"
Fast Explorer (cost-optimized)
[agents.Explore]
provider = "anthropic"
model = "claude-haiku-4-5"
fallbacks = [{ provider = "openai", model = "gpt-4o-mini" }]
Next: Environment Variable Overrides
Environment Variable Overrides
Environment variables provide the highest-priority configuration layer. They're useful for temporary overrides, CI/CD environments, and sensitive values.
Global Overrides
# Default provider and model
export ZEN_DEFAULT_PROVIDER="deepseek"
export ZEN_DEFAULT_MODEL="deepseek-v4-flash"
Per-Task Overrides
Override knowledge-pipeline task routing without modifying any config files. Only these four distill tasks have env overrides:
export ZEN_AGENT_NOTION_EXTRACTION_PROVIDER="ollama"
export ZEN_AGENT_SYNTHESIS_MODEL="claude-sonnet-4-6"
export ZEN_AGENT_DISPATCH_PROVIDER="anthropic"
export ZEN_AGENT_CONTRADICTION_DETECTION_MODEL="qwen3.6:35b-mlx"
Supported task env var targets:
| Env Var | Effect |
|---|---|
ZEN_AGENT_NOTION_EXTRACTION_PROVIDER / _MODEL | Override notion extraction provider / model |
ZEN_AGENT_CONTRADICTION_DETECTION_PROVIDER / _MODEL | Override contradiction detection provider / model |
ZEN_AGENT_SYNTHESIS_PROVIDER / _MODEL | Override synthesis provider / model |
ZEN_AGENT_DISPATCH_PROVIDER / _MODEL | Override dispatch provider / model |
Agent Engine Overrides
The [agentic.*] sections are env-overridable — handy for scripts and CI:
# Distill loop
export ZEN_LOOP_MAX_STEPS=20 # per-cycle distill budget (default 10)
export ZEN_LOOP_MAX_TOKENS=16000 # per-cycle token budget
export ZEN_LOOP_MAX_INGEST_BYTES=134217728 # skip files > 128 MiB
# Tool loop & delegation
export ZEN_TOOL_MAX_ROUNDS=12 # conversational tool rounds (default 8)
export ZEN_DELEGATE_MAX_DEPTH=2 # delegation depth (default 1)
export ZEN_DELEGATE_MAX_CONCURRENT=6 # fan-out width (default 4)
# Quality review
export ZEN_REVIEW_MAX_MOMUS_RETRIES=3
export ZEN_REVIEW_LLM_HIGH_BLAST=true
# Calibrated decision gates (absent = gate closed — pre-calibration behavior)
export ZEN_INTENT_L1_THRESHOLD=0.85
export ZEN_REVIEW_ESCALATE_THRESHOLD=0.9
# Retention sweep
export ZEN_RETENTION_DRY_RUN=true # report-only, delete nothing
Full list by section:
| Section | Env Vars |
|---|---|
[agentic.loop] | ZEN_LOOP_ENABLED, _INTERVAL, _MERGE_THRESHOLD, _MAX_ATTEMPTS, _MIN_FREE_BYTES, _SKIP_EXTENSIONS, _HOST_STAGE_TIMEOUT_SECS, _MAX_STEPS, _MAX_TOKENS, _MAX_INGEST_BYTES, _COMMUNITY_RESOLUTION, _COMMUNITY_MIN_SIZE, _DISTILL_ALLOW_CLOUD, _TREE_INDEX_PAGES |
[agentic.cache] | ZEN_CACHE_BREAKPOINTS |
[agentic.review] | ZEN_REVIEW_MAX_MOMUS_RETRIES, _MAX_HERMES_REVISIONS, _LLM_HIGH_BLAST, _ESCALATE_THRESHOLD |
[agentic.delegate] | ZEN_DELEGATE_ENABLED, _TIMEOUT_SECS, _MAX_DEPTH, _MAX_CONCURRENT |
[agentic.orchestrator] | ZEN_ORCHESTRATOR_SURFACE (full | delegation-only) |
[agentic.intent] | ZEN_INTENT_SHADOW_EMBEDDING, ZEN_INTENT_L1_THRESHOLD |
[agentic.classifiers] | ZEN_CLASSIFIER_CORRECTION_THRESHOLD, ZEN_CLASSIFIER_CITATION_THRESHOLD |
[agentic.audit] | ZEN_AUDIT_DECISION_EXCERPT_CHARS (0 = off, ≤500) |
[agentic.retention] | ZEN_RETENTION_ENABLED, ZEN_RETENTION_DRY_RUN |
[skills.auto_route] | ZEN_SKILLS_AUTO_ROUTE |
[channels.qqbot] | ZEN_QQBOT_APP_ID, ZEN_QQBOT_CLIENT_SECRET |
Cron & Scheduler Overrides
export ZEN_CRON_CONSOLIDATION_TIME="03:00"
export ZEN_CRON_TIMEZONE="America/New_York" # IANA name; bad values fall back to Utc with a warning
export ZEN_CRON_SUBCONSCIOUS_INTERVAL_MINUTES=10
export ZEN_CRON_WISDOM_SYNTHESIS="0 0 2 * * 7"
export ZEN_TUI_SCHEDULER=false # disable all background learning outside `zen serve start`
Plugin & Embeddings Overrides
export ZEN_PLUGIN_BASE_PATH="/custom/plugin/path"
export ZEN_LEARNING_AUTO_RESEARCH="true"
export ZEN_LEARNING_INTERVAL="weekly"
export ZEN_FINANCE_BASE_CURRENCY="USD"
export ZEN_EMBEDDINGS_PROVIDER="local"
export ZEN_EMBEDDINGS_LOCAL_PROVIDER="fastembed"
export ZEN_EMBEDDINGS_MODEL="BGEM3"
export ZEN_EMBEDDINGS_CACHE_DIR="/custom/cache"
API Key Environment Variables
# Set API keys for providers
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export DEEPSEEK_API_KEY="sk-..."
export DASHSCOPE_API_KEY="sk-..."
export GEMINI_API_KEY="..."
export COHERE_API_KEY="..."
export MISTRAL_API_KEY="..."
export GROQ_API_KEY="gsk_..."
export MOONSHOT_API_KEY="..."
export XAI_API_KEY="..."
export PERPLEXITY_API_KEY="..."
Priority Rules
- Explicit
api_key.envin provider config takes precedence over auto-derived names - Environment variables override config files
- Per-task env vars override that task's provider/model settings
- Set env vars in
.envfile (loaded automatically) or export them in your shell profile
Next: Configuration Examples
Configuration Examples
1. Local-Only Setup (Ollama Only)
default_provider = "ollama"
default_model = "qwen3.6:35b-mlx"
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
default_model = "qwen3.6:35b-mlx"
# No other providers needed
For complete offline operation with no cloud dependencies.
Best for: Privacy-critical environments, air-gapped setups, offline use.
2. Cloud-First with Local Fallback
Use cloud for quality, fall back to local when offline:
default_provider = "anthropic"
default_model = "claude-haiku-4-5"
[providers.anthropic]
type = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
default_model = "claude-haiku-4-5"
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
default_model = "qwen3.6:35b-mlx"
[agents.dispatch]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [{ provider = "ollama", model = "qwen3.6:35b-mlx" }]
Best for: Daily driver — cloud quality with offline resilience.
3. Multi-Cloud Hybrid Routing
Route different tasks to different cloud providers based on cost and capability:
default_provider = "deepseek"
default_model = "deepseek-v4-flash"
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash"
[providers.aliyun]
type = "openai-compatible"
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
api_key = { env = "DASHSCOPE_API_KEY" }
default_model = "qwen3.6-plus"
[providers.groq]
type = "openai-compatible"
base_url = "https://api.groq.com/openai/v1"
api_key = { env = "GROQ_API_KEY" }
default_model = "llama-3.3-70b-versatile"
# Knowledge pipeline: local-first, cloud backup
[agents.notion_extraction]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = [
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
# Orchestrator: capable cloud model with fallbacks
[agents.Sisyphus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o" },
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
Best for: Cost optimization with multiple provider accounts.
4. Privacy-Preserving Setup
Local for sensitive data, cloud for public information:
default_provider = "ollama"
default_model = "qwen3.6:35b-mlx"
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
# Private data stays local
[agents.Metis]
provider = "ollama"
sensitivity = "Private"
# Public research can use cloud
[agents.Explore]
provider = "deepseek"
Best for: Healthcare, legal, finance — any domain with data residency requirements.
5. Cost-Optimized Setup
Cheapest capable model for each task tier:
default_provider = "deepseek"
default_model = "deepseek-v4-flash"
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
# Heavy reasoning: use local (free)
[agents.dispatch]
provider = "ollama"
model = "qwen3.6:35b-mlx"
# Light tasks: cheapest cloud API
[agents.Explore]
provider = "deepseek"
model = "deepseek-v4-flash"
# Synthesis: use best model sparingly
[agents.synthesis]
provider = "deepseek"
model = "deepseek-v4-flash"
fallbacks = [{ provider = "ollama", model = "qwen3.6:35b-mlx" }]
Best for: Budget-conscious setups, hobbyist use, development.
6. Complete Production Config
default_provider = "anthropic"
default_model = "claude-haiku-4-5"
[providers.ollama]
type = "ollama"
base_url = "http://127.0.0.1:11434"
default_model = "qwen3.6:35b-mlx"
[providers.anthropic]
type = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
default_model = "claude-haiku-4-5"
[providers.openai]
type = "openai"
api_key = { env = "OPENAI_API_KEY" }
default_model = "gpt-4o-mini"
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash"
# Orchestrator
[agents.Sisyphus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o" },
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
# Planner
[agents.Prometheus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [{ provider = "openai", model = "gpt-4o" }]
# Knowledge pipeline
[agents.notion_extraction]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = [
{ provider = "deepseek", model = "deepseek-v4-flash" },
{ provider = "openai", model = "gpt-4o-mini" }
]
# Fast exploration
[agents.Explore]
provider = "anthropic"
model = "claude-haiku-4-5"
fallbacks = [{ provider = "openai", model = "gpt-4o-mini" }]
# Privacy-sensitive analysis
[agents.Metis]
provider = "deepseek"
model = "deepseek-v4-flash"
fallbacks = [{ provider = "ollama", model = "qwen3.6:35b-mlx" }]
sensitivity = "Private"
# Worker agents
[agents.Hephaestus]
provider = "anthropic"
model = "claude-sonnet-4-6"
fallbacks = [
{ provider = "openai", model = "gpt-4o" },
{ provider = "deepseek", model = "deepseek-v4-flash" }
]
[agents.Atlas]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = [{ provider = "deepseek", model = "deepseek-v4-flash" }]
[agents.Junior]
provider = "ollama"
model = "qwen3.6:35b-mlx"
fallbacks = []
7. Self-Learning Loop Tuning
The background distill loop and its guards are tuned under [agentic.*]. All keys are optional — unset keys use built-in defaults:
# Distill loop: how fast the inbox drains
[agentic.loop]
interval = "0 */5 * * * *" # every 5 minutes (cron, [cron] timezone applies)
max_steps = 10 # per-cycle budget, clamp 1..=100 (env ZEN_LOOP_MAX_STEPS)
max_tokens = 8000 # per-cycle LLM token budget
max_ingest_bytes = 67108864 # skip+quarantine files > 64 MiB
reverify_older_than_days = 7 # re-verify hypotheses older than this
# LLM distill enrichment (T046): names a PROVIDER (e.g. "ollama") whose
# configured default model extracts notions per note and composes merge
# sections. Unset = deterministic heuristic path only (default). Any LLM
# failure is fail-open.
# merge_llm_model = "ollama"
# distill_allow_cloud = false # must be true to let a cloud provider serve distill calls
# tree_index_pages = 20 # notes longer than this many 3000-char pages are
# distilled one call per heading section (clamp 5..=200)
# Prompt-cache breakpoints (Anthropic routing only)
[agentic.cache]
breakpoints = true # cache the stable system prefix (env ZEN_CACHE_BREAKPOINTS)
# Agent tool loop: rounds per conversational turn
[agentic.tool_loop]
max_rounds = 8 # clamp 1..=16 (env ZEN_TOOL_MAX_ROUNDS)
# Sub-agent delegation from the orchestrator
[agentic.delegate]
enabled = true
max_depth = 1 # how deep delegation can nest, clamp 1..=3
max_concurrent = 4 # parallel fan-out, clamp 1..=8
timeout_secs = 300 # clamp 30..=1800
# Daily retention sweep (log/archive rotation + age deletes)
[agentic.retention]
enabled = true
dry_run = false # true = report only, delete nothing
Best for: Tuning throughput vs cost, or taming the loop on low-power machines.
8. QQ Bot Channel
Enable the QQ official-bot channel so allowlisted chats converse with your persistent agent sessions. Requires zen serve start (the channel runs inside the gateway daemon):
[channels.qqbot]
app_id = "your-qq-app-id" # or env ZEN_QQBOT_APP_ID
client_secret = "your-qq-secret" # or env ZEN_QQBOT_CLIENT_SECRET
allowed_users = [ # deny-by-default; group member openids
"ABCDEF1234567890", # and C2C user ids may be mixed
]
outbox_drain_interval_secs = 300 # morning-brief active-send tick, clamp 60..=3600
In chat, /new resets the session and /status renders gateway health. zen doctor fails the outbox probe with an actionable message when briefs are staged but the channel is unconfigured.
Best for: Talking to your knowledge base from QQ, receiving the morning brief.
9. Cost Tracking & Budget Cap
Per-provider token costs (USD per million tokens) make the [cron] budget cap real, and zen discover report surfaces actual spend:
[cron]
llm_cost_cap_usd = 10.0 # monthly cap across scheduled workers (default 10.0)
[providers.deepseek]
type = "openai-compatible"
base_url = "https://api.deepseek.com"
api_key = { env = "DEEPSEEK_API_KEY" }
default_model = "deepseek-v4-flash"
input_cost_per_million = 0.27
output_cost_per_million = 1.10
[providers.anthropic]
type = "anthropic"
api_key = { env = "ANTHROPIC_API_KEY" }
default_model = "claude-haiku-4-5"
input_cost_per_million = 0.80
output_cost_per_million = 4.00
Local (Ollama) usage is structurally free — cost 0.0. Check actual spend with zen discover report (worker costs persist under ~/.zen/logs/worker-costs.json).
Best for: Keeping scheduled background learning inside a predictable budget.
Next: Introduction — back to guide start
System Architecture
Zen is a Rust CLI productivity suite with agentic workspace architecture. It follows a binary/library split with 12 workspace crates.
High-Level Architecture
┌─────────────────────────────────────────────┐
│ zen (binary, 13 lines) │
│ loads .env, calls zen_cli::shell() │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ zen-cli (library) │
│ clap Parser (23 commands), TUI (ratatui) │
│ command dispatcher │
└──┬────┬────┬────┬────┬────┬────┬────┬───────┘
│ │ │ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼ ▼ ▼
┌────┬────┬────┬────┬────┬────┬────┬──────┐
│zen │zen │zen │zen │zen │zen │zen │zen │
│core│repo│vault│agents│mem │prov│auth│... │
└────┴────┴────┴────┴────┴────┴────┴──────┘
Crates Overview
| Crate | Role | Key Modules |
|---|---|---|
| zen | Binary entry (13-line main.rs) | .env loading, dispatches to zen_cli::shell() |
| zen-cli | CLI library | 23 commands, TUI (ratatui), clap derive dispatch |
| zen-core | Core infrastructure | 4-layer global-only config, error taxonomy, path scoping, constants, secrets |
| zen-service | Business logic | Starter/wps/cleanup services |
| zen-repo | Data layer | sqlx + rusqlite dual API, FTS5, vec0, graph schema |
| zen-vault | Knowledge services | Note, Wiki, 5-tier search, consolidation, lint, ingest |
| zen-agents | Agent system | 13 agents, 4 tiers, plan-DAG executor, QualityPipeline, registry, scheduler workers |
| zen-provider | LLM routing | 13 providers, 3 protocol types, DefaultRouter, auth resolution |
| zen-memory | Identity context | SOUL.md, MEMORY.md loading and management |
| zen-auth | Credential management | Keychain integration, SecretRef resolution |
| zen-plugin | Extension system | WASM sandbox (wasmtime), MCP client/server, agent tools |
| zen-gateway | Sole-owner daemon | UDS JSON-RPC 2.0, hosted agent sessions, loopback HTTP carrier, MCP stdio, QQ bot channel |
Key Architectural Patterns
Binary/Library Split
The zen binary (13 lines in crates/zen/src/main.rs) loads .env, calls zen_core::config::load_config(), then delegates to zen_cli::shell().await. All logic lives in library crates.
4-Layer Configuration (global-only)
Rust built-in defaults → embedded config.toml → ~/.zen/config.toml → ZEN_* env vars
Each layer merges into the previous one — higher layers override only the keys they set. Configuration is global-only: the .zen/ directory marks project context but is not a config layer.
4-Tier Agent Architecture
Orchestrator (L0) → Planner (L1) → Specialist (L2) → Worker (L3)
- Orchestrator: Session coordination, intent routing (AgentOrchestrator, Sisyphus)
- Planner: Task decomposition, plan-DAG execution (Prometheus)
- Specialist: Domain expertise (search, consolidate, research)
- Worker: Execution, tool calling (AgentExecutor)
5-Tier Search Pipeline
ripgrep → FTS5 → vector embeddings → entity graph → LLM
Each tier adds depth: keyword search first, semantic when needed.
13 Built-In Agents
| Agent | Tier | Role |
|---|---|---|
| Sisyphus | L0 | Lead orchestrator |
| Prometheus | L1 | Task planner |
| Metis | L1 | Plan correctness review |
| Momus | L1 | Plan quality review |
| Zeus | L1 | Escalation handler |
| Oracle | L2 | Architecture consultation |
| Explore | L2 | Codebase exploration |
| Librarian | L2 | External reference search |
| Argus | L2 | Monitoring & observation |
| Hephaestus | L3 | Tool execution |
| Atlas | L3 | Knowledge pipeline |
| Junior | L3 | General task execution |
| Hermes | L3 | Safety & audit |
Data Flow: Note Creation to Wiki
note dropped into vault/inbox/ → ZenScheduler (zen_loop worker, every 5 min)
→ zen-vault (DistillationPipeline: extract → normalize → compile → merge → archive)
→ zen-vault ingest (pdf/office/sidecar formats convert to Markdown at intake)
→ zen-provider (entity extraction)
→ zen-repo (graph entities + FTS5 + embeddings)
→ zen-vault (WikiPage generation, AtomicWikiWriter)
search (TUI chat / agent tools) → zen-vault (SearchService)
→ tier 1: ripgrep
→ tier 2: FTS5 (notes_fts)
→ tier 3: vec0 embeddings
→ tier 4: entity graph (+ PPR seeded ranking)
→ tier 5: LLM semantic reranking
Learn more: AGENTS.md for the full project knowledge base.
Development
Prerequisites
- Rust 1.80+ (edition 2024)
- macOS (primary development platform)
Build
# Build all crates
cargo build
# Build release binary
bin/build
Test
# Run all tests
cargo test
# Run with nextest (recommended for env-isolated tests)
cargo nextest run
# Run specific crate tests
cargo test -p zen-core
cargo test -p zen-provider
cargo test -p zen-agents
Note: Some tests (particularly path tests) require
cargo nextestfor process isolation because they modify environment variables.
Lint & Format
# Run linter
bin/lint
# Or individually:
cargo clippy -- -D warnings
cargo fmt --all --check
# Format code
cargo fmt --all
Project Structure
zenspace/
├── crates/ # 12 Rust workspace crates
│ ├── zen/ # Binary entry (13-line main.rs)
│ ├── zen-cli/ # CLI library + TUI
│ ├── zen-core/ # Core infrastructure (13 modules)
│ ├── zen-service/ # Business logic
│ ├── zen-repo/ # Data layer (sqlx + rusqlite)
│ ├── zen-vault/ # Knowledge services
│ ├── zen-agents/ # Agent system
│ ├── zen-provider/ # LLM provider routing
│ ├── zen-memory/ # Identity context
│ ├── zen-auth/ # Credential management
│ ├── zen-plugin/ # WASM sandbox + MCP
│ └── zen-gateway/ # HTTP daemon
├── config/ # Embedded config.toml
├── templates/ # Tera templates
├── docs/ # Documentation
│ ├── src/ # User guide (mdBook source)
│ │ ├── introduction.md
│ │ ├── installation.md
│ │ ├── quickstart.md
│ │ └── ...
│ ├── theme/ # mdBook custom theme
│ └── book/ # Generated HTML output
└── bin/ # Build/test/lint/release scripts
Architecture specifications: deep architecture/spec docs live in AGENTS.md (the dev-facing knowledge base). The former
docs/specs/directory is no longer present on disk.
Release
# Automated release
bin/release patch # 0.1.0 → 0.1.1
bin/release minor # 0.1.0 → 0.2.0
bin/release major # 0.1.0 → 1.0.0
# Manual release
echo "0.1.2" > VERSION
git commit -am "release: v0.1.2"
git tag v0.1.2
git push origin main --tags
Code Conventions
- Imports:
use crate::for internal,use zen_*::for cross-crate - Errors:
thiserrorfor libraries,anyhowfor app-level - CLI commands: Each command in
src/cmd/{name}_command.rswithpub fn execute_command() - Config: Embedded
config/config.toml+ user~/.zen/config.toml - Tests: Integration tests only (no inline
#[cfg(test)]in most crates)
Design Principles
See AGENTS.md for the full project constitution:
- CLI-First — Every feature via CLI subcommands
- Robust Error Handling —
thiserrorfor types,anyhowfor propagation - Observability — Structured logging via
tracing - Configuration —
.envviadotenvy, 4-layer global-only config inheritance - Template-Driven — Embedded templates via
include_dir - Code Quality — Zero warnings,
unsafeblocks justified - Single Responsibility — 12 crates with clear boundaries
- Testing — Unit + integration tests required
- UX Consistency — JSON/human-readable dual output
- Performance — <500ms cold start, <50MB footprint
- Design-First & Reuse — Reuse frameworks, don't reinvent