core

package
v0.5.0 Latest Latest
Warning

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

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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

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.

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 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 ID.

func (*NoteService) Get

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

Get fetches a note and decrypts its 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) 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