Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

System Architecture

Comprehensive overview of the KOGRAL architecture.

High-Level Architecture

Architecture Overview

The KOGRAL consists of three main layers:

  1. User Interfaces: kb-cli (terminal), kb-mcp (AI integration), NuShell scripts (automation)
  2. Core Library (kb-core): Rust library with graph engine, storage abstraction, embeddings, query engine
  3. Storage Backends: Filesystem (git-friendly), SurrealDB (scalable), In-Memory (cache/testing)

Component Details

kb-cli (Command-Line Interface)

Purpose: Primary user interface for local knowledge management.

Commands (13 total):

  • init: Initialize .kogral/ directory
  • add: Create nodes (note, decision, guideline, pattern, journal)
  • search: Text and semantic search
  • link: Create relationships between nodes
  • list: List all nodes
  • show: Display node details
  • delete: Remove nodes
  • graph: Visualize knowledge graph
  • sync: Sync filesystem ↔ SurrealDB
  • serve: Start MCP server
  • import: Import from Logseq
  • export: Export to Logseq/JSON
  • config: Manage configuration

Technology: Rust + clap (derive API)

Features:

  • Colored terminal output
  • Interactive prompts
  • Dry-run modes
  • Validation before operations

kb-mcp (MCP Server)

Purpose: AI integration via Model Context Protocol.

Protocol: JSON-RPC 2.0 over stdio

Components:

  1. Tools (7):

    • kogral/search: Query knowledge base
    • kb/add_note: Create notes
    • kb/add_decision: Create ADRs
    • kb/link: Create relationships
    • kb/get_guidelines: Retrieve guidelines with inheritance
    • kb/list_graphs: List available graphs
    • kb/export: Export to formats
  2. Resources (6 URIs):

    • kogral://project/notes
    • kogral://project/decisions
    • kogral://project/guidelines
    • kogral://project/patterns
    • kogral://shared/guidelines
    • kogral://shared/patterns
  3. Prompts (2):

    • kb/summarize_project: Generate project summary
    • kb/find_related: Find related nodes

Integration: Claude Code via ~/.config/claude/config.json

NuShell Scripts

Purpose: Automation and maintenance tasks.

Scripts (6):

  • kb-sync.nu: Filesystem ↔ SurrealDB sync
  • kb-backup.nu: Archive knowledge base
  • kb-reindex.nu: Rebuild embeddings
  • kb-import-logseq.nu: Import from Logseq
  • kb-export-logseq.nu: Export to Logseq
  • kb-stats.nu: Graph statistics

Features:

  • Colored output
  • Dry-run modes
  • Progress indicators
  • Error handling

Core Library (kb-core)

Models

Graph:

#![allow(unused)]
fn main() {
pub struct Graph {
    pub name: String,
    pub version: String,
    pub nodes: HashMap<String, Node>,  // ID → Node
    pub edges: Vec<Edge>,
    pub metadata: HashMap<String, Value>,
}
}

Node:

#![allow(unused)]
fn main() {
pub struct Node {
    pub id: String,
    pub node_type: NodeType,
    pub title: String,
    pub content: String,
    pub tags: Vec<String>,
    pub status: NodeStatus,
    pub created: DateTime<Utc>,
    pub modified: DateTime<Utc>,
    // ... relationships, metadata
}
}

Edge:

#![allow(unused)]
fn main() {
pub struct Edge {
    pub from: String,
    pub to: String,
    pub relation: EdgeType,
    pub strength: f32,
    pub created: DateTime<Utc>,
}
}

Storage Trait

#![allow(unused)]
fn main() {
#[async_trait]
pub trait Storage: Send + Sync {
    async fn save_graph(&self, graph: &Graph) -> Result<()>;
    async fn load_graph(&self, name: &str) -> Result<Graph>;
    async fn delete_graph(&self, name: &str) -> Result<()>;
    async fn list_graphs(&self) -> Result<Vec<String>>;
}
}

Implementations:

  1. FilesystemStorage: Git-friendly markdown files
  2. MemoryStorage: In-memory with DashMap
  3. SurrealDbStorage: Scalable graph database

Embedding Provider Trait

#![allow(unused)]
fn main() {
#[async_trait]
pub trait EmbeddingProvider: Send + Sync {
    async fn embed(&self, texts: Vec<String>) -> Result<Vec<Vec<f32>>>;
    fn dimensions(&self) -> usize;
    fn model_name(&self) -> &str;
}
}

Implementations:

  1. FastEmbedProvider: Local fastembed
  2. RigEmbeddingProvider: OpenAI, Claude, Ollama (via rig-core)

Parser

Input: Markdown file with YAML frontmatter

Output: Node struct

Features:

  • YAML frontmatter extraction
  • Markdown body parsing
  • Wikilink detection ([[linked-note]])
  • Code reference parsing (@file.rs:42)

Example:

---
id: note-123
type: note
title: My Note
tags: [rust, async]
---

# My Note

Content with [[other-note]] and @src/main.rs:10

#![allow(unused)]
fn main() {
Node {
    id: "note-123",
    node_type: NodeType::Note,
    title: "My Note",
    content: "Content with [[other-note]] and @src/main.rs:10",
    tags: vec!["rust", "async"],
    // ... parsed wikilinks, code refs
}
}

Configuration System

Nickel Schema

# schemas/kb-config.ncl
{
  KbConfig = {
    graph | GraphConfig,
    storage | StorageConfig,
    embeddings | EmbeddingConfig,
    templates | TemplateConfig,
    query | QueryConfig,
    mcp | McpConfig,
    sync | SyncConfig,
  },
}

Loading Process

User writes: .kogral/config.ncl
    ↓ [nickel export --format json]
JSON intermediate
    ↓ [serde_json::from_str]
KbConfig struct (Rust)
    ↓
Runtime behavior

Double Validation:

  1. Nickel contracts: Type-safe, enum validation
  2. Serde deserialization: Rust type checking

Benefits:

  • Errors caught at export time
  • Runtime guaranteed valid config
  • Self-documenting schemas

Storage Architecture

Hybrid Strategy

Local Graph (per project):

  • Storage: Filesystem (.kogral/ directory)
  • Format: Markdown + YAML frontmatter
  • Version control: Git
  • Scope: Project-specific knowledge

Shared Graph (organization):

  • Storage: SurrealDB (or synced filesystem)
  • Format: Same markdown (for compatibility)
  • Version control: Optional
  • Scope: Organization-wide guidelines

Sync:

Filesystem (.kogral/)
    ↕ [bidirectional sync]
SurrealDB (central)

File Layout

.kogral/
├── config.toml              # Graph metadata
├── notes/
│   ├── async-patterns.md    # Individual note
│   └── error-handling.md
├── decisions/
│   ├── 0001-use-rust.md     # ADR format
│   └── 0002-surrealdb.md
├── guidelines/
│   ├── rust-errors.md       # Project guideline
│   └── testing.md
├── patterns/
│   └── repository.md
└── journal/
    ├── 2026-01-17.md        # Daily journal
    └── 2026-01-18.md

Query Engine

#![allow(unused)]
fn main() {
let results = graph.nodes.values()
    .filter(|node| {
        node.title.contains(&query) ||
        node.content.contains(&query) ||
        node.tags.iter().any(|tag| tag.contains(&query))
    })
    .collect();
}
#![allow(unused)]
fn main() {
let query_embedding = embeddings.embed(vec![query]).await?;
let mut scored: Vec<_> = graph.nodes.values()
    .filter_map(|node| {
        let node_embedding = node.embedding.as_ref()?;
        let similarity = cosine_similarity(&query_embedding[0], node_embedding);
        (similarity >= threshold).then_some((node, similarity))
    })
    .collect();
scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
}

Cross-Graph Query

#![allow(unused)]
fn main() {
// Query both project and shared graphs
let project_results = project_graph.search(&query).await?;
let shared_results = shared_graph.search(&query).await?;

// Merge with deduplication
let combined = merge_results(project_results, shared_results);
}

MCP Protocol Flow

Claude Code                kb-mcp                    kb-core
    │                         │                         │
    ├─ JSON-RPC request ───→ │                         │
    │  kb/search              │                         │
    │  {"query": "rust"}      │                         │
    │                         ├─ search() ──────────→  │
    │                         │                         │
    │                         │                    Query engine
    │                         │                    Text + semantic
    │                         │                         │
    │                         │ ←──── results ─────────┤
    │                         │                         │
    │ ←─ JSON-RPC response ──┤                         │
    │  {"results": [...]}     │                         │

Template System

Engine: Tera (Jinja2-like)

Templates:

  1. Document Templates (6):

    • note.md.tera
    • decision.md.tera
    • guideline.md.tera
    • pattern.md.tera
    • journal.md.tera
    • execution.md.tera
  2. Export Templates (4):

    • logseq-page.md.tera
    • logseq-journal.md.tera
    • summary.md.tera
    • graph.json.tera

Usage:

#![allow(unused)]
fn main() {
let mut tera = Tera::new("templates/**/*.tera")?;
let rendered = tera.render("note.md.tera", &context)?;
}

Error Handling

Strategy: thiserror for structured errors

#![allow(unused)]
fn main() {
#[derive(Error, Debug)]
pub enum KbError {
    #[error("Storage error: {0}")]
    Storage(String),

    #[error("Node not found: {0}")]
    NodeNotFound(String),

    #[error("Configuration error: {0}")]
    Config(String),

    #[error("Parse error: {0}")]
    Parse(String),

    #[error("Embedding error: {0}")]
    Embedding(String),
}
}

Propagation: ? operator throughout

Testing Strategy

Unit Tests: Per module (models, parser, storage)

Integration Tests: Full workflow (add → save → load → query)

Test Coverage:

  • kb-core: 48 tests
  • kb-mcp: 5 tests
  • Total: 56 tests

Test Data: Fixtures in tests/fixtures/

Performance Considerations

Node Lookup: O(1) via HashMap

Semantic Search: O(n) with early termination (threshold filter)

Storage:

  • Filesystem: Lazy loading (load on demand)
  • Memory: Full graph in RAM
  • SurrealDB: Query optimization (indexes)

Embeddings:

  • Cache embeddings in node metadata
  • Batch processing (configurable batch size)
  • Async generation (non-blocking)

Security

No unsafe code: #![forbid(unsafe_code)]

Input validation:

  • Nickel contracts validate config
  • serde validates JSON
  • Custom validation for user input

File operations:

  • Path sanitization (no ../ traversal)
  • Permissions checking
  • Atomic writes (temp file + rename)

Scalability

Small Projects (< 1000 nodes):

  • Filesystem storage
  • In-memory search
  • Local embeddings (fastembed)

Medium Projects (1000-10,000 nodes):

  • Filesystem + SurrealDB sync
  • Semantic search with caching
  • Cloud embeddings (OpenAI/Claude)

Large Organizations (> 10,000 nodes):

  • SurrealDB primary
  • Distributed embeddings
  • Multi-graph federation

Next Steps