Deployment
NavFlow is a single process writing to a single DuckDB file. There is no external database or broker
to run. DuckDB is single-writer, so a deployment is exactly one navflowd and one data volume —
do not run multiple replicas against the same data.
Local
Install the package and run navflow up:
uv tool install navflow # or: pipx install navflow
navflow up # console on http://127.0.0.1:8787, data in ~/.navflownavflow up accepts --host, --port, --data-dir, and --open. To expose the MCP endpoint for
external agents, also run navflow mcp (see Connecting agents).
Docker
The published image runs the daemon by default:
docker run -p 8787:8787 -v navflow-data:/data \
ghcr.io/glassflow/navflow:latestSelf-hosted (compose)
For a server, run the daemon, the MCP server, and a reverse proxy that terminates TLS and routes one hostname. The image serves both processes (same image, different command):
# docker-compose.yml
name: navflow
services:
navflowd:
image: ghcr.io/glassflow/navflow:${NAVFLOW_VERSION:-latest}
command: ["navflow", "up", "--host", "0.0.0.0", "--data-dir", "/data"]
environment:
NAVFLOW_AUTH_TOKEN: "${NAVFLOW_AUTH_TOKEN:?set NAVFLOW_AUTH_TOKEN}"
volumes: [navflow-data:/data]
expose: ["8787"]
restart: unless-stopped
mcp:
image: ghcr.io/glassflow/navflow:${NAVFLOW_VERSION:-latest}
command: ["navflow", "mcp", "--transport", "streamable-http", "--host", "0.0.0.0",
"--port", "8788", "--navflowd", "http://navflowd:8787"]
environment:
NAVFLOW_AUTH_TOKEN: "${NAVFLOW_AUTH_TOKEN:?set NAVFLOW_AUTH_TOKEN}"
expose: ["8788"]
depends_on: [navflowd]
restart: unless-stopped
caddy:
image: caddy:2
ports: ["80:80", "443:443"]
environment:
NAVFLOW_DOMAIN: "${NAVFLOW_DOMAIN:-:80}"
volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "caddy-data:/data"]
depends_on: [navflowd, mcp]
restart: unless-stopped
volumes:
navflow-data:
caddy-data:# Caddyfile — one hostname; /mcp and /sse go to the MCP server, the rest to the daemon
{$NAVFLOW_DOMAIN} {
handle /mcp* { reverse_proxy mcp:8788 }
handle /sse* { reverse_proxy mcp:8788 }
handle { reverse_proxy navflowd:8787 }
}Set the domain and token, then start it:
export NAVFLOW_AUTH_TOKEN=$(openssl rand -hex 24)
NAVFLOW_DOMAIN=navflow.example.com docker compose up -dA real NAVFLOW_DOMAIN enables Caddy’s automatic HTTPS (point its DNS A record at the host first);
unset, it serves plain HTTP on :80. The console is at https://<domain>; the MCP endpoint at
https://<domain>/mcp.
Image versions
Images are published to ghcr.io/glassflow/navflow: :latest (the default branch) and a tag per
release (:0.1.0, :0.1). Pin NAVFLOW_VERSION for reproducible deploys.
Configuration
navflowd is configured by environment variables.
| variable | default | description |
|---|---|---|
NAVFLOW_DB | navflow.duckdb | path to the DuckDB file (navflow up uses ~/.navflow) |
NAVFLOW_HOST | 127.0.0.1 | bind address (0.0.0.0 to expose) |
NAVFLOW_PORT | 8787 | bind port |
NAVFLOW_CATALOG | catalog.yaml | catalog YAML imported on first boot if the DB is empty |
NAVFLOW_CATALOG_SYNC | unset | if set, re-import the catalog YAML on every boot (file is source of truth) |
NAVFLOW_AUTH_TOKEN | unset | require this bearer token on the API, console, and MCP |
NAVFLOW_INGEST_TOKEN | unset | require this token on ingest endpoints (/ingest/*, /v1/*) |
NAVFLOW_READONLY | unset | refuse the control plane; ingest stays open (see below) |
NAVFLOW_VERCEL_VERIFY | unset | value echoed in the x-vercel-verify header for Vercel drains |
NAVFLOW_OTLP_GRPC_PORT | 4317 | OTLP/gRPC receiver port (off to disable; needs the otlp-grpc extra) |
NAVFLOW_AGENT_MODEL | claude-sonnet-4-6 | model used by the in-app Ask agent |
The MCP server (navflow mcp / navflow-mcp) reads NAVFLOWD_URL, NAVFLOW_AUTH_TOKEN, and
NAVFLOW_MCP_TRANSPORT / NAVFLOW_MCP_HOST / NAVFLOW_MCP_PORT.
Authentication and modes
NavFlow separates two surfaces:
- Control plane — the API, console, and MCP (reads, authoring, source management).
- Data plane — the ingest endpoints that push connectors deliver to.
| setting | effect |
|---|---|
NAVFLOW_AUTH_TOKEN | the control plane requires this bearer token; the console prompts for it, agents send it. The data plane is unaffected. |
NAVFLOW_INGEST_TOKEN | ingest endpoints require this token (X-NavFlow-Token or Authorization: Bearer). Independent of the auth token — producers are not console users. |
NAVFLOW_READONLY | the control plane refuses writes (the console and MCP go read-only); the data plane stays open so push sources keep delivering. Use for a public, read-only instance. |
A token in NAVFLOW_AUTH_TOKEN is the same credential as the console login. For per-user SSO, put a
proxy (oauth2-proxy, Tailscale, Caddy basic-auth) in front of the console; the machine paths (MCP,
ingest) keep their bearer tokens.
Backups
The data is the DuckDB file in the volume. Back it up by snapshotting the volume (or the host) or
copying /data/navflow.duckdb; restore by putting the file back before start.