Skip to content

Quickstart

Lorebase is pre-alpha. This page takes you from a fresh clone to a running Lorebase with the sample supply Domain loaded, the web app open, a first query answered, an agent connected over MCP and a model answering, then shows how to stop and clean up. Every command runs from the repository root unless the step says otherwise.

Step What you get Time measured on a fresh clone
2 Postgres, Keeper, the web app, the sample Domain, a first query 1.5 minutes measured; a few minutes more on a machine that has never pulled the base images
3 An agent reading the Domain through the MCP server (Herald) under a minute
2.7 Guide and Scribe with make up PROFILES=assistant: query embeddings, semantic search, schema suggestions from the example documents a few minutes for the second image
4 A chat model for document extraction: Ollama, or a hosted endpoint seconds with Ollama already on the machine or a hosted endpoint; about 11 minutes the first time for Ollama in Docker
5 Stop the stack, keep or delete its data seconds
6 What to do when a step fails
Manual setup The same path step by step: the core engine without services, tests, compose by hand, the REST API with curl and the CLI, the web app in mock mode

The measured times of each command are in Timings and in the fresh-clone check.

1. Prerequisites

Tool Version Check Needed for
git any recent git --version the clone
uv a recent release; it installs Python 3.12 by itself uv --version the CLI, Herald, the tests
Docker with the compose plugin compose v2 with up --wait and --profile "*" (checked with Docker 29 and compose 5.1) docker compose version Postgres, Keeper, Scribe, Ollama
Node.js and pnpm Node 24 or later; pnpm 11 (corepack enable installs the version of apps/web/package.json) node --version, pnpm --version only the web app dev server (pnpm dev, contributors); make up serves the web app from a container
curl, openssl, make any the helper commands

Resources: give Docker at least 4 GB of memory (8 GB with the Ollama container), and keep about 3 GB of disk free for the images (10 GB more for the Ollama image and a model). The stack publishes these ports on 127.0.0.1 only:

Port Service Variable in .env
8080 Keeper (REST API) KEEPER_HOST_PORT
5432 Postgres POSTGRES_HOST_PORT
11434 Ollama (profile local-models) OLLAMA_HOST_PORT
8090 web app (container started by make up) LOREBASE_WEB_PORT
8095 Guide (profile assistant) GUIDE_HOST_PORT
8091 Herald, Streamable HTTP (profile mcp) HERALD_HOST_PORT
8088, 5433 load balancer in front of the Keepers and read replica (profile scale-out, deployment) PROXY_HOST_PORT, POSTGRES_REPLICA_HOST_PORT
5173 web app dev server (pnpm dev, contributors) picks the next free port by itself

make up checks every port the chosen profiles publish before it starts anything and names the variable to change (lsof -nP -iTCP:8080 -sTCP:LISTEN shows who holds a port); if one is taken, change the variable in .env after step 2.2. It also refuses to start while a *_BIND value in .env would publish the Keeper of this stack, which runs with authentication off, beyond this machine (KEEPER_PUBLISH_INSECURE=true, in .env or the shell, starts it anyway). The compose project name comes from COMPOSE_PROJECT_NAME in .env (default lorebase): give a second checkout on the same machine another name, so that the two stacks never share containers or volumes.

2. Five-minute path

2.1 Clone and install

git clone https://github.com/aantenore/lorebase.git
cd lorebase
uv sync --all-packages

uv sync installs Python 3.12 if needed and every workspace package, among them the lorebase command line and Herald.

2.2 Create the environment file

make env

It copies .env.example to .env and fills POSTGRES_PASSWORD and LOREBASE_APP_PASSWORD with random values (openssl rand -hex 24), and LOREBASE_WEBHOOK_KEY, the key that seals webhook signing keys, with openssl rand -hex 32; an existing .env keeps its values. Without make: cp .env.example .env, then set the three yourself (URL-safe characters only for the passwords: they are embedded in connection strings). The other values have working defaults; this is where you change ports and COMPOSE_PROJECT_NAME.

2.3 Start Postgres, Keeper and the web app

make up

This runs docker compose --env-file .env -f deploy/compose/docker-compose.yaml up -d --build --wait postgres migrate keeper web. The first run builds the backend and web images (a few minutes; later runs reuse the layers). Compose starts Postgres 16, creates the application role Keeper connects as (db-roles), runs the migrations once (migrate exits when done), starts the web app and returns when Keeper and the web app are healthy. It ends by printing the Keeper URL (Keeper is ready at http://127.0.0.1:8080 with the default KEEPER_HOST_PORT), the web app URL (http://127.0.0.1:8090) and the export line that points the CLI at them (make shell-env prints it again). Point the commands of this page at that Keeper and check it:

eval "$(make shell-env)"   # export LOREBASE_URL=http://127.0.0.1:<KEEPER_HOST_PORT> LOREBASE_DOMAIN=supply, the line `make up` printed
curl "$LOREBASE_URL/readyz"
{"status":"ready","checks":{"database":{"status":"ok","detail":null}}}

$LOREBASE_URL/api/v1/docs serves the interactive OpenAPI docs. The web app answers on http://127.0.0.1:8090, and http://127.0.0.1:8090/api/v1/auth/whoami reaches Keeper through it.

Credentials. There are none to type. The example configuration (config/lorebase.example.yaml) sets keeper.auth.mode: none: Keeper answers every request as an anonymous installation administrator, and the web app needs no sign-in. That is for a laptop only: compose publishes the port on 127.0.0.1 and Keeper refuses that mode on any other address. The database passwords are the ones in .env; you never need them for the steps on this page. Turning authentication on is in The first administrator key.

2.4 Load the example data

make example-data
[quickstart] creating Domain supply (required_approvals 0)
[quickstart] applying the supply schema to branch work
[quickstart] importing the sample data
batch 1/1: rows 1-426 committed, 426 edits
[quickstart] merging Proposal prp_... into main
[quickstart] loaded the supply sample into Domain supply (branches main and work)

It uses the lorebase CLI against the Keeper of .env: creates the Domain supply, applies examples/supply/schema.yaml to a branch work, imports the CSV files of examples/supply/data (185 Records with their Links), opens a Proposal from work to main and merges it. main is protected, so changes reach it only through a Proposal; the quickstart Domain is created with required_approvals: 0 so that you, the only principal of a stack without authentication, can merge your own Proposal. Real Domains keep the default of one approval from someone else. Running it again does nothing once the sample is on main.

2.5 First query

# LOREBASE_URL and LOREBASE_DOMAIN=supply as exported in step 2.3
uv run lorebase record list Part -n 5 \
  --where '{"attr": "category", "op": "eq", "value": "bearing"}' --select category --select unit_cost
uv run lorebase search bearing --kind Part
key      title                        category  unit_cost
P-10001  Tapered roller bearing 6305  bearing   10.79
P-10010  Needle roller bearing 6305   bearing   40.48
P-10013  Needle roller bearing NK25   bearing   25.62
P-10023  Needle roller bearing NK25   bearing   37.59
P-10029  Needle roller bearing 6001   bearing   45.94

The same over REST, with no CLI:

curl -s -X POST "$LOREBASE_URL/api/v1/domains/supply/query" \
  -H 'Content-Type: application/json' \
  -d '{"kind": "Part", "aggregate": {"group_by": "category"}}'

The answer lists the Parts and ends with "aggregate":{"counts":[{"kind":"Part","value":"bearing","count":6}, ...]}.

Reads never call a model; every answer names the Revision it was served from in the Lorebase-Revision header. Manual setup goes through the API one call at a time.

2.6 Open the web app

make up already started it. Open http://127.0.0.1:8090 (LOREBASE_WEB_PORT in .env). The app opens on the supply Domain and its main branch: Explorer lists the six Kinds, Canvas draws the graph around a Record, Review lists Proposals (the one that loaded the sample is under the Merged filter), Reconciliation lists the values on which Sources disagree. There is no sign-in with authentication off; with an API key, paste it in Settings.

The page and the API share one origin: the web service of the compose file (deploy/docker/web.Dockerfile, nginx) serves the built app and forwards /api/ to Keeper on the compose network, event streams included, so Keeper needs no CORS setting. The Content Security Policy allows connections to that origin only. With make up PROFILES=assistant (2.7) /guide/ is forwarded to Guide the same way. The web settings are in .env.example; apps/web/README.md lists the variables of the app itself, and a mock mode with built-in sample data and no Keeper is in Manual setup.

Browser sign-in (WEB_AUTH_MODE=session) can be tried on this stack with a mock OpenID Connect provider, the overlay deploy/compose/oidc-mock.yaml (it signs in anyone: local use only). Add a session key to .env, then start Keeper, the web app and the provider with it:

echo "LOREBASE_SESSION_KEY=$(openssl rand -base64 48)" >> .env
docker compose --env-file .env -f deploy/compose/docker-compose.yaml \
  -f deploy/compose/oidc-mock.yaml up -d --wait keeper web oidc-mock

Keeper switches to auth.mode: oidc with auth.sessions (client lorebase-web, PKCE, callback http://127.0.0.1:8090/api/v1/auth/callback) and the app to session mode. Open http://127.0.0.1:8090 and sign in: the provider's page asks for any user name (and optional claims such as {"name": "Alice"}), then returns to the app with a session cookie. The provider runs in Keeper's network namespace and is published on 127.0.0.1:8096 (OIDC_MOCK_PORT), so the browser and Keeper use the same issuer URL, http://localhost:8096/lorebase. The user is created at its first sign-in (keeper.auth.oidc.auto_provision) and, like any principal, reads what its grants allow. To go back to authentication off, remove the provider (the same command with rm -sf oidc-mock instead of up ...) and run make up. Settings and cookies: deployment.

Contributors who change the app run the dev server instead (Node.js 24 and pnpm), with hot reload, on the Keeper of the stack:

cd apps/web
pnpm install
printf 'VITE_API_MODE=live\nVITE_KEEPER_BASE_URL=/\n' > .env.local
LOREBASE_KEEPER_PROXY="$LOREBASE_URL" pnpm dev    # the Keeper URL of step 2.3

Open the URL it prints (http://localhost:5173, or the next free port). The dev server forwards /api to Keeper, so no CORS setting is needed here either.

2.7 Guide, Scribe and the example documents

Guide (the exploration assistant, /embed for query vectors) and Scribe (the worker: extraction, embeddings) are not part of the plain make up. Start them with the assistant profile:

make up PROFILES=assistant     # or: make up-assistant

It builds a second image, lorebase-ml (target backend-ml of the backend Dockerfile: the backend image plus fastembed and the embedding model BAAI/bge-small-en-v1.5, downloaded at build time so nothing is fetched at run time; 844 MB against 453 MB for the backend image, which is why Keeper, the migrations and Herald stay on the small one), starts Guide and Scribe next to the default services, and adds the overlay deploy/compose/assistant.yaml: the web app learns its Guide at /guide and Guide accepts calls without credentials, which is only right while Keeper runs with authentication off as here. make down stops every profile make up can start. EMBEDDING_MODEL in .env names another model to bake in (empty: none, and the model is downloaded on first use into a writable inference.embeddings.cache_dir).

Semantic search. The supply schema gives Part and Supplier an embedding template, so Scribe embeds them on its next pass (every scribe.scheduler.embeddings_interval_s, 60 s). Guide embeds the query, and the vector goes to the search:

curl -s http://127.0.0.1:8090/guide/embed -H 'Content-Type: application/json' \
  -d '{"text": "protects a rotating shaft from oil leaks", "mode": "query"}' \
  | python3 -c 'import json, sys; print(json.dumps(json.load(sys.stdin)["vector"]))' > "${TMPDIR:-/tmp}/query.json"
uv run lorebase search "protects a rotating shaft from oil leaks" --kind Part \
  --mode semantic --vector-file "${TMPDIR:-/tmp}/query.json" -n 3
kind  key      title                       score  snippet
Part  P-10028  Radial shaft seal 25x42x7   0.732  Radial shaft seal 25x42x7 Stock item for machine tool spind...

The first hits are radial shaft seals although the query shares no word with the name. /embed needs no chat model. Guide's questions (/explore, the Explore panel) do: set LLM_* as in section 4.

Schema suggestions. Extraction reports what the documents hold that the schema has no place for, and lists a suggestion once schema.suggestions.min_support observations (2) support it. A single document supports each of its suggestions once, so uploading only the contract C-3099 lists nothing (lorebase schema suggestions list --weak shows the one it made, an Attribute buyer of Contract). The step below uploads that contract and the four documents of examples/supply/documents, rules tier only, so it needs no model, and prints the suggestions:

make example-documents
id                              type       name               of        support  docs  status   samples
ssg_...                         attribute  ends               Supplier  3        1     pending  2026-02-07, 2026-02-09, 2026-04-12
ssg_...                         relation   sole_source_parts  Supplier  3        1     pending
ssg_...                         attribute  buyer              Contract  2        2     pending  Corvane Industries, purchasing entity of the Distribution d...

buyer is supported by two documents (C-3099 and C-3025), ends and sole_source_parts by the three rows of the supplier review. The contract C-3099 also opens a Proposal with its new Contract Record; the other documents describe Records that make example-data loaded. Review the suggestions in the web app (Review, Schema suggestions) or with uv run lorebase schema suggestions accept ID; see extraction.

3. Connect an agent over MCP

Herald, the MCP server, turns the Domain into tools an MCP host can call (lorebase_search, lorebase_get, lorebase_query, lorebase_context and more, listed in mcp.md). It runs from the checkout; the host starts it over stdio. Add this entry to the MCP configuration of your host (most hosts read an mcpServers map; mcp.md has the file for each host), with the absolute path of your clone and the Keeper URL make up printed:

{
  "mcpServers": {
    "lorebase": {
      "command": "uv",
      "args": ["--directory", "/absolute/path/to/lorebase", "run", "lorebase-herald"],
      "env": {
        "LOREBASE_URL": "http://127.0.0.1:8080",
        "LOREBASE_DOMAIN": "supply"
      }
    }
  }
}

Restart the host and ask it, for example, "which parts depend on a single supplier?". With authentication off no key is needed. Once authentication is on, give the agent its own key with only the grants it needs, and keep it out of the file with "LOREBASE_API_KEY": "secret://file//absolute/path/to/agent.key" (mcp.md).

To check Herald without a host, list its tools and run one search from Python (the mcp package is installed by uv sync):

LOREBASE_DOMAIN=supply uv run python - <<'EOF'    # LOREBASE_URL as exported in step 2.3
import asyncio, os
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server = StdioServerParameters(command="uv", args=["run", "lorebase-herald"], env=dict(os.environ))
    async with stdio_client(server) as (read, write), ClientSession(read, write) as session:
        await session.initialize()
        print([tool.name for tool in (await session.list_tools()).tools])
        result = await session.call_tool("lorebase_search", {"query": "bearing", "limit": 2})
        print(result.content[0].text)

asyncio.run(main())
EOF

Herald logs its requests to Keeper on standard error; standard output shows:

['lorebase_schema', 'lorebase_get', 'lorebase_query', 'lorebase_search', ...]
rev bb30d34bebf7... (lexical): 2 hits
1. Part/P-10001 "Tapered roller bearing 6305" (2.4) - Tapered roller <b>bearing</b> 6305 ...

For agent frameworks that speak Streamable HTTP, run one shared Herald with LOREBASE__HERALD__TRANSPORT=streamable_http (mcp.md), or start it in the compose stack:

make up PROFILES=mcp      # Herald at http://127.0.0.1:8091/mcp (HERALD_HOST_PORT)

Agents send Authorization: Bearer <api key>; with the stack's authentication off, set HERALD_ALLOW_ANONYMOUS=true in .env to let them call without one, and HERALD_DOMAIN=supply so that calls naming no domain read the sample Domain (without it every call must pass domain) (mcp.md).

4. Use a real model

Reads never call a model. Models are used by Scribe, the worker, to extract documents into Proposals (extraction.md). Lorebase talks to any OpenAI-compatible endpoint, selected by three variables of .env: LLM_BASE_URL, LLM_MODEL (default: OLLAMA_MODEL) and LLM_API_KEY. inference-check sends one short prompt and prints the model, the latency and the token counts. Pick one of the three options.

Ollama in Docker (the default). The local-models profile starts Ollama and pulls OLLAMA_MODEL. The image is about 7 GB and the default model qwen2.5:7b-instruct 4.7 GB; it runs on the CPU inside Docker. A small model is enough to try it:

# in .env: OLLAMA_MODEL=qwen2.5:1.5b-instruct  (and OLLAMA_HOST_PORT=11435 if Ollama already runs on the host)
docker compose --env-file .env -f deploy/compose/docker-compose.yaml \
  --profile local-models run --rm inference-check
chat       ok     provider=openai_compatible model=qwen2.5:1.5b-instruct latency_ms=36015 tokens=30/9 structured_output=json_schema

The model download prints nothing while it runs; follow it with docker compose --env-file .env -f deploy/compose/docker-compose.yaml logs -f ollama-pull.

Ollama already on your machine (faster on a Mac, where Docker has no GPU). Point the stack at it and skip the Ollama container with --no-deps:

ollama pull qwen2.5:7b-instruct
# in .env: LLM_BASE_URL=http://host.docker.internal:11434/v1 and LLM_MODEL=qwen2.5:7b-instruct
docker compose --env-file .env -f deploy/compose/docker-compose.yaml run --rm --no-deps inference-check

Any instruct model works. Prefer one that answers without a long reasoning phase: a reasoning model such as qwen3:4b passes the check but can run past the 120-second request timeout on a whole document.

On Linux, Ollama must listen beyond loopback for containers to reach it (OLLAMA_HOST=0.0.0.0).

A hosted provider. Any OpenAI-compatible endpoint works. Keep the key in a file, never in .env or on the command line: secrets/ is ignored by git, and the overlay deploy/compose/hosted-model.yaml mounts the file as a compose secret that the services read through secret://file//run/secrets/llm_api_key.

mkdir -p secrets && (umask 077 && cat > secrets/llm_api_key)   # paste the key, then Ctrl+D
# in .env: LLM_BASE_URL=https://api.example-provider.com/v1, LLM_MODEL=<model name>, LLM_API_KEY empty
docker compose --env-file .env -f deploy/compose/docker-compose.yaml \
  -f deploy/compose/hosted-model.yaml run --rm --no-deps inference-check

Providers that do not speak the OpenAI protocol go through the LiteLLM proxy (profile litellm); per-task routing, prices and embeddings are in inference-providers.md.

Keep the overlay for make. COMPOSE_OVERLAYS lists extra compose files, separated by spaces and relative to the repository root, that make up, make logs, make down, make clean-stack and scripts/quickstart.sh add after their own. Set it in .env so that every command starts Scribe with the hosted key:

# in .env
COMPOSE_OVERLAYS=deploy/compose/hosted-model.yaml

Your own overlay (another secret, a model setting of your own) goes next to the key, under the ignored secrets/ directory, and is listed after it: COMPOSE_OVERLAYS=deploy/compose/hosted-model.yaml secrets/my-model.yaml. A listed file that does not exist stops the command before compose runs. COMPOSE_OVERLAYS=... make up on the command line overrides .env for one run.

Extract a document. Start Scribe with the same files (make up PROFILES=assistant starts it with the files of COMPOSE_OVERLAYS; by hand, add -f deploy/compose/hosted-model.yaml for a hosted key) and upload a contract that the sample does not hold yet; tier 0 uses rules only, tier 2 adds the chat model:

docker compose --env-file .env -f deploy/compose/docker-compose.yaml up -d --no-deps scribe
uv run lorebase documents upload examples/five_day_demo/documents/contract-c-3099-wexrix-filtration.md --tiers 0,2
uv run lorebase documents list                 # then: uv run lorebase documents get <id>
uv run lorebase proposal list --status open

When the job has succeeded, the extraction is an open Proposal from branch extract/<job> to main, with one item per Record, value and Link and the evidence of each (here the Contract C-3099 and its with_supplier Link). Review it under Review in the web app, or with uv run lorebase proposal items <id> and uv run lorebase proposal decide. A value a document states differently from the current one is also held for review as a reconciliation candidate (Reconciliation in the web app, uv run lorebase reconcile list; Scribe). The documents of examples/supply/documents describe Records that make example-data already loaded, so their jobs succeed with no items and open no Proposal. With lfm2.5:8b on the host the job took 91 seconds; a small model on the CPU inside Docker takes several minutes.

5. Stop and clean up

make logs          # follow the logs (SERVICES="scribe guide" narrows them); Ctrl+C stops
make down          # stop the containers of every profile `make up` can start, keep the data
make clean-stack   # stop them and delete the volumes: database, pulled models

make clean-stack runs docker compose ... --profile "*" down -v --remove-orphans, so the Ollama and observability containers go too. (If you started the contributor dev server, stop it with Ctrl+C.) To remove the built images as well: docker image rm lorebase:dev lorebase-ml:dev lorebase-web:dev (the LOREBASE_IMAGE, LOREBASE_ML_IMAGE, LOREBASE_WEB_IMAGE and LOREBASE_TAG of .env). Delete the clone and nothing else is left on the machine.

6. Troubleshooting

Symptom Cause and fix
port 5432 on 127.0.0.1 (POSTGRES_HOST_PORT) is already in use from make up, or Bind for 127.0.0.1:5432 failed: port is already allocated from compose run by hand (or 8080, 8090, 8091, 8095, 11434) Another Postgres, service or Ollama holds the port. Set POSTGRES_HOST_PORT, KEEPER_HOST_PORT, LOREBASE_WEB_PORT, HERALD_HOST_PORT (profile mcp), GUIDE_HOST_PORT (profile assistant) or OLLAMA_HOST_PORT in .env, run make up again, and use the Keeper URL it prints in LOREBASE_URL (and LOREBASE_KEEPER_PROXY of the dev server).
refusing to start: KEEPER_BIND=0.0.0.0 publishes Keeper beyond this machine (or WEB_BIND, GUIDE_BIND, HERALD_BIND) The stack runs with authentication off, so make up keeps its ports on 127.0.0.1. Keep the *_BIND values there, turn authentication on first (KEEPER_ALLOW_INSECURE=false and an auth mode, manual setup), or set KEEPER_PUBLISH_INSECURE=true to publish it anyway.
required variable POSTGRES_PASSWORD is missing a value, set LOREBASE_APP_PASSWORD in the environment file or set LOREBASE_WEBHOOK_KEY in the environment file .env is missing or a password or the webhook key is empty: make env fills them.
Keeper unhealthy, make up fails with dependency failed to start docker compose --env-file .env -f deploy/compose/docker-compose.yaml logs migrate keeper names the error. A password changed after the first start does not change the database: run make clean-stack (deletes the data) and make up.
curl: (52) Empty reply from server right after up -d without --wait Keeper is still starting; make up waits for its health check.
The stack runs code older than the checkout The image tag lorebase:dev is shared by every checkout on the machine. Compose rebuilds the images from the checkout on every up (LOREBASE_PULL_POLICY=build, the default; docker compose ... build builds them alone); check that .env does not set LOREBASE_PULL_POLICY=missing, or set LOREBASE_TAG per checkout.
migrate fails with the database is at revision ..., which this code does not know A newer Lorebase migrated this database and older code is running. Rebuild from the newer checkout (make up, or docker compose ... build), or make clean-stack to start over without the data (deployment).
Containers or volumes of another Lorebase stack are reused or replaced Both use the project name lorebase. Set COMPOSE_PROJECT_NAME in .env.
docker compose hangs on pull or build with a credential helper error The Docker credential helper is stuck. Run the command with an empty Docker config: DOCKER_CONFIG=$(mktemp -d) make up (public images need no login).
no configuration file provided: not found The command left out -f deploy/compose/docker-compose.yaml, or did not run from the repository root.
make example-data: Domain supply exists ... but main does not hold the sample An earlier load stopped half way, or the Domain was created by hand (manual setup). uv run lorebase domain delete supply -y, then make example-data.
make example-data: no Keeper at http://127.0.0.1:8080 The stack is not running, or Keeper runs on another port: make up, or LOREBASE_URL=http://127.0.0.1:PORT make example-data.
The web app shows "This Branch has no Kinds yet" It is on a branch without the sample: pick main or work in the top bar, or run make example-data.
The web app at http://127.0.0.1:8090 shows a network error or 502 Bad Gateway on /api/ Keeper is not up (or restarting): docker compose --env-file .env -f deploy/compose/docker-compose.yaml ps keeper web and logs keeper web. The web service reaches Keeper at keeper:KEEPER_PORT, so KEEPER_PORT must be the port Keeper listens on.
The web app does not load, or the browser blocks requests (CSP) Open it at the address make up printed (LOREBASE_WEB_PORT), not through another host name or a tunnel: the policy allows connections to the origin that served the page. Add other origins with LOREBASE_WEB_CSP_CONNECT_SRC on the web service.
The assistant (Guide) does not answer from the web app Start it with make up PROFILES=assistant. A 502 on /guide/ means Guide is not running. With plain compose, add the profile and -f deploy/compose/assistant.yaml, or set WEB_GUIDE_URL=/guide and GUIDE_ALLOW_ANONYMOUS=true in .env.
/guide/embed answers 502 The service runs on the plain backend image: build the lorebase-ml image (make up PROFILES=assistant does) or point inference.embeddings at an OpenAI-compatible endpoint.
make example-documents waits and gives up Scribe is not running: make up PROFILES=assistant.
Schema suggestions are empty after one upload A pending suggestion is listed from schema.suggestions.min_support (2) observations: run make example-documents, or lorebase schema suggestions list --weak.
The contributor dev server (pnpm dev) cannot reach Keeper LOREBASE_KEEPER_PROXY must be set when pnpm dev starts and must name Keeper's port; .env.local must hold VITE_API_MODE=live and VITE_KEEPER_BASE_URL=/. Restart pnpm dev after a change.
The MCP host shows no Lorebase tools The host could not start uv: give its absolute path (which uv) in command, and the absolute path of the clone after --directory. Check Herald with the script of section 3.
Herald answers [not-found] LOREBASE_DOMAIN names a Domain that does not exist, or LOREBASE_BRANCH a branch without the Record.
inference-check: connection refused or timeout Ollama is not reachable from the containers: in Docker the address is http://ollama:11434/v1 (profile local-models), on the host http://host.docker.internal:11434/v1 with --no-deps.
An extraction job fails with EndpointError: /chat/completions: request failed (ReadTimeout) The model needs more than inference.llm.timeout_s (120 s) per call: use a smaller or non-reasoning instruct model, or raise the timeout in a copy of config/lorebase.example.yaml named by LOREBASE_CONFIG_FILE in .env.
inference-check: model ... not found The model is not pulled: ollama pull NAME on the host, or wait for ollama-pull (docker compose ... logs -f ollama-pull).
inference-check with the hosted overlay: bind source path does not exist: .../secrets/llm_api_key Create secrets/llm_api_key, or point LLM_API_KEY_FILE at the key file (relative paths start from deploy/compose).
A step is slow the first time The backend image build, the Ollama image (about 7 GB) and model downloads happen once; Timings shows what to expect.

Timings

Measured on 2026-10-22 on an Apple silicon laptop (Docker Desktop with Docker 29.5 and compose 5.1, 15 CPUs and 7.7 GB of memory for Docker), from a fresh copy of the repository, following this page. The Docker build cache and the pgvector and Python base images were already on the machine; a machine that has never pulled them spends a few more minutes in make up.

Step Command Time
2.1 git clone 6 s
2.1 uv sync --all-packages, empty uv cache 29 s (1 s with a warm cache)
2.2 make env under 1 s
2.3 make up (backend and web images, Postgres, migrations, Keeper and web healthy) 38 s (the web image adds a pnpm install and a build)
2.4 make example-data 5 s
2.5 first query, CLI and curl 1 s
2.6 open the web app (already running); contributor path: pnpm install, empty store; pnpm dev until ready 0 s; 11 s; 2 s
3 Herald check script (start, list tools, one search) 3 s
4 inference-check, Ollama in Docker, first time (7 GB image, qwen2.5:1.5b-instruct, CPU) 645 s; 209 s with the image already pulled
4 inference-check, Ollama on the host (lfm2.5:8b) 9 s
4 inference-check with the hosted overlay and a key file 5 s
4 extraction of contract C-3099, tiers 0 and 2, lfm2.5:8b on the host 91 s; tier 0 alone under 1 s
5 make down; make clean-stack 1 s; under 1 s

make up PROFILES=assistant adds the lorebase-ml image: 844 MB against 453 MB for the backend image (fastembed with ONNX Runtime, and a 65 MB model).

The five-minute path (2.1 to 2.6) took about 80 seconds end to end.

Manual setup

The steps that used to follow here are on their own page, Manual setup:

Next steps

  • Model your own Domain: the modeling guide explains every field of the schema with a complete example.
  • Decide who approves what and what may merge on its own: governance and ingestion.
  • Plug in an agent: examples/agent reads the supply Domain through Herald and proposes changes as Proposals, with Ollama or a hosted model.

Coming next

The roadmap tracks what is planned, milestone by milestone.