Lore

module
v0.0.0-...-2c7f779 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: Apache-2.0

README ΒΆ

Lore

A decision-provenance engine for engineering teams. Ask why a decision was made β€” and what happened afterwards β€” and get back evidence where every claim carries a source URL.

🚧 Work in progress

Lore is under active development and has not been released. The retrieval core, provenance engine, GitHub/GitLab/Notion/Jira connectors, code anchoring, background sync, both MCP transports, the lore.v1 gRPC API and LLM synthesis are implemented and covered by tests. See Status for the exact line.


The problem

Teams lose their why. Decisions happen in incident channels, Jira tickets, Notion pages and PR reviews; then people rotate and six months later nobody can answer "why did we choose B over A when incident X happened?" The answer exists β€” scattered across a trail nobody reads manually. A commit that says only PROJ-4521 carries no reasoning: the reasoning is in the ticket, and the consequences are in a postmortem written weeks later.

Codebase RAG tools answer what the code is. Lore answers why it was decided and what happened next.

How it works

  1. Ingest β€” connectors stream documents from GitHub, GitLab, Notion and Jira into one normalized Document model. Every source is optional.
  2. Link β€” a resolver turns raw references (ticket keys, URLs, commit SHAs, file paths) into a typed, directional edge graph: ticket β†’ design doc β†’ PR β†’ review thread β†’ commit.
  3. Answer β€” one pipeline, four seed modes: resolve anchor β†’ seed β†’ graph walk β†’ hybrid retrieval β†’ rank β†’ EvidenceBundle. Retrieval is BM25 (FTS5) + vector KNN (sqlite-vec) fused with Reciprocal Rank Fusion.

Two design choices set it apart:

  • Code is one anchor among several, not the center. A Jira + Notion workspace with no repository at all is a first-class configuration; git blame anchoring is an optional enrichment.
  • Honesty is a feature. Every answer reports its gaps β€” "Rate limit the export endpoint (jira🎫PROJ-4521) stands alone; no linked discussion" β€” instead of fabricating a chain. No URL means no evidence, so the node is not returned.

Two personas, one engine

Ask-only workspace (no repos) Code-anchored workspace
Sources: Jira + Notion (Β± GitHub/GitLab issues and PRs) Same, plus local clones registered
"Why B over A during incident X?" β†’ find_decision "Why does auth.go:40-55 exist?" β†’ why
"What impact did decision A have?" β†’ impact_of "How did this file evolve?" β†’ history_of
Anchors: query, document, time window Additional anchor: code span (blame)

Nothing in the first column is degraded. find_decision, trace and impact_of run the full walk β€” chains, gaps, time anchoring β€” on a workspace with zero repositories; the second column is the first plus one extra anchor type. Walk through either one: ask-only demo Β· code-anchored demo.

Quickstart

Requirements: Go 1.27+, git, and an OpenAI API key for embeddings β€” or a local Ollama daemon instead, with embedder.provider: ollama. No cgo, no Docker, no database server β€” SQLite ships inside the binary as pure-Go WASM (ncruces/go-sqlite3 + sqlite-vec), so the index is a single portable file and queries work offline after a sync.

go install github.com/setthasit/Lore/cmd/lore@latest   # or: git clone … && make bin

export OPENAI_API_KEY=...          # embeddings
export LORE_GITHUB_TOKEN=...       # fine-grained, read-only PAT

lore init                          # writes a commented lore.yaml scaffold
lore source add jira               # optional: grow the workspace interactively
lore sync                          # first run creates ~/.lore/<workspace>.db
lore status                        # index counts, cursor ages, sync lock
lore ask "why did we pick sqlite?" # prose answer; needs an llm: block

lore init writes credential variable names, never credentials, and scaffolds the GitHub source; lore source add notion|jira|gitlab appends the rest interactively. lore --version prints the build stamp plus the embedder identity of the workspace.

What an answer looks like

lore ask answers in prose, citing the documents it used β€” it needs the llm: block in lore.yaml:

SQLite carries the index because it ships everywhere and needs no server [1].
Postgres with pgvector was the alternative the storage design weighed [2].

**Sources**

1. Index on SQLite, not Postgres β€” https://github.com/acme/lore/pull/12
2. Storage design β€” https://notion.so/design/storage

why, trace, impact and history print the evidence itself as a timeline (shape shown; the values are invented):

provenance of Storage design

anchor: Storage design
        https://notion.so/design/storage

2 documents

2025-03-10 Storage design
   notion page Β· 2025-03-10
   https://notion.so/design/storage
      postgres with pgvector was the alternative

2025-03-12 Index on SQLite, not Postgres
   github pr Β· dev@example.test Β· 2025-03-12 Β· follow_up
   https://github.com/acme/lore/pull/12
      sqlite ships everywhere and needs no server

chains:
  notion:page:design/storage β†’ github:pr:acme/lore/pull/12

gaps:
  no follow-up evidence after 2025-03-12

Pass --explain to any of those four to get prose instead of the timeline, and --raw to any query command to get the EvidenceBundle as JSON for scripting. --raw wins when both are given.

What to expect

Lore is only as good as the trail your team leaves. Where commits name their tickets and PRs describe their reasoning, chains run four and five hops deep across sources. Where they do not, the honest outcome is a short chain plus a gaps line β€” retrieval still surfaces the unlinked discussion, but nothing invents the missing edge. A first sync of a large workspace is the slow part (it embeds every chunk); after that, syncs are incremental and queries are local.

Running the services

Lore is a single binary; the subcommand picks the transport.

lore mcp                              # MCP over stdio, for a local agent harness
lore serve --http 127.0.0.1:8080      # MCP streamable HTTP at /mcp + lore.v1 gRPC + background sync

serve also runs the sync scheduler, so the index stays fresh while the endpoints are up. gRPC listens on 127.0.0.1:9090 unless --grpc or server.grpc_addr says otherwise, and --mtls makes it require a client certificate signed by server.mtls.client_ca.

A bind address that is not provably loopback is refused unless server.mtls.cert and server.mtls.key are configured β€” :8080 and localhost:8080 do not qualify, because a bare port reaches every interface and a host name is not proof.

Register the stdio server with an MCP host (Claude Code, Cursor, …):

{
  "mcpServers": {
    "lore": {
      "command": "/absolute/path/to/lore",
      "args": ["mcp", "--config", "/absolute/path/to/lore.yaml"]
    }
  }
}

Per-client paths, the streamable-HTTP variant and the troubleshooting table live in the MCP quickstart.

MCP tools return the structured EvidenceBundle, never prose: the host model is already an LLM, so it synthesizes in its own context and can immediately call another tool. That is why no LLM key is needed for MCP usage β€” only an embedding key.

MCP tool Answers
find_decision "why B over A, around incident X?" β€” retrieval-seeded, works with zero repos
why "why does auth.go:40-55 look like this?" β€” blame-seeded
trace everything linked to one commit / PR / ticket / page, in order
impact_of what followed a decision, as a chronological timeline
history_of how one file evolved, commit by commit
sync_now / sync_status trigger a sync round; report cursors, counts and the lock

CLI surface

Command Purpose
lore init Β· lore source add <notion|jira|gitlab> scaffold and grow lore.yaml
lore sync [--source <name>] [--reembed] one sync round; checkpoints per batch, so an interrupted run resumes
lore status index counts, per-source cursor ages, sync lock state
lore ask <question> synthesized prose; --around --source --repo --doc-type --since --until --raw
lore why <file>:<L1>-<L2> blame-anchored trail; --repo --explain --raw
lore trace <ref> one document's neighborhood; --direction in|out|both --explain --raw
lore impact <ref | "query"> consequences timeline; --question --explain --raw
lore history <path> file timeline; --limit --before pagination; --explain --raw
lore mcp Β· lore serve MCP stdio Β· MCP streamable HTTP + lore.v1 gRPC + scheduler

Every command takes --config (default ./lore.yaml).

Guides

Guide Contents
MCP quickstart Claude Code / Cursor config for stdio and streamable HTTP, tool routing, troubleshooting
Source setup least-privilege credentials for GitHub, GitLab, Notion and Jira
Fully local Ollama for embeddings and synthesis; what leaves the machine in each mode
Ask-only demo seeded Jira + Notion workspace, zero repositories, two flagship questions
Code-anchored demo why on a real OSS repository: commit β†’ PR β†’ issue

Architecture

Strict unidirectional layering, wired with Uber FX. Transports never touch the store or a connector β€” including the MCP path.

flowchart TB
    T["Transport β€” MCP stdio Β· MCP HTTP Β· gRPC Β· CLI"]
    S["Service β€” Query Β· Why Β· Trace Β· Impact Β· History Β· Synthesis Β· SyncOrchestrator Β· LinkResolver"]
    R["Repository β€” IndexStore (SQLite: FTS5 + sqlite-vec)"]
    C["Connectors β€” GitHub Β· GitLab Β· Notion Β· Jira Β· local git Β· embedder Β· LLM"]
    T --> S
    S --> R
    S --> C

One SQLite file per workspace holds documents, chunks, chunks_fts (BM25), chunk_vectors (sqlite-vec), edges, pending_refs, cursors, sync_lock and meta. The index is derived data: sources are ground truth, so deleting it is safe and the next sync rebuilds it.

Sync is crash-safe by construction: connectors yield Batch{Docs, Cursor}, and the orchestrator commits the batch then persists that batch's cursor. A single-row lease with a heartbeat and TTL takeover keeps a manual lore sync, the background scheduler and a lore serve daemon from colliding β€” and a crashed run never wedges the scheduler.

The design documents in docs/v3/ are the source of truth:

Doc Contents
01 β€” Overview problem, concept, differentiators, goals / non-goals
02 β€” Architecture layers, transports, request flows, key decisions
03 β€” Data Model Document / Edge model, schema, chunking, hybrid retrieval
04 β€” Connectors & Sync connector contract, scheduler, lease, link resolver
05 β€” Query Engine pipeline, anchors, tool algorithms, EvidenceBundle
06 β€” Interfaces & Config MCP tools, CLI, gRPC API, lore.yaml reference
07 β€” Roadmap & Risks milestones, named risks, open questions

Status

Area State
Config loading, validation, FX wiring βœ… implemented
SQLite IndexStore β€” FTS5 + sqlite-vec, RRF fusion in Go βœ… implemented
GitHub, GitLab, Notion, Jira connectors + shared conformance suite βœ… implemented
Link resolver, edge graph, pending_refs retry βœ… implemented
find_decision, trace, impact_of + event resolution βœ… implemented
Code anchoring β€” why, history_of via local-clone blame/log βœ… implemented
Sync lease, background scheduler, sync_now / sync_status βœ… implemented
MCP stdio + MCP streamable HTTP (lore serve) βœ… implemented
Embedder providers βœ… OpenAI and Ollama β€” Ollama also needs embedder.dimensions, the model's native width (ollama show <model> reports it); an unimplemented provider is refused at startup
gRPC API (lore.v1) + mTLS βœ… implemented
LLM synthesis β€” lore ask, --explain, gRPC synthesize βœ… implemented; needs the llm: block in lore.yaml
Ollama fully-local pipeline βœ… implemented β€” set embedder.provider: ollama (with dimensions) and llm.provider: ollama; both default to http://127.0.0.1:11434 and take no API key
Release binaries βœ… make build.matrix cross-compiles linux/darwin/windows Γ— amd64/arm64 with CGO_ENABLED=0

The CLI synthesizes for lore ask and --explain; gRPC synthesizes unless a request sets synthesize: false. MCP always returns the evidence bundle itself β€” the host model is already an LLM. A workspace with no llm: block says so instead of guessing.

Development

make build         # go build ./...
make bin           # stamped, static binary at bin/lore
make build.matrix  # cross-compile every released platform with CGO_ENABLED=0
make test          # go test ./...
make lint          # golangci-lint run    β€” errcheck, govet, gosec, staticcheck
make gen.mock      # go generate ./...    β€” gomock doubles under internal/mocks

Tests need no external service: connectors run against httptest fixture servers, the store against a temp SQLite file, and the end-to-end suite drives the real MCP transports over a live DI graph β€” an MCP client session over streamable HTTP on a real socket, against fixture GitHub/Notion/Jira servers. It includes an ask-only workspace with zero repositories, which asserts that code-anchored tools refuse with a precondition error rather than degrading.

Contributions follow the branch β†’ PR workflow in AGENTS.md: no direct commits to main, and make build/test/lint green before a PR is opened.

Security posture

  • Read-only toward every source. Lore never writes to GitHub, GitLab, Notion or Jira.
  • Secrets live in environment variables named by config. They are never written to lore.yaml, the index, or logs; least-privilege tokens are the documented default.
  • Off-loopback serving requires TLS, enforced at startup, with mTLS support.
  • Private data leaves the machine only toward the configured embedder and, once llm: is set, the configured LLM. With provider: ollama on both, nothing leaves at all.

License

Apache License 2.0 β€” see LICENSE. Contributions are accepted under the same terms.

Directories ΒΆ

Path Synopsis
api
cmd
lore command
internal
config
Package config loads and validates lore.yaml workspace configuration.
Package config loads and validates lore.yaml workspace configuration.
connectors/conformance
Package conformance is the shared contract suite every connector passes.
Package conformance is the shared contract suite every connector passes.
connectors/embedder
Package embedder defines the embedding provider interface used for vector retrieval.
Package embedder defines the embedding provider interface used for vector retrieval.
connectors/embedder/ollama
Package ollama embeds text with a local Ollama daemon's embeddings API over net/http.
Package ollama embeds text with a local Ollama daemon's embeddings API over net/http.
connectors/embedder/openai
Package openai embeds text with the OpenAI embeddings API over net/http.
Package openai embeds text with the OpenAI embeddings API over net/http.
connectors/github
Package github ingests commits, pull requests, reviews, issues and their comments from GitHub.
Package github ingests commits, pull requests, reviews, issues and their comments from GitHub.
connectors/gitlab
Package gitlab ingests commits, merge requests, discussion threads, issues and their notes from a GitLab instance over the REST v4 API.
Package gitlab ingests commits, merge requests, discussion threads, issues and their notes from a GitLab instance over the REST v4 API.
connectors/gitrepo
Package gitrepo answers blame and history questions about one local clone by shelling out to git.
Package gitrepo answers blame and history questions about one local clone by shelling out to git.
connectors/httpretry
Package httpretry posts JSON to an HTTP API and retries the failures a server reports as temporary: 429, 5xx, a transport error, a truncated body.
Package httpretry posts JSON to an HTTP API and retries the failures a server reports as temporary: 429, 5xx, a transport error, a truncated body.
connectors/httpretry/httpretrytest
Package httpretrytest hosts the httptest scaffolding shared by the connector tests that drive httpretry.Client.
Package httpretrytest hosts the httptest scaffolding shared by the connector tests that drive httpretry.Client.
connectors/jira
Package jira ingests Jira Cloud issues and their comments.
Package jira ingests Jira Cloud issues and their comments.
connectors/llm
Package llm defines the text completion provider interface used for synthesis.
Package llm defines the text completion provider interface used for synthesis.
connectors/llm/anthropic
Package anthropic completes text with the Anthropic messages API over net/http.
Package anthropic completes text with the Anthropic messages API over net/http.
connectors/llm/ollama
Package ollama completes text with a local Ollama daemon's chat API over net/http.
Package ollama completes text with a local Ollama daemon's chat API over net/http.
connectors/llm/openai
Package openai completes text with the OpenAI chat completions API over net/http.
Package openai completes text with the OpenAI chat completions API over net/http.
connectors/llm/zai
Package zai completes text with Z.AI's GLM chat API, which serves the same protocol as OpenAI chat completions.
Package zai completes text with Z.AI's GLM chat API, which serves the same protocol as OpenAI chat completions.
connectors/notion
Package notion ingests workspace pages and their block content from Notion.
Package notion ingests workspace pages and their block content from Notion.
connectors/refscan
Package refscan collects the source-agnostic references a connector finds in document text.
Package refscan collects the source-agnostic references a connector finds in document text.
di
entities
Package entities defines the domain types shared across every layer.
Package entities defines the domain types shared across every layer.
errors/internalerror
Package internalerror defines the typed error taxonomy translated at the transport boundary.
Package internalerror defines the typed error taxonomy translated at the transport boundary.
mocks/embedder
Package mock_embedder is a generated GoMock package.
Package mock_embedder is a generated GoMock package.
mocks/entities
Package mock_entities is a generated GoMock package.
Package mock_entities is a generated GoMock package.
mocks/gitrepo
Package mock_gitrepo is a generated GoMock package.
Package mock_gitrepo is a generated GoMock package.
mocks/llm
Package mock_llm is a generated GoMock package.
Package mock_llm is a generated GoMock package.
mocks/repositories
Package mock_repositories is a generated GoMock package.
Package mock_repositories is a generated GoMock package.
mocks/services
Package mock_services is a generated GoMock package.
Package mock_services is a generated GoMock package.
repositories
Package repositories holds the persistence layer backed by SQLite.
Package repositories holds the persistence layer backed by SQLite.
repositories/sqlite
Package sqlite implements the workspace index on a single SQLite file.
Package sqlite implements the workspace index on a single SQLite file.
services
Package services holds the use-case layer orchestrating repositories and connectors.
Package services holds the use-case layer orchestrating repositories and connectors.
transport
Package transport holds the service set and the error classification every Lore transport shares; each transport renders its own protocol error from them.
Package transport holds the service set and the error classification every Lore transport shares; each transport renders its own protocol error from them.
transport/mcp
Package mcp serves the Lore tool surface over stdio and streamable HTTP.
Package mcp serves the Lore tool surface over stdio and streamable HTTP.

Jump to

Keyboard shortcuts

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