Documentation
¶
Index ¶
- Variables
- func Decrypt(key, data, noteID []byte) ([]byte, error)
- func Encrypt(key, plaintext, noteID []byte) ([]byte, error)
- func MaskSensitive(notes []*model.Note) []*model.Note
- func ResolveBackends(cfg *config.Config) (*config.Config, error)
- func ShortID(id string) string
- type AddOptions
- type AmbiguousIDError
- type ClassifyOptions
- type Container
- type KeyManager
- type NoteService
- func (s *NoteService) Add(ctx context.Context, content string, opts AddOptions) (*model.Note, error)
- func (s *NoteService) Delete(ctx context.Context, id string) error
- func (s *NoteService) Get(ctx context.Context, ref string) (*model.Note, error)
- func (s *NoteService) List(ctx context.Context, filters db.Filters, limit, offset int) ([]*model.Note, error)
- func (s *NoteService) Resolve(ctx context.Context, ref string) (*model.Note, error)
- func (s *NoteService) ScrubSensitiveSummaries(ctx context.Context) (int, error)
- type SearchOptions
- type SearchResult
- type SearchService
- type TaggerService
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidID = errors.New("invalid note ID: use the hex ID shown by inode list")
ErrInvalidID is returned for an empty ID or one with characters that never appear in a note ID (IDs are UUIDs: hex digits and dashes).
Functions ¶
func Decrypt ¶
Decrypt decrypts data produced by Encrypt. Returns an error if the auth tag fails — data has been tampered with.
func Encrypt ¶
Encrypt encrypts plaintext using AES-256-GCM. noteID is used as additional authenticated data — binds ciphertext to the note. Returns: iv[12] + sealed[N] (sealed includes ciphertext + 16-byte GCM auth tag).
func MaskSensitive ¶
MaskSensitive replaces sensitive values in a string with asterisks. Used by the CLI to mask content before display when --reveal is not set.
func ResolveBackends ¶ added in v0.5.1
ResolveBackends returns a copy of cfg with model, base URL and embedding dimension filled in from the selected backends' defaults wherever they are not set. Values set in the config file or environment always win. When only the dimension cannot be resolved, the error comes with the config resolved as far as possible, for `inode config show`.
Before #31 these defaults were global and Ollama-specific, so switching to openai, claude-api or voyage kept llama3.2, localhost:11434 and 768.
Types ¶
type AddOptions ¶
type AddOptions struct {
ClassifyOptions
DefaultSensitive bool // from config — used when IsSensitive is not explicitly set
}
AddOptions carries user-supplied options for adding a note.
type AmbiguousIDError ¶ added in v0.5.1
AmbiguousIDError is returned when an ID prefix matches more than one note. Matches holds up to maxMatches of them; More is true when there are others.
func (*AmbiguousIDError) Error ¶ added in v0.5.1
func (e *AmbiguousIDError) Error() string
type ClassifyOptions ¶
type ClassifyOptions struct {
Category string // explicit category; empty = auto-detect
Tags []string // explicit tags; nil = auto-detect
IsSensitive *bool // explicit sensitivity; nil = auto-detect
}
ClassifyOptions carries optional user-supplied overrides.
type Container ¶
type Container struct {
Notes *NoteService
Search *SearchService
DB db.Adapter
}
Container holds all initialised services and adapters.
type KeyManager ¶
type KeyManager struct {
// contains filtered or unexported fields
}
KeyManager handles master secret storage and encryption key derivation. The derived key is never stored — it is rederived per process.
func NewKeyManager ¶
func NewKeyManager(configDir string) (*KeyManager, error)
NewKeyManager initialises the key manager, generating a master secret and salt on first run if they don't exist.
func (*KeyManager) DeriveKey ¶
func (km *KeyManager) DeriveKey() ([]byte, error)
DeriveKey derives a 32-byte AES key from the master secret and salt.
type NoteService ¶
type NoteService struct {
// contains filtered or unexported fields
}
NoteService orchestrates saving, fetching, and deleting notes. It coordinates the DB adapter, embedding adapter, tagger, and encryption.
func NewNoteService ¶
func NewNoteService(dbAdapter db.Adapter, embAdapter embedding.Adapter, tagger *TaggerService, keyMgr *KeyManager) *NoteService
NewNoteService creates a NoteService with all required dependencies.
func (*NoteService) Add ¶
func (s *NoteService) Add(ctx context.Context, content string, opts AddOptions) (*model.Note, error)
Add classifies, embeds, encrypts (if sensitive), and persists a note.
func (*NoteService) Delete ¶
func (s *NoteService) Delete(ctx context.Context, id string) error
Delete removes a note by its full ID. Callers resolve user input with Resolve first, so a prefix can never delete a note the user did not see.
func (*NoteService) Get ¶
Get resolves ref (see Resolve) and decrypts the note's content if sensitive.
func (*NoteService) List ¶
func (s *NoteService) List(ctx context.Context, filters db.Filters, limit, offset int) ([]*model.Note, error)
List returns notes matching the given filters.
func (*NoteService) Resolve ¶ added in v0.5.1
Resolve finds the one note that ref names: a full ID or a prefix of one. It fails with ErrInvalidID, db.ErrNotFound or *AmbiguousIDError rather than guessing. Content of a sensitive note is returned still encrypted.
func (*NoteService) ScrubSensitiveSummaries ¶
func (s *NoteService) ScrubSensitiveSummaries(ctx context.Context) (int, error)
ScrubSensitiveSummaries validates summaries of sensitive notes saved before summaries were checked (#53) and replaces the ones that leak content. Each note is checked once; the database records it. Returns how many summaries were replaced.
type SearchOptions ¶
type SearchOptions struct {
TopK int
Category string
Tags []string
// IsSensitive optionally restricts results by the sensitive flag.
// nil = no filter (default); pointer to false = exclude sensitive
// notes from results before they reach the LLM. Used by the MCP
// server to prevent sensitive notes leaking to the calling agent.
IsSensitive *bool
// MaxDistance is the L2-distance ceiling for keeping a candidate note.
//
// = 0 → use the service default (typically 1.0)
// > 0 → keep notes with Distance <= MaxDistance
// < 0 → disable filtering entirely (return whatever the DB returned)
//
// The "= 0 means default" overload means an *exact* 0 threshold cannot be
// expressed via this field. In practice this is fine: with L2-normalised
// embeddings, bit-exact distance 0 implies bit-identical content, which is
// not a useful retrieval target.
MaxDistance float32
// OnStep is an optional progress hook invoked at the start of each
// pipeline phase ("embedding", "searching", "answering"). Used by the
// CLI to update a spinner label. Must be cheap and non-blocking.
OnStep func(step string)
}
SearchOptions carries optional filters for a search query.
type SearchResult ¶
SearchResult holds both the LLM-generated answer and the raw notes used as context.
type SearchService ¶
type SearchService struct {
// contains filtered or unexported fields
}
SearchService implements the RAG pipeline for natural language queries. All external concerns are pluggable via adapters — Voyage/Ollama for embeddings, Claude/Ollama for the LLM, SQLite/(future)Postgres for storage.
Flow:
- Embed the user query (embedding adapter)
- Vector similarity search → top-K notes (DB adapter)
- Drop notes beyond the relevance threshold
- Decrypt sensitive notes in memory
- Inject remaining notes as context → answer (LLM adapter)
func NewSearchService ¶
func NewSearchService(dbAdapter db.Adapter, embAdapter embedding.Adapter, llmAdapter llm.Adapter, keyMgr *KeyManager, maxDistance float32, topK int) *SearchService
NewSearchService creates a SearchService with all required dependencies. maxDistance and topK supply service-level defaults; pass 0 to use built-ins.
func (*SearchService) Retrieve ¶
func (s *SearchService) Retrieve(ctx context.Context, query string, opts SearchOptions) ([]*model.Note, error)
Retrieve runs the embed → vector search → threshold filter → decrypt pipeline and returns the candidate notes. No LLM call.
Used by the MCP tool surface: the calling agent (Claude Code, Cursor) is itself an LLM and prefers to reason over raw candidates rather than a pre-generated answer from inode's local model.
func (*SearchService) Search ¶
func (s *SearchService) Search(ctx context.Context, query string, opts SearchOptions) (*SearchResult, error)
Search embeds the query, retrieves top-K notes, decrypts them, and returns an LLM-generated answer alongside the source notes.
type TaggerService ¶
type TaggerService struct {
// contains filtered or unexported fields
}
TaggerService classifies notes into categories and tags. When the user provides values explicitly, they are passed through (with normalisation). Otherwise the LLM adapter is called for auto-detection.
func NewTaggerService ¶
func NewTaggerService(llmAdapter llm.Adapter) *TaggerService
NewTaggerService creates a TaggerService backed by the given LLM adapter.
func (*TaggerService) Classify ¶
func (t *TaggerService) Classify(ctx context.Context, content string, opts ClassifyOptions) (llm.ClassifyResult, error)
Classify returns the final classification for a note. User-supplied values take precedence; the LLM fills in anything missing.