-
-
Notifications
You must be signed in to change notification settings - Fork 22
Description
Feature and its Use Cases
SmartNotes currently has no defined storage/vault layer. Several PRs (notably #16 for hybrid search and the AI architecture in docs/ai-architecture.md) are building AI modules that need to read notes, enumerate the vault, and react to changes but there is no agreed API for any of this.
without a clear vault interface, every module ends up reaching directly into the filesystem or SQLite in its own way, making the codebase tightly coupled and hard to test.
this issue proposes we define and agree on a clean vault/storage layer API before more
AI and UI modules are built on top of undefined assumptions.
Problem
Currently:
- AI modules (chunking, embedding, indexing) have no standard way to enumerate notes
- there is no event system for note create / update / delete - so incremental indexing cannot be triggered correctly
- modules reach into raw filesystem paths or storage internals directly
- there is no metadata API (note ID, title, path, last-modified, tags, etc.)
this is explicitly called out as a concern in docs/ai-architecture.md:
"AI modules should consume these APIs, not reach directly into SQLite schemas or
filesystem paths."
"RAG/indexing workflows should use storage events to trigger incremental updates."
Proposed Vault API (Conceptual)
these are starting-point contracts, not a final implementation. The goal of this issue is to discuss and agree on the shape before anyone starts coding.
Note metadata type
interface NoteMetadata {
id: string; // stable unique identifier (not path-dependent)
title: string;
path: string; // relative path within the vault
lastModified: number; // unix timestamp
tags?: string[];
}Core read API
interface VaultReader {
listNotes(): Promise<NoteMetadata[]>;
readNote(noteId: string): Promise<string>;
getNoteMetadata(noteId: string): Promise<NoteMetadata>;
}Write API
interface VaultWriter {
createNote(title: string, content: string): Promise<NoteMetadata>;
updateNote(noteId: string, content: string): Promise<void>;
deleteNote(noteId: string): Promise<void>;
renameNote(noteId: string, newTitle: string): Promise<void>;
}Event / change stream API
type VaultEventType = 'created' | 'updated' | 'deleted' | 'renamed';
interface VaultEvent {
type: VaultEventType;
noteId: string;
metadata?: NoteMetadata;
}
interface VaultWatcher {
on(event: 'change', listener: (event: VaultEvent) => void): void;
off(event: 'change', listener: (event: VaultEvent) => void): void;
}
// Combined
interface Vault extends VaultReader, VaultWriter, VaultWatcher {}Additional Context
Why This Matters
- AI modules only need
VaultReader+VaultWatcher- they subscribe to change events and trigger incremental re-indexing - UI components use
VaultWriterfor creating/editing notes - Tests can inject a mock
Vault, keeping AI logic fully unit-testable without touching the real filesystem - Storage can evolve (SQLite today, different backend later) without breaking AI or UI code
Open Questions for Discussion
- should
noteIdbe a content hash, a UUID, or a path-derived stable key? - should the watcher use
chokidar/fs.watch, or an in-process event emitter triggered by the write API? - where does metadata (title, tags) live - in markdown frontmatter, or a separate sidecar (SQLite/JSON)?
- should
VaultReadermethods be synchronous or always async?
Related
- [FEATURE]: Document offline‑first AI architecture and modular retrieval design for SmartNotes #17 - AI architecture feature request
- PR Feat/hybrid search #16 - hybrid search (needs vault events for incremental indexing)
- docs/ai-architecture.md - vault interaction guidelines (lines 195–210)
Code of Conduct
- I have joined the Discord server and will post updates there
- I have searched existing issues to avoid duplicates