vault-os/vault-os-design-spec.md
Harald Hoyer f820a72b04 Initial implementation of vault-os
Complete implementation across all 13 phases:

- vault-core: types, YAML frontmatter parsing, entity classification,
  filesystem ops, config, prompt composition, validation, search
- vault-watch: filesystem watcher with daemon write filtering, event
  classification
- vault-scheduler: cron engine, process executor, task runner with
  retry logic and concurrency limiting
- vault-api: Axum REST API (15 route modules), WebSocket with broadcast,
  AI assistant proxy, validation, templates
- Dashboard: React + TypeScript + Tailwind v4 with kanban, CodeMirror
  editor, dynamic view system, AI chat sidebar
- Nix flake with dev shell and NixOS module
- Graceful shutdown, inotify overflow recovery, tracing instrumentation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 01:21:17 +01:00

63 KiB

vault:os — Design Specification

Overview

vault:os is a personal AI operations platform built as a single Rust daemon. It turns a directory of markdown files into a reactive system — a combined knowledge base, task manager, agent orchestrator, cron scheduler, and web dashboard.

The core philosophy: everything is a markdown file with YAML frontmatter. Agents, tasks, cron jobs, and knowledge are all defined as .md files in a directory tree (the "vault"). The daemon watches the filesystem, reacts to changes, executes agents on schedule, and serves a web dashboard.

Architecture

Single Binary Daemon

One long-running process, one systemd unit, one config: the path to the vault directory.

vault-os --vault /path/to/vault --port 8080

The daemon is fully reactive. No polling. It uses inotify (via the notify crate) for filesystem events and computes sleep timers for cron scheduling. When nothing is happening, it consumes essentially zero resources.

Core Event Loop

tokio::select! {
    event = fs_watcher.next() => handle_fs_event(event),
    _ = sleep_until(next_cron_time) => execute_scheduled_cron(),
    msg = ws_receiver.next() => handle_dashboard_action(msg),
}

Three event sources, no polling:

  1. Filesystem events (inotify) — file created, modified, deleted, moved in the vault
  2. Cron timer — a single tokio::time::sleep_until targeting the nearest next cron execution; recomputed when cron definitions change
  3. WebSocket messages — actions from the dashboard (move task, trigger agent, edit file)

Crate Structure

vault-os/
├── Cargo.toml              # workspace
├── crates/
│   ├── vault-core/         # types, frontmatter parsing, filesystem ops
│   ├── vault-scheduler/    # cron engine, agent spawning, task lifecycle
│   ├── vault-watch/        # inotify watcher, debouncing, event classification
│   └── vault-api/          # axum web server, REST API, WebSocket, static assets
├── src/
│   └── main.rs             # wires crates together, runs the event loop
└── flake.nix               # nix build

Vault Directory Structure

vault/
├── agents/                 # agent definitions
│   ├── github-triage.md
│   ├── pr-reviewer.md
│   ├── news-monitor.md
│   ├── email-triage.md
│   └── research-assistant.md
├── skills/                 # reusable agent skills
│   ├── vault/              # vault operation skills (how to interact with the vault)
│   │   ├── create-ticket.md
│   │   ├── move-ticket.md
│   │   ├── write-knowledge.md
│   │   ├── queue-agent-task.md
│   │   ├── read-vault.md
│   │   ├── search-vault.md
│   │   ├── link-files.md
│   │   ├── modify-ui.md
│   │   ├── request-tool.md
│   │   └── create-skill.md
│   ├── github-notifications.md
│   ├── ticket-creation.md
│   ├── markdown-summarize.md
│   ├── pr-diff-analysis.md
│   └── knowledge-writer.md
├── crons/
│   ├── active/             # enabled cron jobs
│   │   ├── every-15min-github-triage.md
│   │   ├── daily-0900-news-digest.md
│   │   └── weekly-mon-project-summary.md
│   ├── paused/             # disabled cron jobs (ignored by scheduler)
│   └── templates/          # cron templates for creating new jobs
├── todos/
│   ├── harald/             # human task board
│   │   ├── urgent/
│   │   ├── open/
│   │   ├── in-progress/
│   │   └── done/
│   └── agent/              # agent task queue
│       ├── queued/         # pending tasks for agents to pick up
│       ├── running/        # currently executing
│       ├── done/           # completed
│       └── failed/         # failed with error details in body
├── knowledge/              # knowledge base (free-form structure)
│   ├── projects/
│   ├── github/
│   ├── security/
│   ├── notes/
│   └── ...
├── views/                  # dashboard UI definitions
│   ├── layouts/            # page layout templates
│   ├── pages/              # page definitions (user-managed)
│   ├── widgets/            # widget definitions
│   ├── custom/             # agent-created views
│   └── notifications/      # ephemeral agent notifications
└── .vault/                 # daemon state (git-ignored)
    ├── state.json          # runtime state, last cron runs, etc.
    └── logs/               # agent execution logs

File Formats

Agent Definition (agents/*.md)

---
name: github-triage
executable: claude-code
model: local/qwen3
escalate_to: claude-sonnet-4
escalate_when:
  - "diff > 500 lines"
  - "security-related"
mcp_servers:
  - github
  - forgejo
skills:
  - github-notifications
  - ticket-creation
timeout: 300
max_retries: 3
env:
  GITHUB_TOKEN: "${GITHUB_TOKEN}"
  FORGEJO_URL: "https://forgejo.example.com"
---

# GitHub Triage Agent

You are a notification triage agent. Your job is to fetch GitHub
notifications, classify them, and route them appropriately.

## Classification
...

## Routing
...

Frontmatter fields:

Field Type Required Description
name string yes Unique agent identifier
executable string yes Command to run (e.g., claude-code, ollama, custom binary)
model string no Model identifier passed to executable
escalate_to string no Fallback model for complex tasks
escalate_when string[] no Conditions for model escalation
mcp_servers string[] no MCP server names this agent can use
skills string[] no Claude Code skill names
timeout int no Max execution time in seconds (default: 600)
max_retries int no Retry count on failure (default: 0)
env map no Environment variables; supports ${VAR} expansion from daemon env

Markdown body is the system prompt, passed to the executable as-is.

Skill Definition (skills/*.md)

Skills are reusable capability modules that agents reference. They define a focused piece of functionality — a tool, a procedure, or domain knowledge — that can be composed across multiple agents. When an agent lists a skill in its skills frontmatter, the skill's markdown body is appended to the agent's system prompt at execution time.

---
name: ticket-creation
description: Create well-structured tickets in the vault from classified inputs
version: 1
requires_mcp:
  - forgejo
inputs:
  - classification
  - source_url
  - summary
outputs:
  - ticket_path
---

# Ticket Creation

You have the ability to create tickets in the vault filesystem.

## Procedure

1. Determine the target directory based on classification:
   - `urgent` or `ci-failure` → `todos/harald/urgent/`
   - `needs-review` or `action-required` → `todos/harald/open/`
   - `informational` → skip ticket, write to `knowledge/` instead

2. Generate the filename: `{date}-{slugified-title}.md`

3. Write the file with frontmatter:
   - `title`: concise summary
   - `priority`: derived from classification
   - `source`: the agent name (your own name)
   - `labels`: derived from classification and context
   - `created`: current timestamp

4. The markdown body should contain:
   - Context section with source link
   - Action needed section
   - Related links to relevant knowledge notes

## Rules

- Never create duplicate tickets — check for existing files with similar titles
- Always include the source URL for traceability
- Keep titles under 80 characters

Frontmatter fields:

Field Type Required Description
name string yes Unique skill identifier, referenced by agents
description string yes Human-readable description of what the skill does
version int no Version number for tracking changes
requires_mcp string[] no MCP servers this skill needs; validated against agent's mcp_servers at execution time
inputs string[] no Expected input data the skill operates on (documentation/validation)
outputs string[] no What the skill produces (documentation/validation)

Markdown body is the skill's instruction text. At execution time, when an agent references this skill, the body is appended to the agent's system prompt under a ## Skills section. Multiple skills are concatenated in the order listed in the agent's skills array.

Skill composition at execution time:

The final prompt sent to the executable is assembled as:

{agent markdown body}

## Skills

### {skill-1 name}
{skill-1 markdown body}

### {skill-2 name}
{skill-2 markdown body}

This keeps skills modular — edit a skill once, all agents using it get the update on their next run.

Vault Operation Skills (skills/vault/)

These are foundational skills that teach agents how to interact with the vault filesystem. Most agents should include at least some of these. They encode the vault's conventions — directory structure, frontmatter schemas, naming patterns — so that every agent produces consistent, well-formed output without each one needing to hardcode these rules.

The vault operation skills are maintained as part of the vault itself, meaning you can evolve vault conventions by editing these skills and all agents pick up the changes immediately.

skills/vault/create-ticket.md — How to create a human task in todos/harald/. Covers: choosing the right status directory based on priority, generating a slug filename, required and optional frontmatter fields, markdown body conventions, deduplication checks.

skills/vault/move-ticket.md — How to change a ticket's status by moving it between directories. Covers: valid status transitions, updating timestamps, preserving file content.

skills/vault/write-knowledge.md — How to write a knowledge note in knowledge/. Covers: choosing the right subdirectory, frontmatter conventions (title, tags, source, related), linking to other vault files, when to create a new note vs append to an existing one.

skills/vault/queue-agent-task.md — How to create a task for another agent in todos/agent/queued/. Covers: required frontmatter (title, agent, priority), structuring the input field, writing clear instructions in the body, setting appropriate max_retries.

skills/vault/read-vault.md — How to discover and read existing vault content. Covers: directory layout reference, how to list files, how to parse frontmatter from other files, how to follow related: links and [[wiki-links]].

skills/vault/search-vault.md — How to find relevant existing content before creating new files. Covers: searching by filename patterns, searching by frontmatter fields (tags, labels, repo), searching markdown body content. Used to avoid duplicates and to find related context.

skills/vault/link-files.md — How to create connections between vault files. Covers: using related: frontmatter fields with relative paths, using [[wiki-link]] syntax in markdown bodies, when to use each approach, bidirectional linking conventions.

skills/vault/modify-ui.md — How to create and modify dashboard views. Covers: creating custom pages in views/custom/ (namespaced by agent name), creating notifications in views/notifications/, available built-in widget components and their props schemas, layout regions, view modification rules (what agents can and cannot touch), how to compose widgets into pages. Also covers how to respond to user requests like "show me a dashboard for X" by generating the appropriate view definition.

skills/vault/request-tool.md — How to request a new tool, skill, or capability that doesn't exist yet. When an agent encounters a task it cannot complete because it lacks the right skill, MCP server, or tooling, it creates a structured request in todos/agent/queued/ targeting a builder agent. Covers: how to describe the missing capability clearly, specifying the interface (inputs/outputs) the new tool should have, providing context and examples of when the tool would be used, priority assessment (blocking vs nice-to-have). The request is a regular agent task but with type: tool-request:

---
title: "Need skill: parse-cargo-toml"
agent: builder
priority: medium
type: tool-request
created: 2026-02-26T14:00:00Z
input:
  requested_by: github-triage
  capability: "Parse Cargo.toml files to extract dependency lists"
  interface:
    inputs: [file_path]
    outputs: [dependencies, dev_dependencies, version]
  use_case: "When triaging PRs that modify Cargo.toml, I need to understand what dependencies changed to assess risk"
  examples:
    - "PR #1234 adds serde_yaml 0.9 — is this a major version bump?"
  blocking: false
---

## Context

The github-triage agent frequently encounters PRs that modify
Cargo.toml but cannot currently parse the file to understand
what changed. This leads to generic triage results instead of
specific dependency change analysis.

## Desired behavior

A skill that can read a Cargo.toml, extract the dependency tree,
and compare two versions to produce a changelog of dependency
changes with risk assessment.

skills/vault/create-skill.md — How to create or modify skills and agent definitions. This is the meta-skill that enables agents to extend the system. Covers: writing well-structured skill markdown with proper frontmatter, testing a skill by creating a test agent task, versioning conventions, how to modify an existing skill safely (increment version, document changes in the body), how to create a new agent definition. Also covers constraints:

  • Agents can create skills in skills/ (including skills/vault/)
  • Agents can create new agent definitions in agents/
  • Modifying existing agent definitions requires user approval — the agent creates a proposal ticket in todos/harald/open/ with the proposed changes as a diff in the body
  • All newly created skills and agents go through a review cycle: created with a status: draft frontmatter field, and a review ticket is created in todos/harald/open/ for the user to approve, test, and activate

Builder Agent

The system includes a special-purpose builder agent (agents/builder.md) that handles tool-request tasks. This agent is capable of:

  1. Reading tool requests from other agents
  2. Understanding the desired interface and use case
  3. Writing new skill definitions (or modifying existing ones)
  4. Creating test tasks to validate the new skill works
  5. Creating a review ticket for the user with a summary of what was built and why

The builder agent should use a powerful model (e.g., claude-sonnet-4 or claude-opus) since it's writing prompts and tool definitions that other agents will depend on.

---
name: builder
executable: claude-code
model: claude-sonnet-4
skills:
  - vault/read-vault
  - vault/search-vault
  - vault/create-skill
  - vault/create-ticket
  - vault/queue-agent-task
timeout: 600
max_retries: 1
---

# Builder Agent

You are the builder agent for vault:os. Your job is to extend
the system by creating new skills, tools, and agent definitions
when other agents request capabilities they don't have.

## Process

1. Read the tool request carefully — understand the use case,
   not just the interface
2. Search existing skills to see if something similar exists
   that could be extended
3. Write the new skill with clear instructions, good examples,
   and proper frontmatter
4. Set status: draft in the frontmatter
5. Create a test agent task that exercises the new skill
6. Create a review ticket for the user explaining:
   - What was requested and by whom
   - What you built and why you made the design choices you did
   - How to test it
   - Any concerns or limitations

## Principles

- Keep skills focused and composable — one skill, one job
- Reuse existing vault skills where possible
- Write clear, unambiguous instructions that work with
  both local models and Claude
- Consider edge cases the requesting agent may not have thought of
- Version existing skills rather than replacing them

Self-Evolution Flow

The complete flow for system self-extension:

  1. Agent encounters a gap — e.g., github-triage can't parse Cargo.toml diffs
  2. Agent creates a tool request — using vault/request-tool skill, writes a task to todos/agent/queued/ targeting the builder agent
  3. Builder picks it up — reads the request, searches for existing skills, designs the solution
  4. Builder creates the skill — writes skills/parse-cargo-toml.md with status: draft
  5. Builder creates a test — queues a test task using the new skill
  6. Builder creates a review tickettodos/harald/open/review-new-skill-parse-cargo-toml.md explaining what was built
  7. User reviews — reads the skill, checks the test results, approves or requests changes
  8. User activates — removes status: draft from the skill, adds it to the relevant agent's skills list
  9. Agent now has the capability — next time github-triage runs, it can parse Cargo.toml diffs

The same flow works for agents requesting new views, new agent definitions, or improvements to existing skills. The builder agent is the bootstrapping mechanism — it can even improve itself by writing better versions of vault/create-skill.

All vault skills reference the VAULT_PATH environment variable (set by the daemon) as the root directory for all filesystem operations.

Example agent using vault skills:

---
name: github-triage
executable: claude-code
model: local/qwen3
skills:
  - vault/read-vault
  - vault/search-vault
  - vault/create-ticket
  - vault/write-knowledge
  - vault/queue-agent-task
  - github-notifications
---

By including the vault skills, this agent knows how to navigate the vault, check for duplicate tickets, create properly formatted tasks, write knowledge notes, and delegate subtasks to other agents — all following consistent conventions.

Cron Definition (crons/active/*.md)

---
schedule: "*/15 * * * *"
agent: github-triage
title: Triage GitHub notifications
enabled: true
last_run: 2026-02-26T10:15:00Z
last_status: success
next_run: 2026-02-26T10:30:00Z
run_count: 47
---

## Task Description

Fetch all unread GitHub notifications, classify them,
and route to the appropriate destination.

## Parameters

Additional context or parameters passed to the agent
alongside its system prompt.

Frontmatter fields:

Field Type Required Description
schedule string yes Standard cron expression (5-field)
agent string yes Reference to agent name in agents/
title string yes Human-readable description
enabled bool no Override; file in active/ implies enabled (default: true)
last_run datetime no Updated by daemon after each run
last_status string no success, failure, timeout — updated by daemon
next_run datetime no Computed and updated by daemon
run_count int no Total runs, updated by daemon

Markdown body is additional task context appended to the agent's system prompt when this cron fires.

Note: The daemon updates last_run, last_status, next_run, and run_count in the frontmatter after each execution. These writes must not re-trigger the cron (the watcher must debounce/ignore daemon-originated writes).

Human Task (todos/harald/<status>/*.md)

---
title: Review PR #1234 — SEV-SNP attestation chain refactor
priority: urgent
source: github-triage
repo: subzero-labs/attestation
labels:
  - needs-review
  - security
created: 2026-02-26T10:30:00Z
due: 2026-02-27T17:00:00Z
---

## Context

PR changes the VLEK certificate validation logic to support
the new AMD EPYC 9005 series processors...

## Action Needed

Review the changes to chain verification and approve or
request changes.

## Links

- PR: https://github.com/subzero-labs/attestation/pull/1234
- Related: knowledge/security/vlek-cert-rotation.md

Frontmatter fields:

Field Type Required Description
title string yes Task summary
priority string no urgent, high, medium, low (default: medium)
source string no Agent name that created this, or self for manually created
repo string no Associated repository
labels string[] no Freeform labels
created datetime yes Creation timestamp
due datetime no Optional deadline

Status is determined by directory, not frontmatter. Moving a file from open/ to in-progress/ changes its status. This keeps state transitions as simple filesystem operations.

Agent Task (todos/agent/<status>/*.md)

---
title: Summarize PR #1234
agent: pr-reviewer
priority: high
type: pr-review
created: 2026-02-26T10:00:00Z
started: null
completed: null
retry: 0
max_retries: 3
input:
  repo: subzero-labs/attestation
  pr: 1234
  diff_url: https://github.com/subzero-labs/attestation/pull/1234.diff
output: null
error: null
---

## Instructions

Fetch the PR diff, summarize the changes, assess risk level,
and create a review summary in knowledge/github/.
If the PR touches security-critical paths, escalate to
claude-sonnet-4 for deeper analysis.

Frontmatter fields:

Field Type Required Description
title string yes Task summary
agent string yes Which agent should execute this
priority string no urgent, high, medium, low (default: medium)
type string no Freeform task type for categorization
created datetime yes Creation timestamp
started datetime no Set by daemon when execution begins
completed datetime no Set by daemon when execution finishes
retry int no Current retry count
max_retries int no Max retries before moving to failed/
input map no Structured input data for the agent
output map no Structured output data from the agent (set after completion)
error string no Error message if failed

Lifecycle:

  1. Task created in queued/ (by cron, another agent, dashboard, or manually)
  2. Daemon detects new file via inotify
  3. Daemon moves file to running/, sets started timestamp
  4. Daemon spawns the referenced agent with the task's markdown body as additional context
  5. On success: daemon moves to done/, sets completed and output
  6. On failure: increment retry; if < max_retries move back to queued/, else move to failed/ with error set

Knowledge Note (knowledge/**/*.md)

Knowledge files have no required structure — they're free-form markdown. Agents and humans write whatever is useful. However, frontmatter is encouraged for discoverability:

---
title: SEV-SNP Firmware Update — February 2026
tags:
  - sev-snp
  - firmware
  - amd
source: news-monitor
created: 2026-02-26T10:02:00Z
related:
  - knowledge/security/vlek-cert-rotation.md
  - todos/harald/open/update-attestation-docs.md
---

## Summary

AMD released firmware update 1.55.24 for EPYC 9004 series...

Crate Details

vault-core

The foundation. Types, parsing, and filesystem operations.

Responsibilities:

  • Define Rust types for all vault entities: Agent, Skill, CronJob, HumanTask, AgentTask, KnowledgeNote
  • Parse YAML frontmatter from markdown files (use serde_yaml for frontmatter, pulldown-cmark for markdown body)
  • Serialize frontmatter back to files (for daemon-updated fields like last_run)
  • Compose agent prompts: resolve skill references and assemble the final prompt (agent body + skill bodies)
  • File path conventions: derive entity type and status from directory structure
  • Validate entities on parse (required fields present, references resolve)

Key types:

pub struct VaultEntity<T: DeserializeOwned> {
    pub path: PathBuf,
    pub frontmatter: T,
    pub body: String,  // raw markdown body
}

pub struct Agent {
    pub name: String,
    pub executable: String,
    pub model: Option<String>,
    pub escalate_to: Option<String>,
    pub escalate_when: Vec<String>,
    pub mcp_servers: Vec<String>,
    pub skills: Vec<String>,       // references to skill names in skills/
    pub timeout: u64,
    pub max_retries: u32,
    pub env: HashMap<String, String>,
}

pub struct Skill {
    pub name: String,
    pub description: String,
    pub version: Option<u32>,
    pub requires_mcp: Vec<String>,
    pub inputs: Vec<String>,
    pub outputs: Vec<String>,
}

pub struct CronJob {
    pub schedule: String,        // cron expression
    pub agent: String,           // agent name reference
    pub title: String,
    pub enabled: bool,
    pub last_run: Option<DateTime<Utc>>,
    pub last_status: Option<RunStatus>,
    pub next_run: Option<DateTime<Utc>>,
    pub run_count: u64,
}

pub enum TaskStatus {
    Urgent,
    Open,
    InProgress,
    Done,
}

pub enum AgentTaskStatus {
    Queued,
    Running,
    Done,
    Failed,
}

pub struct HumanTask {
    pub title: String,
    pub priority: Priority,
    pub source: Option<String>,
    pub repo: Option<String>,
    pub labels: Vec<String>,
    pub created: DateTime<Utc>,
    pub due: Option<DateTime<Utc>>,
}

pub struct AgentTask {
    pub title: String,
    pub agent: String,
    pub priority: Priority,
    pub task_type: Option<String>,
    pub created: DateTime<Utc>,
    pub started: Option<DateTime<Utc>>,
    pub completed: Option<DateTime<Utc>>,
    pub retry: u32,
    pub max_retries: u32,
    pub input: Option<serde_yaml::Value>,
    pub output: Option<serde_yaml::Value>,
    pub error: Option<String>,
}

pub enum Priority {
    Urgent,
    High,
    Medium,
    Low,
}

pub enum RunStatus {
    Success,
    Failure,
    Timeout,
}

Frontmatter parsing approach:

Split file on --- delimiters. First block is YAML, rest is markdown body. Use serde_yaml::from_str for the frontmatter into the appropriate type. When writing back, only update the frontmatter portion, preserving the markdown body exactly.

Critical: When the daemon writes updated frontmatter (e.g., updating last_run on a cron), it must preserve the original markdown body byte-for-byte, including any trailing whitespace or newlines.

vault-watch

Filesystem watching with intelligent event classification.

Responsibilities:

  • Watch the entire vault directory tree using inotify (via notify crate)
  • Debounce rapid filesystem events (editors often write temp files, rename, etc.)
  • Classify events by vault area: agents/, skills/, crons/, todos/harald/, todos/agent/, knowledge/, views/
  • Filter out daemon-originated writes (to prevent feedback loops when the daemon updates frontmatter)
  • Emit typed events to the core event loop

Event types:

pub enum VaultEvent {
    // Agent definitions
    AgentCreated(PathBuf),
    AgentModified(PathBuf),
    AgentDeleted(PathBuf),

    // Skill definitions
    SkillCreated(PathBuf),
    SkillModified(PathBuf),
    SkillDeleted(PathBuf),

    // Cron definitions
    CronCreated(PathBuf),
    CronModified(PathBuf),
    CronDeleted(PathBuf),
    CronMoved { from: PathBuf, to: PathBuf },  // e.g., active/ → paused/

    // Human tasks
    HumanTaskCreated { path: PathBuf, status: TaskStatus },
    HumanTaskModified(PathBuf),
    HumanTaskMoved { from: PathBuf, to: PathBuf, new_status: TaskStatus },
    HumanTaskDeleted(PathBuf),

    // Agent tasks
    AgentTaskCreated { path: PathBuf, status: AgentTaskStatus },
    AgentTaskModified(PathBuf),
    AgentTaskMoved { from: PathBuf, to: PathBuf, new_status: AgentTaskStatus },
    AgentTaskDeleted(PathBuf),

    // Knowledge
    KnowledgeCreated(PathBuf),
    KnowledgeModified(PathBuf),
    KnowledgeDeleted(PathBuf),

    // Views
    ViewCreated(PathBuf),
    ViewModified(PathBuf),
    ViewDeleted(PathBuf),
    NotificationCreated(PathBuf),
    NotificationExpired(PathBuf),

    // Catch-all for unclassified files
    FileChanged(PathBuf),
}

Debouncing strategy:

Use a short debounce window (e.g., 100ms) per file path. Many editors write files in multiple steps (write to temp, rename, update metadata). Coalesce these into a single event. The notify crate's debouncer module handles this.

Daemon write filtering:

The daemon must track its own writes (e.g., in a HashSet<PathBuf> with timestamps) so that when inotify fires for a daemon-originated write, the event is suppressed. Clear tracked paths after the debounce window.

vault-scheduler

Cron scheduling and agent task execution.

Responsibilities:

  • Parse cron expressions from cron job definitions (use cron crate)
  • Maintain a priority queue of upcoming cron firings
  • Compute the next wake time (Instant) for tokio::time::sleep_until
  • Recompute schedule when cron definitions change (created, modified, deleted, moved)
  • Execute agent tasks: pick up from queued/, spawn process, manage lifecycle
  • Handle concurrency limits (configurable max parallel agent executions)
  • Manage retries and failure routing

Cron execution flow:

  1. Timer fires for next scheduled cron
  2. Read the cron definition file, resolve the referenced agent
  3. Create an agent task file in todos/agent/queued/ with the cron's markdown body as instructions
  4. The agent task pickup mechanism handles the rest
  5. Update cron frontmatter: last_run, last_status, next_run, run_count
  6. Recompute next wake time

Agent task execution flow:

  1. Detect new file in todos/agent/queued/ (via inotify)
  2. Check concurrency limits; if at max, leave in queue (will be picked up when a slot opens)
  3. Move file to running/, update started timestamp
  4. Resolve the agent definition from agents/{task.agent}.md
  5. Build the execution context:
    • System prompt = agent markdown body
    • Task context = task markdown body
    • Input data = task input frontmatter field
    • Environment = agent env field with variable expansion
  6. Spawn the executable as a child process via tokio::process::Command
  7. Set up timeout via tokio::time::timeout
  8. Capture stdout/stderr
  9. On success: move to done/, populate output and completed
  10. On failure: increment retry, decide re-queue or move to failed/

Process spawning:

The exact spawning mechanism depends on the executable:

  • claude-code: invoke via CLI with --system-prompt flag (or pipe via stdin)
  • ollama: HTTP API call to local inference server
  • Custom binary: execute directly, pass system prompt via stdin, task context via args or env

This should be abstracted behind an Executor trait:

#[async_trait]
pub trait Executor: Send + Sync {
    async fn execute(
        &self,
        agent: &Agent,
        task: &AgentTask,
        system_prompt: &str,
        task_context: &str,
    ) -> Result<ExecutionResult, ExecutionError>;
}

pub struct ExecutionResult {
    pub stdout: String,
    pub stderr: String,
    pub exit_code: i32,
    pub duration: Duration,
}

Built-in executors: ClaudeCodeExecutor, OllamaExecutor, GenericProcessExecutor.

Concurrency:

Use a tokio::sync::Semaphore to limit parallel agent executions. Default to a sensible limit (e.g., 4). Configurable via CLI flag or a vault-level config file.

vault-api

Web server and dashboard.

Responsibilities:

  • Serve the React dashboard as static assets
  • REST API for reading vault state (tasks, agents, crons, knowledge, activity)
  • REST API for mutations (move task, create task, trigger cron, edit file)
  • WebSocket endpoint for live updates (push vault events to connected clients)
  • Serve rendered markdown for knowledge notes

REST API endpoints:

GET  /api/agents                    # list all agents
GET  /api/agents/:name              # get agent detail + stats
POST /api/agents/:name/trigger      # manually trigger an agent

GET  /api/skills                    # list all skills
GET  /api/skills/:name              # get skill detail + which agents use it
GET  /api/skills/:name/used-by      # list agents referencing this skill

GET  /api/crons                     # list all crons (active + paused)
POST /api/crons/:name/trigger       # manually fire a cron
POST /api/crons/:name/pause         # move to paused/
POST /api/crons/:name/resume        # move to active/

GET  /api/todos/harald              # list human tasks (all statuses)
GET  /api/todos/harald/:status      # list by status
POST /api/todos/harald              # create new task
PATCH /api/todos/harald/:id/move    # move task to different status
DELETE /api/todos/harald/:id        # delete task

GET  /api/todos/agent               # list agent tasks (all statuses)
POST /api/todos/agent               # create/queue new agent task
GET  /api/todos/agent/:id           # get task detail + execution log

GET  /api/knowledge                 # list/search knowledge notes
GET  /api/knowledge/*path           # get rendered note

GET  /api/activity                  # recent activity feed
GET  /api/stats                     # vault statistics

WS   /ws                            # live event stream

WebSocket protocol:

Server pushes JSON events to connected clients:

{
    "type": "task_created",
    "area": "todos/harald/urgent",
    "path": "todos/harald/urgent/review-pr-1234.md",
    "data": { /* parsed frontmatter */ }
}

Event types mirror VaultEvent from vault-watch, serialized as JSON.

Static asset serving:

Serve the built React dashboard from an embedded directory (use include_dir or rust-embed crate) or from a configurable static files path. The latter is better for development.

Configuration

CLI Arguments

vault-os [OPTIONS]

Options:
    --vault <PATH>          Path to vault directory (required)
    --port <PORT>           HTTP/WebSocket port (default: 8080)
    --bind <ADDR>           Bind address (default: 127.0.0.1)
    --max-parallel <N>      Max parallel agent executions (default: 4)
    --log-level <LEVEL>     Log level (default: info)
    --static-dir <PATH>     Dashboard static files (default: embedded)

Vault-Level Config (vault/.vault/config.yaml)

Optional. Overrides for vault-specific settings:

# MCP server definitions referenced by agents
mcp_servers:
  github:
    command: "gh-mcp-server"
    args: ["--transport", "stdio"]
    env:
      GITHUB_TOKEN: "${GITHUB_TOKEN}"
  forgejo:
    command: "forgejo-mcp"
    args: ["--transport", "stdio", "--url", "https://forgejo.example.com"]
    env:
      FORGEJO_ACCESS_TOKEN: "${FORGEJO_ACCESS_TOKEN}"

# Default executor settings
executors:
  claude-code:
    command: "claude"
    default_model: "sonnet"
  ollama:
    base_url: "http://bosgame-m5:11434"
    default_model: "qwen3"

# Task queue settings
queue:
  max_parallel: 4
  default_timeout: 600
  retry_delay: 60        # seconds before retrying a failed task

Key Design Decisions

Status as Directory, Not Field

A task's status is its directory location, not a frontmatter field. This means:

  • Moving a task = mv or rename() — atomic on the same filesystem
  • No risk of frontmatter/directory disagreement
  • Any tool can change status: CLI, file manager, agent, dashboard
  • The daemon never needs to parse a file to know its status — just its path

Daemon-Originated Writes

The daemon updates frontmatter in cron files (after runs) and agent task files (lifecycle). These writes must not trigger reactive behavior. Strategy:

  1. Before writing, add the file path to a pending_writes: HashSet<PathBuf>
  2. Write the file
  3. When inotify fires, check pending_writes — if present, suppress the event and remove from set
  4. Use a short timeout to clean stale entries from pending_writes

Agent Output Handling

Agents produce output in two ways:

  1. Filesystem side effects — writing new files to the vault (knowledge notes, tickets). The daemon doesn't need to mediate this; the agent writes directly and inotify picks up the changes.
  2. Structured output — returned via stdout. The daemon captures this and stores it in the task's output frontmatter field.

Agents should be given the vault path as an environment variable (VAULT_PATH) so they can write files directly.

Hot Reloading

Everything hot-reloads:

  • Edit an agent's markdown → next execution uses the new prompt
  • Edit a cron's schedule → daemon recomputes next fire time immediately
  • Move a cron from active/ to paused/ → removed from schedule immediately
  • Drop a new agent task file in queued/ → picked up immediately

No restart required for any configuration change.

Error Handling

  • Agent timeout: kill the process, move task to failed/ (or retry)
  • Agent crash: capture stderr, store in task error field
  • Invalid frontmatter: log warning, skip file, don't crash the daemon
  • Inotify overflow: if the event queue overflows (rare), do a full vault rescan
  • Filesystem full: log error, pause task creation, surface on dashboard

Idempotency

The daemon should be safe to restart at any time. On startup:

  1. Scan crons/active/ — rebuild schedule
  2. Scan todos/agent/running/ — these were in-flight when the daemon stopped. Move back to queued/ for re-execution (they may have been partially completed; agents should be designed to be idempotent)
  3. Scan agents/ — load all agent definitions
  4. Start inotify watches
  5. Compute next cron time, enter event loop

Dashboard

The dashboard is a React application (separate build artifact) served by the daemon's axum server.

Dynamic UI Architecture

The dashboard is not a static application with hardcoded views. It is agent-customizable — agents (and the user, via the inline assistant) can create, modify, and compose dashboard views by writing view definition files to the vault. The dashboard reads these definitions and renders them dynamically.

This means the UI evolves with usage. An agent can:

  • Create a new dashboard panel to visualize its output
  • Add a widget to the home screen showing a summary it maintains
  • Build a custom view for a specific project or workflow
  • Reorganize the layout based on what the user asks for ("I want to see my security tickets and the news feed side by side")

View Definitions (views/)

vault/
├── views/
│   ├── layouts/
│   │   └── default.md              # default page layout
│   ├── pages/
│   │   ├── home.md                 # home page (task kanban + agent status)
│   │   ├── knowledge.md            # knowledge browser page
│   │   ├── agents.md               # agent management page
│   │   ├── crons.md                # cron overview page
│   │   └── agent-queue.md          # agent task pipeline page
│   ├── widgets/
│   │   ├── kanban.md               # kanban board widget
│   │   ├── agent-status-bar.md     # compact agent health strip
│   │   ├── activity-feed.md        # recent agent activity
│   │   ├── vault-stats.md          # file counts, queue depths
│   │   ├── timeline-gantt.md       # agent execution timeline
│   │   ├── cron-schedule.md        # upcoming cron fires
│   │   └── knowledge-recent.md     # recently modified notes
│   └── custom/                     # agent-created or user-created views
│       ├── security-overview.md    # example: agent-created security dashboard
│       └── project-attestation.md  # example: project-specific view

View Definition Format

A view definition is a markdown file with YAML frontmatter that describes the UI to render.

Page definition (views/pages/home.md):

---
type: page
title: Tasks
icon: check-square
route: /
position: 1
layout: default
regions:
  header:
    - widget: agent-status-bar
      props:
        compact: true
  main:
    - widget: kanban
      props:
        source: todos/harald
        columns:
          - { status: urgent, accent: "#F87171" }
          - { status: open, accent: "#60A5FA" }
          - { status: in-progress, accent: "#FBBF24" }
          - { status: done, accent: "#4ADE80" }
        draggable: true
        card_fields: [title, labels, source, repo, created]
  sidebar:
    - widget: activity-feed
      props:
        filter: "relevant-to:todos/harald"
        max_items: 15
        collapsible: true
---

# Tasks

The primary workspace. Your kanban board with agent-generated
tasks and manual items. Drag cards between columns to change status.

Widget definition (views/widgets/kanban.md):

---
type: widget
name: kanban
description: Kanban board with draggable cards
component: Kanban
props_schema:
  source:
    type: string
    required: true
    description: "Vault directory to read tasks from"
  columns:
    type: array
    items:
      type: object
      properties:
        status: { type: string }
        accent: { type: string, format: color }
    description: "Column definitions with status and accent color"
  draggable:
    type: boolean
    default: true
    description: "Whether cards can be dragged between columns"
  card_fields:
    type: array
    items: { type: string }
    default: [title, labels, source, created]
    description: "Frontmatter fields to display on cards"
  filter:
    type: string
    description: "Optional filter expression for which files to show"
---

# Kanban Widget

Renders a kanban board from a vault directory. Each subdirectory
becomes a column, each markdown file becomes a card.

## Behavior

- Cards are rendered from file frontmatter
- Drag-and-drop moves files between subdirectories
- Click opens the file in the editor
- New card button creates a file in that column's directory
- Cards are sorted by priority, then by created date

Layout definition (views/layouts/default.md):

---
type: layout
name: default
description: Standard page layout with header, main content, and optional sidebar
regions:
  header:
    position: top
    height: auto
  main:
    position: center
    flex: 1
  sidebar:
    position: right
    width: 340px
    collapsible: true
---

# Default Layout

Three-region layout: compact header strip, main content area,
and a collapsible right sidebar.

Built-in Widget Components

The dashboard ships with a set of built-in React components that widget definitions can reference via the component field. These are the rendering primitives:

Component Description
Kanban Kanban board with draggable cards from a vault directory
ActivityFeed Chronological event stream with filtering
AgentStatusBar Compact status indicators for all agents
TimelineGantt Horizontal timeline of agent executions
FileList Sortable, filterable list of vault files
MarkdownViewer Rendered markdown with frontmatter display
CronSchedule Visual cron schedule with next-fire indicators
Stats Key-value stats display with vault path references
Chart Line/bar/area chart from vault data or agent output
Table Data table from vault files with sortable columns
TagCloud Tag frequency visualization across vault files
FileTree Interactive directory browser
Custom Renders custom HTML/React from the widget markdown body (sandboxed)

The Custom component is the escape hatch — agents can generate a widget with arbitrary React JSX in the markdown body. This is rendered in a sandboxed iframe for safety but has access to the vault API for data fetching.

Agent-Driven UI Customization

Agents can modify the dashboard by writing to views/. This is how the UI becomes dynamic:

Scenario 1: Agent creates a project dashboard

The research-assistant agent is asked to track a topic. It creates:

views/custom/sev-snp-tracker.md
---
type: page
title: SEV-SNP Tracker
icon: shield
route: /custom/sev-snp-tracker
position: 10
layout: default
regions:
  main:
    - widget: file-list
      props:
        source: knowledge/security
        filter: "tags contains sev-snp"
        sort: created desc
        card_fields: [title, tags, source, created]
    - widget: chart
      props:
        title: "Related Activity"
        source: knowledge/security
        filter: "tags contains sev-snp"
        x: created
        y: count
        period: week
  sidebar:
    - widget: file-list
      props:
        source: todos/harald
        filter: "labels contains security"
        title: "Security Tasks"
---

# SEV-SNP Tracker

Auto-generated dashboard tracking SEV-SNP related knowledge
notes and security tasks.

This page appears in the navigation automatically because the daemon detects the new view file via inotify and pushes a WebSocket event to the dashboard.

Scenario 2: User asks the inline assistant to customize the home page

User: "I want to see my urgent tickets bigger, and add a small panel showing upcoming crons"

The assistant modifies views/pages/home.md, adjusting the kanban widget props and adding a cron widget to the sidebar. The dashboard live-updates via WebSocket.

Scenario 3: Agent adds a widget to an existing page

The news-monitor agent wants to surface a breaking news alert. It can't modify views/pages/home.md directly (to avoid conflicts), but it can write to a notifications area:

views/notifications/security-alert-2026-02-26.md
---
type: notification
title: "AMD Security Advisory: SEV-SNP firmware update"
priority: high
expires: 2026-03-05T00:00:00Z
target_page: home
source: news-monitor
---

AMD has released a critical firmware update affecting EPYC 9004
series processors. Review knowledge/security/amd-firmware-155.md.

The dashboard renders active notifications as a banner or toast on the target page. Expired notifications are auto-cleaned.

View Modification Rules

To prevent chaos from multiple agents modifying views simultaneously:

  1. Pages in views/pages/ — only the user (via editor or inline assistant) can modify these. Agents can read them but not write.
  2. Custom pages in views/custom/ — agents can create and modify these freely. Each agent should namespace its views (e.g., views/custom/{agent-name}-*.md).
  3. Notifications in views/notifications/ — agents can create these. They're ephemeral and auto-expire.
  4. Widgets in views/widgets/ — agents can create new widget definitions. Modifying existing built-in widgets requires user confirmation (the agent creates a proposal in todos/harald/open/ instead).
  5. Layouts — user-only modification.

A vault skill vault/modify-ui teaches agents these conventions:

---
name: vault/modify-ui
description: How to create and modify dashboard views
version: 1
inputs:
  - view_type
  - content
outputs:
  - view_path
---

REST API for Views

GET    /api/views                     # list all view definitions
GET    /api/views/pages               # list pages (for navigation)
GET    /api/views/widgets             # list available widgets
GET    /api/views/layouts             # list layouts
GET    /api/views/*path               # get specific view definition
PUT    /api/views/*path               # create/update view definition
DELETE /api/views/*path               # delete view

GET    /api/notifications             # list active notifications
DELETE /api/notifications/:id         # dismiss notification

GET    /api/views/schema/widget       # JSON schema for widget props
GET    /api/views/schema/page         # JSON schema for page definition

Views

  1. Tasks (default/home) — the human kanban board (todos/harald/) is the primary view and the first thing you see. Full-width kanban with columns for urgent, open, in-progress, and done. Cards show title, labels, source agent, repo, and age. Drag-and-drop between columns. Click a card to open it in the editor. A compact agent status strip at the top provides at-a-glance awareness of agent health without competing for attention. A collapsible activity feed sidebar shows recent agent actions relevant to your tasks.
  2. Knowledge — browse and search the knowledge base, render markdown, tag filtering, graph view (optional/future)
  3. Agents — agent cards with stats, prompt editor (edit agent markdown inline, saves to filesystem), execution history, agent timeline (Gantt view of today's runs)
  4. Crons — schedule overview, run history, enable/disable toggle, manual trigger button
  5. Agent Queue — view agent task pipeline: queued → running → done/failed

Real-Time Updates

Dashboard connects via WebSocket on load. All vault changes are pushed as events. The dashboard maintains client-side state and applies incremental updates — no full-page refreshes.

Dashboard Actions

Actions from the dashboard translate to filesystem operations via the REST API:

  • Move task → renames file to different status directory
  • Create task → writes new markdown file to todos/harald/open/
  • Queue agent task → writes new markdown file to todos/agent/queued/
  • Trigger cron → creates agent task as if the cron had fired
  • Edit agent prompt → overwrites agent markdown file
  • Pause/resume cron → moves file between active/ and paused/

File Editor

The dashboard includes a full file editor that can create and edit any file in the vault. This is a core feature — the dashboard is a workspace, not just a viewer.

Editor Modes

The editor operates in two modes:

  1. Domain-aware mode — activated when creating or editing files in known vault areas (agents/, crons/, todos/, knowledge/). The editor understands the file type and provides structured assistance.
  2. Raw mode — plain markdown editing for any file anywhere in the vault. Always available as a fallback.

Domain-Aware File Creation

When creating a new file, the editor detects the target directory and offers a structured form for the frontmatter, pre-populated with sensible defaults and validated fields. The markdown body area is a free-form editor below.

Creating in agents/:

  • Form fields: name (auto-derived from filename), executable (dropdown: claude-code, ollama, custom), model (text with suggestions from known models), escalate_to, mcp_servers (multi-select from configured servers in .vault/config.yaml), skills (multi-select/freeform), timeout, max_retries, env (key-value editor)
  • Body: system prompt editor with markdown preview
  • Validation: name and executable required, timeout must be positive integer

Creating in crons/active/ or crons/paused/:

  • Form fields: schedule (cron expression input with human-readable preview, e.g., "*/15 * * * *" shows "every 15 minutes"), agent (dropdown of defined agents from agents/), title
  • Body: task description / additional context
  • Validation: schedule must be valid cron expression, agent must reference an existing agent

Creating in todos/harald/<status>/:

  • Form fields: title, priority (dropdown: urgent/high/medium/low), labels (tag input), due (date picker), source (defaults to self), repo (optional, text with autocomplete from known repos)
  • Body: free-form task description
  • Validation: title required

Creating in todos/agent/queued/:

  • Form fields: title, agent (dropdown of defined agents), priority, type (freeform with suggestions from existing task types), max_retries, input (structured YAML editor or key-value form)
  • Body: instructions for the agent
  • Validation: title and agent required

Creating in skills/:

  • Form fields: name (auto-derived from filename), description, version (auto-incremented), requires_mcp (multi-select from configured MCP servers), inputs (tag-style list), outputs (tag-style list)
  • Body: skill instruction text (the reusable prompt fragment)
  • Validation: name and description required
  • Shows a "used by" indicator listing which agents reference this skill

Creating in knowledge/:

  • Form fields: title, tags (tag input with autocomplete from existing tags), source (optional), related (file picker for linking to other vault files)
  • Body: free-form markdown
  • Minimal validation — knowledge notes are intentionally flexible

Editor Features

Frontmatter form ↔ YAML toggle: Users can switch between the structured form view and raw YAML editing. Changes in one are reflected in the other. The form is the default for domain-aware mode; raw YAML is the default for unknown file types.

Markdown body editor:

  • Syntax-highlighted markdown editing (use CodeMirror or Monaco)
  • Live preview panel (split view or toggle)
  • Basic toolbar: headings, bold, italic, code, links, lists
  • [[wiki-link]] autocomplete — type [[ and get suggestions from existing vault files
  • File link insertion — browse the vault tree and insert relative links
  • Image support (for knowledge notes) — paste or upload, stored in knowledge/_assets/

File tree browser:

  • Sidebar showing the full vault directory tree
  • Create new file/directory at any location via right-click or button
  • Drag and drop files between directories (= status changes for tasks)
  • Rename and delete files
  • Visual indicators: file type icons, task priority colors, agent status dots

Template system:

Files in crons/templates/ serve as templates. When creating a new cron, the user can optionally start from a template. Extend this to other areas:

vault/
├── .vault/
│   └── templates/
│       ├── agent-default.md        # default agent template
│       ├── task-bug.md             # bug report task template
│       ├── task-review.md          # review request template
│       ├── knowledge-meeting.md    # meeting notes template
│       └── knowledge-research.md   # research note template

Templates are regular vault files. The editor loads them as starting points and the user fills in the specifics.

Quick create shortcuts:

The dashboard header or a command palette (Ctrl+K / Cmd+K) provides quick actions:

  • "New task" → opens editor in todos/harald/open/ with task form
  • "New agent task" → opens editor in todos/agent/queued/ with agent task form
  • "New note" → opens editor in knowledge/ with minimal form
  • "New agent" → opens editor in agents/ with agent form
  • "New skill" → opens editor in skills/ with skill form
  • "New cron" → opens editor in crons/active/ with cron form

Inline AI Assistant

The editor includes a built-in AI assistant for interactive, conversational help while editing any document. This is a direct, synchronous interaction — you ask, it responds, you iterate — without going through the agent task queue. Think of it as a pair-editing session.

UI: Chat sidebar

A collapsible chat panel on the right side of the editor. When open, the editor and chat share the screen. The AI can see:

  • The current file content (frontmatter + body)
  • The file's path and entity type (so it understands vault context)
  • Optionally, related files if referenced in related: frontmatter or [[wiki-links]]

Interaction model:

  • User types a message: "help me structure this agent prompt better", "add a section about error handling", "rewrite the classification rules to be more specific", "what's missing from this spec?"
  • The AI responds with suggestions, explanations, or proposed edits
  • Proposed edits are shown as diffs in the chat — the user can apply them with one click, which patches the editor content directly
  • The conversation is contextual to the current file — switching files starts a fresh context (but the user can explicitly say "keep the conversation")

Capabilities:

  • Drafting: "Write me a knowledge note about X" — AI generates content, user reviews and edits
  • Refining: "Make this more concise" / "Expand the routing rules section" — AI proposes changes as diffs
  • Structuring: "This agent prompt is getting messy, help me reorganize it" — AI suggests a restructured version
  • Domain-aware help: The AI understands vault conventions. "Add the standard frontmatter for a knowledge note" — it knows the schema. "This agent should also create tickets" — it suggests adding the vault/create-ticket skill
  • Explaining: "What does this cron expression mean?" / "Which agents use this skill?" — contextual answers
  • Reviewing: "Review this agent definition for issues" — checks for missing fields, unreferenced skills, potential problems
  • Multi-file awareness: "What other notes do we have about SEV-SNP?" — searches the vault and suggests links

Applying edits:

When the AI suggests changes, they appear as a diff block in the chat:

--- current
+++ proposed
@@ -3,4 +3,6 @@
 ## Classification
 
-- needs-review: PRs where I am a reviewer
+- needs-review: PRs where I am a requested reviewer or
+  where my team is assigned
+- needs-review-urgent: PRs marked as security-critical
 - my-pr-update: activity on PRs I authored

The user can:

  • Apply — patch the editor content with the proposed changes
  • Apply & continue — apply and keep discussing
  • Edit — modify the suggestion before applying
  • Reject — dismiss and continue conversation

Model selection:

The AI assistant uses the model configuration from .vault/config.yaml. A dropdown in the chat header lets the user switch between configured models on the fly (local model for quick edits, Claude for complex restructuring). The default can be set in config:

# in .vault/config.yaml
assistant:
  default_model: local/qwen3
  models:
    - local/qwen3
    - claude-sonnet-4
    - claude-opus

Conversation is ephemeral by default — it's a working session, not stored. But the user can save a conversation to knowledge/ as a note if it contains useful context (button: "Save conversation as note").

REST API:

POST /api/assistant/chat              # send message, receive response
  Body: {
    "message": "help me restructure this",
    "file_path": "agents/github-triage.md",
    "file_content": "...",              # current editor content
    "context_files": ["skills/vault/create-ticket.md"],  # optional related files
    "model": "local/qwen3",            # optional model override
    "conversation_id": "...",           # for multi-turn
  }
  Response: {
    "message": "Here's a restructured version...",
    "diffs": [                          # optional proposed edits
      {
        "description": "Reorganize classification section",
        "hunks": [...]                  # unified diff hunks
      }
    ],
    "conversation_id": "..."
  }

POST /api/assistant/apply-diff        # apply a proposed diff to a file
  Body: {
    "file_path": "agents/github-triage.md",
    "diff": { ... }
  }

GET  /api/assistant/models            # list available models for the assistant

Implementation:

The backend proxies chat requests to the configured model endpoint (OpenAI-compatible API for local models, Anthropic API for Claude). It prepends a system prompt that includes:

  1. The vault operation context (directory structure, entity schemas)
  2. The current file content and path
  3. Instructions to produce diffs in a parseable format when suggesting edits

This is intentionally simpler than the agent task system — no frontmatter, no queue, no skills composition. It's a direct request-response to an LLM with the current file as context. The complexity lives in the frontend (diff rendering, apply logic, conversation state) not the backend.

REST API for File Operations

# File CRUD
GET    /api/files/*path              # read file content (frontmatter + body)
PUT    /api/files/*path              # create or overwrite file
PATCH  /api/files/*path              # update frontmatter only (preserves body)
DELETE /api/files/*path              # delete file

# Directory operations
GET    /api/tree                     # full vault directory tree
GET    /api/tree/*path               # subtree at path
POST   /api/tree/*path               # create directory
DELETE /api/tree/*path               # delete directory (must be empty)

# File operations
POST   /api/files/*path/move         # move/rename file
        Body: { "destination": "new/path.md" }
POST   /api/files/*path/duplicate    # copy file with new name

# Schema/validation
GET    /api/schema/:entity_type      # get frontmatter schema for entity type
POST   /api/validate                 # validate frontmatter against schema
        Body: { "entity_type": "agent", "frontmatter": { ... } }

# Templates
GET    /api/templates                # list available templates
GET    /api/templates/:name          # get template content

# Autocomplete helpers
GET    /api/suggest/agents           # list agent names (for dropdowns)
GET    /api/suggest/skills           # list skill names (for agent editor)
GET    /api/suggest/tags             # list all tags used across vault
GET    /api/suggest/repos            # list all repos referenced in vault
GET    /api/suggest/labels           # list all labels used in tasks
GET    /api/suggest/files?q=...      # search file paths (for wiki-links)
GET    /api/suggest/models           # list configured models from config
GET    /api/suggest/mcp-servers      # list configured MCP servers

Validation

The API validates files on write:

  • Parse YAML frontmatter — reject if malformed
  • Check required fields for the entity type (derived from file path)
  • Validate field types (datetime strings, cron expressions, enum values)
  • Check references (agent task references existing agent, cron references existing agent)
  • Return detailed validation errors with field-level messages

Validation is advisory when editing in raw mode — the user can override and save anyway (with a warning). In form mode, the save button is disabled until required fields are filled.

Dependencies (Rust Crates)

Crate Purpose
tokio Async runtime, process spawning, timers
axum HTTP server, WebSocket, routing
tower Middleware (CORS, compression, logging)
notify Filesystem watching (inotify on Linux)
serde, serde_yaml, serde_json Serialization
pulldown-cmark Markdown parsing/rendering
cron Cron expression parsing
chrono DateTime handling
tokio-tungstenite or axum built-in WebSocket
tracing, tracing-subscriber Structured logging
clap CLI argument parsing
rust-embed or include_dir Embed static dashboard assets
uuid Task ID generation (for filenames)

Nix

The project should be a Nix flake with:

  • Rust build via crane or naersk
  • Dashboard build (Node.js) as a separate derivation
  • Combined package that embeds dashboard assets into the Rust binary
  • NixOS module with systemd service definition
  • Development shell with all dependencies

Future Considerations

These are not in scope for the initial build but should inform architectural decisions:

  • Multiple vaults — supporting more than one vault directory
  • Remote agents — agents running on different machines (e.g., inference on Bosgame M5, agent logic on agent machine)
  • Git integration — auto-commit vault changes, sync across machines
  • Plugin system — custom executors loaded at runtime
  • Full-text search — tantivy-based search index over the vault
  • Knowledge graph — parse related: frontmatter and [[wiki-links]] in markdown bodies to build a navigable graph
  • Mobile view — responsive dashboard or dedicated mobile app
  • Notifications — push notifications via ntfy, email, or webhooks for urgent tasks
  • Metrics/observability — Prometheus endpoint for agent execution metrics