core

package
v0.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
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

func Decrypt(key, data, noteID []byte) ([]byte, error)

Decrypt decrypts data produced by Encrypt. Returns an error if the auth tag fails — data has been tampered with.

func Encrypt

func Encrypt(key, plaintext, noteID []byte) ([]byte, error)

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

func MaskSensitive(notes []*model.Note) []*model.Note

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

func ResolveBackends(cfg *config.Config) (*config.Config, error)

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.

func ShortID added in v0.5.1

func ShortID(id string) string

ShortID is the 8-character form of a note ID shown by list and search.

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

type AmbiguousIDError struct {
	Prefix  string
	Matches []*model.Note
	More    bool
}

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.

func NewContainer

func NewContainer(cfg *config.Config) (*Container, error)

NewContainer initialises every dependency from the loaded config.

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

func (s *NoteService) Get(ctx context.Context, ref string) (*model.Note, error)

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

func (s *NoteService) Resolve(ctx context.Context, ref string) (*model.Note, error)

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

type SearchResult struct {
	Answer string
	Notes  []*model.Note
}

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:

  1. Embed the user query (embedding adapter)
  2. Vector similarity search → top-K notes (DB adapter)
  3. Drop notes beyond the relevance threshold
  4. Decrypt sensitive notes in memory
  5. 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL