Advanced install
The Quickstart commits to one path: the reference stack with local embeddings, ready in one command. This page is for when you want to go the other way — step by step, from the minimum: run just the engine against your own Postgres, build the image from source, verify the supply chain, and know every knob. Pick the pieces you need.
Platform-only — your own Postgres, the minimum
The leanest footprint: clone the engine on its own and start it with Docker Compose. This is the right choice when you already run Postgres, or simply want the smallest possible thing on disk. Either way the stack is Node 20 + Fastify + PostgreSQL 16 + pgvector, and the database migrations run automatically on first boot.
git clone https://github.com/KvendraAI/kvendra-platform
cd kvendra-platform && docker compose up -d Verify it is healthy:
curl http://localhost:7777/healthz # {"status":"ok","schema_version":"v1.0"}
The Platform-only compose ships with EMBEDDINGS_PROVIDER=mock
(deterministic stub vectors) so it boots with no external dependency. That is
fine for a first health check, but search is non-semantic until you point it
at a real provider — see Environment variables below.
Reading the auth token — two different commands
The engine bootstraps a bearer token at /data/auth.token
(mode 0600) on first boot, and gates every tools/call
on it. Where you read it from depends on how /data is mounted —
and the two supported stacks differ:
- Platform-only (bind-mount): the
kvendra-platformcompose file mounts./data:/data, so the token is a real file on your host. Read it directly:cat ./data/auth.token - Reference stack (named volume): the reference stack mounts
a named Docker volume at
/data, so there is no./data/auth.tokenon the host to cat. Read it out of the running container instead:docker exec kvendra-ref-platform cat /data/auth.token
Then register the Platform with your agent — the same step as Quickstart step 3:
TOKEN=$(docker exec kvendra-ref-platform cat /data/auth.token) # reference stack
# Platform-only (bind-mount): TOKEN=$(cat ./data/auth.token)
claude mcp add --transport http kvendra-platform http://localhost:7777/mcp \
-H "Authorization: Bearer $TOKEN"
Or add the equivalent block to .mcp.json at your project root
(it holds your token, so keep it out of version control):
{
"mcpServers": {
"kvendra-platform": {
"type": "http",
"url": "http://localhost:7777/mcp",
"headers": { "Authorization": "Bearer <your token>" }
}
}
}
Restart Claude Code (or reconnect it from /mcp)
to activate it, then confirm by calling whoami. Once
connected, the agent gains 14 MCP tools: entity_get,
entity_create, entity_update,
entity_archive, entity_related,
entity_query, entity_search,
txn_create, txn_activate, txn_cancel,
txn_check_interrupted, whoami,
config_get and help.
Build from source
There is no separate compile-from-source toolchain to learn:
building Kvendra Platform means building its Docker image.
The image itself is a multi-stage Node 20 build (install production deps,
compile TypeScript, assemble a minimal runtime), so a plain
docker build against the cloned repo is the from-source build:
git clone https://github.com/KvendraAI/kvendra-platform
cd kvendra-platform && docker build -t kvendra-platform:local .
The reference stack wraps exactly this for regulated environments that must
compile every Kvendra-controlled component from public source before running
anything. Its scripts/build-from-source.sh clones
kvendra-platform, runs docker build, and writes a
docker-compose.override.yml pointing the
kvendra-platform service at your locally-built image — so the
usual ./scripts/up.sh picks it up with no registry pull of the
Kvendra bits:
cd kvendra-reference-stack
./scripts/build-from-source.sh
./scripts/up.sh # uses the override automatically
The upstream base images (node:20-alpine,
pgvector/pgvector:pg16, ollama/ollama,
postgres:16-alpine) are still pulled from their public
registries — they are themselves open-source projects, not Kvendra builds.
Rebuilding those from source too is a rarely-needed extra step documented in
the reference stack's troubleshooting notes.
Published Docker images are signed
You do not have to build from source to trust the images. Each released
Kvendra image is signed with Sigstore/cosign keyless (via
GitHub Actions OIDC — there is no long-lived signing key to manage or leak),
and each release also attaches a signed SBOM attestation
(SPDX JSON, generated by Syft). The Fulcio certificate ties every signature
to the KvendraAI/kvendra-platform GitHub Actions workflow, and
the signature lands in Sigstore's transparency log (Rekor). Signing has
covered every release from the first published tag onwards.
The reference stack ships a one-shot verifier:
cd kvendra-reference-stack
./scripts/verify.sh # fails if any pinned image signature does not check out
Or verify by hand with cosign (brew install cosign on macOS):
IMG=docker.io/kvendra/kvendra-platform:<version>
# 1. image signature
cosign verify "$IMG" \
--certificate-identity-regexp '^https://github.com/KvendraAI/kvendra-platform/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
# 2. SBOM attestation
cosign verify-attestation "$IMG" \
--certificate-identity-regexp '^https://github.com/KvendraAI/kvendra-platform/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--type spdxjson
Both commands exit 0 with a JSON payload when the signature is
valid. The SBOM is also attached as a GitHub Release asset for archival and
compliance.
Environment variables
Everything is configured through environment variables (the reference stack
reads them from .env; the Platform-only compose sets them inline
or via its own .env). Most have sane defaults. The full surface:
Embeddings
The provider is resolved at startup. Allowed values are
openai-compatible and mock — a third,
bedrock, is intentionally not available in the
open-core Platform. With openai-compatible you can point at
Ollama, vLLM, llama.cpp or any other OpenAI-compatible
/embeddings endpoint; what you point it at decides whether
anything leaves your infrastructure. Vectors are validated as 1024-dim, so
use a 1024-dim model (e.g. mxbai-embed-large); other dimensions
fail at request time.
The reference stack's .env.example already points at the local
Ollama container, which ./scripts/up.sh starts by default. If
you run the engine on its own, or against an existing Ollama you manage,
point embeddings at it by hand:
# in .env
EMBEDDINGS_PROVIDER=openai-compatible
EMBEDDINGS_BASE_URL=http://kvendra-ollama:11434/v1
EMBEDDINGS_MODEL=mxbai-embed-large
To use your own embeddings provider instead of running a model locally,
point the same openai-compatible provider at any
OpenAI-compatible /embeddings endpoint and supply a key if it
needs one. The engine sends only the model and the input — it does not
request a dimensions size — so the model must return
1024-dimension vectors natively (for example mxbai-embed-large
or bge-large-en-v1.5 served by vLLM or TEI, or
mistral-embed), or sit behind a gateway such as LiteLLM that
pins the output to 1024. With the reference stack, start it with
./scripts/up.sh --no-ollama so the local container is skipped.
# in .env — your own OpenAI-compatible provider
EMBEDDINGS_PROVIDER=openai-compatible
EMBEDDINGS_BASE_URL=https://<your-provider>/v1
EMBEDDINGS_MODEL=<a 1024-dim model>
EMBEDDINGS_API_KEY=<your key>
After editing .env, re-run the stack (or restart the Platform
container) so the provider is picked up.
Embeddings are model-specific: vectors are not portable across models (a property of embedding spaces, not a Kvendra limitation). Pick a model and stay on it — switching models or providers, even between two 1024-dim ones, means re-embedding the corpus.
| Variable | Default | Notes |
|---|---|---|
EMBEDDINGS_PROVIDER | mock (Platform-only) | openai-compatible or mock. |
EMBEDDINGS_BASE_URL | — | Required for openai-compatible. E.g.
http://kvendra-ollama:11434/v1. |
EMBEDDINGS_MODEL | — | Required for openai-compatible. E.g.
mxbai-embed-large (1024-dim). |
EMBEDDINGS_API_KEY | unset | Bearer token. Omit for local Ollama, which needs no auth. |
EMBEDDINGS_TIMEOUT_MS | 30000 | Per-request timeout in milliseconds. |
Postgres & engine
| Variable | Default | Notes |
|---|---|---|
POSTGRES_USER | kvendra | Reference-stack Postgres user. |
POSTGRES_PASSWORD | kvendra | Change it for anything beyond local. |
POSTGRES_DB | kvendra_platform | Database name. |
DATABASE_URL | — | Platform-only connection string, e.g.
postgres://kvendra:kvendra@db:5432/kvendra_platform. |
PORT | 7777 | Port the engine listens on inside the container. |
PLATFORM_HOST_PORT | 7777 | Reference-stack host-side mapping. Change it if 7777 is taken on your host. |
HOST | 0.0.0.0 | Listen address. |
AUTH_TOKEN_FILE | /data/auth.token | Where the bearer token is generated and read from. |
LOG_LEVEL | info | trace | debug | … | fatal. |
STAGE | local | local | staging | prod. |
PG_POOL_MAX | 10 | Postgres connection-pool size. |
Backup sidecar & Ollama (reference stack)
| Variable | Default | Notes |
|---|---|---|
BACKUP_CRON | 30 3 * * * | Cron schedule for the pg_dump sidecar (daily 03:30). |
BACKUP_RETENTION_DAYS | 14 | How many days of dumps to keep before pruning. |
OLLAMA_MODELS_PRELOAD | mxbai-embed-large | Comma-separated models up.sh pulls on first
run (skipped with --no-ollama). The default is just the embedding model; add more models (e.g. a
chat model) if you need them. |
OLLAMA_HOST_PORT | 11434 | Host-side port for the Ollama container. |
Volumes
- Platform-only bind-mounts
./data:/data(so the token is a host file) and keeps Postgres data in a named volumekvendra_platform_pgdata. - Reference stack uses named volumes throughout —
kvendra_ref_platform_data(engine/data, incl. the token),kvendra_ref_pgdata(Postgres) andkvendra_ref_ollama_models(Ollama weights) — plus a host./backupsdirectory for the sidecar's dumps.
Just want it running? Go back to the Quickstart — one command, local embeddings, no signup.