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>
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:
- Filesystem events (inotify) — file created, modified, deleted, moved in the vault
- Cron timer — a single
tokio::time::sleep_untiltargeting the nearest next cron execution; recomputed when cron definitions change - 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/(includingskills/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: draftfrontmatter field, and a review ticket is created intodos/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:
- Reading tool requests from other agents
- Understanding the desired interface and use case
- Writing new skill definitions (or modifying existing ones)
- Creating test tasks to validate the new skill works
- 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:
- Agent encounters a gap — e.g.,
github-triagecan't parse Cargo.toml diffs - Agent creates a tool request — using
vault/request-toolskill, writes a task totodos/agent/queued/targeting thebuilderagent - Builder picks it up — reads the request, searches for existing skills, designs the solution
- Builder creates the skill — writes
skills/parse-cargo-toml.mdwithstatus: draft - Builder creates a test — queues a test task using the new skill
- Builder creates a review ticket —
todos/harald/open/review-new-skill-parse-cargo-toml.mdexplaining what was built - User reviews — reads the skill, checks the test results, approves or requests changes
- User activates — removes
status: draftfrom the skill, adds it to the relevant agent'sskillslist - Agent now has the capability — next time
github-triageruns, 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:
- Task created in
queued/(by cron, another agent, dashboard, or manually) - Daemon detects new file via inotify
- Daemon moves file to
running/, setsstartedtimestamp - Daemon spawns the referenced agent with the task's markdown body as additional context
- On success: daemon moves to
done/, setscompletedandoutput - On failure: increment
retry; if <max_retriesmove back toqueued/, else move tofailed/witherrorset
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_yamlfor frontmatter,pulldown-cmarkfor 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
notifycrate) - 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
croncrate) - Maintain a priority queue of upcoming cron firings
- Compute the next wake time (
Instant) fortokio::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:
- Timer fires for next scheduled cron
- Read the cron definition file, resolve the referenced agent
- Create an agent task file in
todos/agent/queued/with the cron's markdown body as instructions - The agent task pickup mechanism handles the rest
- Update cron frontmatter:
last_run,last_status,next_run,run_count - Recompute next wake time
Agent task execution flow:
- Detect new file in
todos/agent/queued/(via inotify) - Check concurrency limits; if at max, leave in queue (will be picked up when a slot opens)
- Move file to
running/, updatestartedtimestamp - Resolve the agent definition from
agents/{task.agent}.md - Build the execution context:
- System prompt = agent markdown body
- Task context = task markdown body
- Input data = task
inputfrontmatter field - Environment = agent
envfield with variable expansion
- Spawn the executable as a child process via
tokio::process::Command - Set up timeout via
tokio::time::timeout - Capture stdout/stderr
- On success: move to
done/, populateoutputandcompleted - On failure: increment
retry, decide re-queue or move tofailed/
Process spawning:
The exact spawning mechanism depends on the executable:
claude-code: invoke via CLI with--system-promptflag (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 =
mvorrename()— 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:
- Before writing, add the file path to a
pending_writes: HashSet<PathBuf> - Write the file
- When inotify fires, check
pending_writes— if present, suppress the event and remove from set - Use a short timeout to clean stale entries from
pending_writes
Agent Output Handling
Agents produce output in two ways:
- 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.
- Structured output — returned via stdout. The daemon captures this and stores it in the task's
outputfrontmatter 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/topaused/→ 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
errorfield - 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:
- Scan
crons/active/— rebuild schedule - Scan
todos/agent/running/— these were in-flight when the daemon stopped. Move back toqueued/for re-execution (they may have been partially completed; agents should be designed to be idempotent) - Scan
agents/— load all agent definitions - Start inotify watches
- 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:
- Pages in
views/pages/— only the user (via editor or inline assistant) can modify these. Agents can read them but not write. - 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). - Notifications in
views/notifications/— agents can create these. They're ephemeral and auto-expire. - Widgets in
views/widgets/— agents can create new widget definitions. Modifying existing built-in widgets requires user confirmation (the agent creates a proposal intodos/harald/open/instead). - 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
- 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. - Knowledge — browse and search the knowledge base, render markdown, tag filtering, graph view (optional/future)
- Agents — agent cards with stats, prompt editor (edit agent markdown inline, saves to filesystem), execution history, agent timeline (Gantt view of today's runs)
- Crons — schedule overview, run history, enable/disable toggle, manual trigger button
- 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/andpaused/
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:
- 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. - 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:
nameandexecutablerequired,timeoutmust 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:
schedulemust be valid cron expression,agentmust 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:
titlerequired
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:
titleandagentrequired
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:
nameanddescriptionrequired - 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-ticketskill - 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:
- The vault operation context (directory structure, entity schemas)
- The current file content and path
- 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
craneornaersk - 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