Skip to content
mughalmuneeb786Public

About

Lochive turns your files into a private knowledge base you can ask questions of — in plain language, with every answer citing its source. It runs entirely on your own machine or server: local embeddings, a local LLM, no account and no API key required, so your documents never touch a third party. Open source, MIT licensed.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Lochive — AI RAG for your Local Archive

A local archive you can talk to.

Point Lochive at your files — a folder, a drag-and-drop, a link — and ask questions about them. Answers cite the exact passage they came from, so you can check the source instead of trusting the model.

It runs entirely on your own machine or your own server. No account, no upload to anyone else's cloud, and by default no API key: the embedding model and the language model both run locally. Your documents stay where you put them.

Open source, MIT licensed.

Lochive


What you'd use it for

Ask questions about your data without handing it to an AI company

This is the reason Lochive exists. You have documents you'd never paste into ChatGPT — a contract, a medical report, an employee file, your own private notes — but you still want to ask questions about them and get straight answers.

Lochive does that with nothing leaving your machine. Both models run locally: the embedding model (which reads your files) and the language model (which answers) are downloaded once and run on your own hardware. There's no account, no API key required, and no upload to anyone's cloud.

1. Drop your files into a folder — or drag them into the app.
2. Ask: "What's the notice period in the Acme contract?"
3. Get the answer, with the exact clause it came from quoted underneath.

The citation is the point: you check the source passage instead of trusting the model. And because it's self-hosted, the data physically never travels to a third party — verify it yourself by pulling the plug on your internet and watching it keep working.

A searchable archive of your own stuff

Manuals, receipts, warranties, tax documents, meeting notes, old contracts — the things you keep "somewhere" and can never find. Point Lochive at the folder and ask in plain language: "Which invoice was the standing desk on?", "What did we agree about the deposit?". It reads files in place and never modifies your folder.

A company's internal knowledge base, on your own server

Run it on a server your employees can reach and let them ask the handbook, the policies, the runbooks — instead of digging through a wiki or pinging whoever wrote it. Each person signs in; nothing is sent to an outside service unless you deliberately configure a hosted model.

Reading across a pile of PDFs

Research papers, reports, a whole document set you need to reason across. Ask a question and get answers stitched from the relevant passages, each cited so you can jump to the original.

Local by default, hosted only when you decide

Keep a local model as the default so sensitive material is answered on-machine, and switch a specific chat to a stronger hosted model (GPT, Claude, Kimi…) when the content isn't sensitive. The local-only guardrail lets you mark individual documents that must never be sent to a non-local model — the server enforces it, refusing the question rather than quietly leaking the text.

The honest version: sending a document to a hosted provider means it leaves your machine. Lochive makes the fully-local path the default and the easy one, and gives you per-document controls when you mix in a hosted model — but the only way to guarantee data never leaves is to keep everything local, which is exactly what it does out of the box.


Two ways to run it

Lochive is the same program in both cases; one setting decides which.

Personal — your own archive, no login

For your own laptop or home server. There are no accounts and no login screen: you open it and it's your archive.

SINGLE_USER_MODE=true

Everything belongs to one implicit local owner. Your watched folder is inbox/owner@lochive.local/.

Personal mode has no authentication at all. That's the point on a machine only you use — and completely wrong for anything other people can reach. Keep it bound to 127.0.0.1. The server logs a warning at startup to make this hard to forget.

Team — your company's archive on your server

For a company running Lochive internally so employees can search shared knowledge. Accounts are required; each employee signs in.

SINGLE_USER_MODE=false

Today every employee gets a private archive. Shared team workspaces are designed but not yet built — see docs/ROADMAP.md.


Quick start

Requires Docker. For the default local model you also need Ollama with a model pulled:

ollama pull gemma4          # or any model you prefer
cp .env.example .env
sed -i '' "s|^JWT_SECRET_KEY=.*|JWT_SECRET_KEY=$(openssl rand -hex 32)|" .env

# personal archive, no login:
echo "SINGLE_USER_MODE=true" >> .env

docker compose -f docker-compose.dev.yml up -d --build
docker compose -f docker-compose.dev.yml exec backend alembic upgrade head

Open http://localhost:7300.

Default login

In personal mode (SINGLE_USER_MODE=true) there is no login — you go straight in.

In team mode, .env.example ships a starter account so a fresh install has something to sign into:

Email demo@lochive.dev
Password lochive-demo-2026

Change this before anyone else can reach the instance. Either set a new SEED_USER_PASSWORD, or set SEED_USER=false and register your own account. Lochive refuses to create it when ENVIRONMENT=production, but that is a backstop, not a substitute for changing it.

Prefer a password nobody has seen? Leave SEED_USER_PASSWORD empty and one is generated on first boot and printed once:

docker compose -f docker-compose.dev.yml logs backend | grep -A3 "Starter account"

The first document is slower — the embedding model (~130 MB) downloads once and is cached in a volume.

API docs: http://localhost:7301/docs


Accounts and sign-in

Personal mode — nothing to sign in to

SINGLE_USER_MODE=true. Open the app and you're in. No account, no password.

Team mode — the starter account, or register your own

SINGLE_USER_MODE=false. Sign in with the default login above, or click "No account? Create one" to register. The first account is created the same way as every other one.

Registration is open to anyone who can reach the instance. Put it behind your VPN or a reverse proxy until invite-only signup exists.

How the starter account behaves

SEED_USER=true
SEED_USER_EMAIL=demo@lochive.dev
SEED_USER_PASSWORD=lochive-demo-2026   # empty = generate one and log it
  • Refused when ENVIRONMENT=production. It logs an error and creates nothing, so the shipped password can't follow you into a real deployment.
  • Ignored in personal mode, which has no accounts.
  • Never resets an existing password. Change it and restarts leave it alone — so changing the password in the app is enough; you don't also have to edit .env.
  • Set SEED_USER=false to stop creating it. That does not delete an account that already exists; remove it yourself if you want it gone.
  • The email must be a real-looking address. Reserved TLDs like .local fail login validation, leaving an account you cannot use.

Getting documents in

Four ways. All of them land in the same pipeline, and everything is searchable together.

1. Drag and drop

Drop files onto the sidebar. Simplest for one-offs.

2. A watched folder

The main way to use Lochive as an archive. Drop files into a folder and they ingest themselves — no browser needed.

Personal mode — one folder, yours:

inbox/
  owner@lochive.local/
    manuals/printer.pdf
    notes/2026-planning.md
    receipts/invoice.pdf

Team mode — one subfolder per employee, named by their email:

inbox/
  alice@company.com/
    handbook.pdf
  bob@company.com/
    contracts/acme-2026.docx

What happens:

You do Lochive does
Add a file Ingests it and makes it searchable
Edit a file Re-ingests it; answers reflect the new version
Delete a file Removes it and its vectors
Add a nested folder Scans it recursively
Add an unsupported file Ignores it
Add a huge file Skips it and says why

Scanned every INBOX_SCAN_SECONDS (default 30). Folders that don't match a registered user are skipped, never guessed at.

Lochive never modifies your folder. Files are read in place — nothing is moved, rewritten, or deleted. That's also why folder documents can't be deleted from the UI: delete the file instead, and the next scan catches up.

Using a folder somewhere else

./inbox is just the host path mounted into the container. Point it anywhere by editing the mount in docker-compose.dev.yml:

  backend:
    volumes:
      - /Users/you/Documents/Archive:/data/inbox   # any folder you like
  worker:
    volumes:
      - /Users/you/Documents/Archive:/data/inbox   # must match

Both services need the same mount — the API reads it, the worker ingests it. Mount read-only with :ro if you want that guarantee enforced by Docker rather than by convention:

      - /Users/you/Documents/Archive:/data/inbox:ro

Then recreate — restart won't re-read the file:

docker compose -f docker-compose.dev.yml up -d backend worker

Multiple source folders aren't supported yet; symlink or bind-mount them under one root for now.

3. Paste a link

Give it a URL to a web page or a direct file. HTML is stripped to text before ingesting.

URLs are validated against the resolved IP, not the hostname. Private, loopback, link-local (cloud metadata), and reserved ranges are refused, and every redirect hop is re-checked. Without that, "fetch this URL" would let anyone make your server read your internal network.

4. A SQL database — planned

Point Lochive at Postgres or MySQL, pick tables or a query, and turn rows into searchable documents on a refresh schedule. Designed, not yet built — see docs/ROADMAP.md.

Supported files

PDF, DOCX, TXT, MD, CSV, and HTML via URL. Scanned/image-only PDFs need OCR, which isn't implemented — they fail with a clear message rather than silently ingesting nothing.


Choosing a model

Runs on a local Ollama model by default, so it works with no API key and nothing leaves your machine. Any LiteLLM-supported model also works — GPT, Claude, Kimi, Groq, DeepSeek, Mistral, or any OpenAI-compatible server (vLLM, LM Studio, OpenRouter).

List what you want available; users pick per chat from a dropdown:

LLM_MODELS=[
  {"id":"ollama/gemma4:latest","label":"gemma4 (local)","api_base":"http://host.docker.internal:11434"},
  {"id":"gpt-4o-mini","label":"GPT-4o mini"},
  {"id":"claude-sonnet-5","label":"Claude Sonnet 5"},
  {"id":"moonshot/kimi-k2-0905-preview","label":"Kimi K2"}
]

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...
MOONSHOT_API_KEY=sk-...

(One line in .env — expanded here for readability.) The first entry is the default. Keys live in the environment and are never returned by the API.

Set api_base only for local or self-hosted runtimes. It's per-model on purpose: one global base URL would route your OpenAI calls at your local Ollama.

Sending documents to a hosted provider means they leave your machine. If that matters, keep a local model as the default — and note that per-source model restrictions are on the roadmap, not built yet.


Settings

Everything below is in the app at /settings — no .env editing, no restart.

API keys and models

Paste a provider key (OpenAI, Anthropic, Moonshot/Kimi, Groq, DeepSeek, Mistral, Together, Gemini) and add any LiteLLM model id. Added models appear in the chat picker straight away.

Keys are encrypted at rest and never returned by the API — the page shows only that a key is saved. They are passed to the provider per request and never written into the server's environment, so on a shared instance one person's key can't sign another person's call.

A self-hosted model endpoint on a private address (Ollama, vLLM) is refused unless you set ALLOW_PRIVATE_MODEL_HOSTS=true. Accepting arbitrary base URLs would let any user aim the server at your internal network.

Guardrails

  1. Answer only from my documents — on by default. Off lets the model add general knowledge, which it must mark as unsourced.
  2. Exclude a document — never embedded, never retrieved. Excluding one that's already indexed purges its vectors, rather than just hiding it.
  3. Local-only documents — never sent to a model that isn't local. A question that needs them is refused rather than answered from a quietly reduced set. Enforced server-side.
  4. Redact before sending — strips emails, phone numbers and card/ID numbers from text sent to a hosted model. Citations keep the real text, which never left your machine. It catches formatted identifiers, not names or addresses.

Exclusion and local-only are set per document in the sidebar; the rest are on the settings page.


How it works

drag & drop ─┐
folder       ─┼→ parse → chunk → embed → Qdrant (one collection per user)
link         ─┘                              ↓
question → embed → search → top matches → model → answer + citations

Ingestion runs in a background worker, so a large PDF doesn't block anything. Every query is scoped by user id on top of per-user collections.

Layer Choice
API FastAPI (Python 3.11, async)
Worker Celery + Redis
Database PostgreSQL + SQLAlchemy 2.x + Alembic
Vectors Qdrant, one collection per user
Embeddings fastembed / BAAI bge-small-en-v1.5 — local, no key
Generation LiteLLM — local or any provider, picked per chat
Frontend Next.js 14 + TypeScript + Tailwind + ShadcN

Ports

All in the 73xx block, bound to 127.0.0.1, so they don't collide with other local projects or get exposed on your network.

Service Host
frontend 7300
backend 7301
postgres 7302
redis 7303
qdrant 7304 / 7305

Change the frontend port and you must update CORS_ORIGINS in .env to match, or the browser blocks every API call. It's a comma-separated list, so adding an origin never needs a code change:

CORS_ORIGINS=http://localhost:7300,http://127.0.0.1:7300,https://archive.company.com

localhost and 127.0.0.1 entries automatically allow each other — browsers treat them as separate origins even though they're the same machine.

After editing, recreate the container; restart keeps the old environment:

docker compose -f docker-compose.dev.yml up -d backend

Layout

backend/app/
  core/       config, security, dependencies
  models/     User, Document, Chat, Message
  services/   business logic — routers stay thin
    parser_service     text extraction per file type
    chunker            overlapping splits
    embedding_service  local embeddings
    vector_service     Qdrant, per-user collections
    sync_service       the watched folder
    url_service        link fetching + SSRF guard
    ingestion_service  the worker-side pipeline
    chat_service       retrieval + generation + citations
  routers/    HTTP only
frontend/
  app/          login and chat pages
  lib/api.ts    the only place that calls the API

Tests

docker compose -f docker-compose.dev.yml exec backend pytest

78 tests, run against real Postgres — the same engine used in production, since the models use Postgres-native UUID, JSONB, and enum types.


Known limitations

  • No streaming. Answers arrive all at once; with a local model that's a visible wait (~40s on gemma4). Hosted models are much faster.
  • Citation numbering drifts on small local models. Retrieval picks the right passage, but a weak model sometimes writes [2] where it used [1].
  • Team mode has no shared workspace yet — every employee's archive is private. Sharing means putting a copy in each person's folder.
  • No admin controls. In team mode registration is open to anyone who can reach the instance; there's no invite flow or approval.
  • No refresh-token revocation. Signing out clears browser tokens but can't invalidate an issued refresh token server-side.
  • No OCR, so scanned PDFs are rejected.
  • Signing out revokes on this device only. Other sessions stay signed in until their tokens expire; there is no "sign out everywhere".
  • No settings UI — guardrails are designed, not built.
  • The folder scan is a full walk, hashing every file each pass. Fine for hundreds of files; for tens of thousands, switch to filesystem events.

Roadmap

  • Local archive: upload, watched folder, URL ingestion
  • Retrieval with citations
  • Any model, picked per chat
  • Personal mode with no login
  • Shared team workspaces
  • Settings + guardrails
  • SQL database as a source
  • Streaming answers
  • OCR for scanned documents
  • Admin controls (invites, roles)

Details and design in docs/ROADMAP.md.

Deploying it for real

docker-compose.dev.yml is a development stack: it reloads on change and mounts your source into the container. For anything other people use, there is docker-compose.yml:

cp .env.example .env          # then work through the checklist at the top of the file
docker compose up -d --build
docker compose exec backend alembic upgrade head

It builds the frontend rather than running a dev server, runs it as a non-root user, publishes nothing but the app and API on 127.0.0.1, keeps the database off the host entirely, and mounts the watched folder read-only. Put a reverse proxy in front for TLS.

Contributing

See CONTRIBUTING.md. Security reports go through SECURITY.md rather than a public issue.

License

MIT — see LICENSE.

About

Lochive turns your files into a private knowledge base you can ask questions of — in plain language, with every answer citing its source. It runs entirely on your own machine or server: local embeddings, a local LLM, no account and no API key required, so your documents never touch a third party. Open source, MIT licensed.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages