## 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

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

### Text Search

    #![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();
    }

### Semantic Search

    #![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

- Graph Model Details: Graph Model
- Storage Deep Dive: Storage Architecture
- ADRs: Architectural Decisions
- Implementation: Development Guide