Skip to Content
Deployment

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 ~/.navflow

navflow up accepts --host, --port, --data-dir, --open, and --auth (see Authentication — off by default). 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:latest

Self-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 -d

A 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.

variabledefaultdescription
NAVFLOW_DBnavflow.duckdbpath to the DuckDB file (navflow up uses ~/.navflow)
NAVFLOW_HOST127.0.0.1bind address (0.0.0.0 to expose)
NAVFLOW_PORT8787bind port
NAVFLOW_CATALOGcatalog.yamlcatalog YAML imported on first boot if the DB is empty
NAVFLOW_CATALOG_SYNCunsetif set, re-import the catalog YAML on every boot (file is source of truth)
NAVFLOW_AUTH_TOKENunsetrequire this bearer token on the API, console, MCP, and ingest (what navflow up --auth sets)
NAVFLOW_OTLP_GRPC_PORT4317OTLP/gRPC receiver port (off to disable; needs the otlp-grpc extra)
ANTHROPIC_API_KEYunsetmodel key for the Ask assistant and NavFlow agents (or set one in the console)
NAVFLOW_AGENT_MODELclaude-sonnet-4-6model used by the Ask assistant and NavFlow agents

The MCP server (navflow mcp / navflow-mcp) reads NAVFLOWD_URL, NAVFLOW_AUTH_TOKEN, and NAVFLOW_MCP_TRANSPORT / NAVFLOW_MCP_HOST / NAVFLOW_MCP_PORT.

Authentication

Auth is one switch, set at launch:

  • navflow upopen. No login; the API, console, and ingest are all reachable without a credential. The local default.
  • navflow up --authsecured. Every route requires a credential: the console and API, and ingest too. A bare --auth generates a root token, persists it to the data dir, and prints a click-to-login URL each launch (…/?token=<root>). --auth=<token> (or setting NAVFLOW_AUTH_TOKEN) uses your own token — the shape for hosted/scripted deploys.

There is no separate ingest token and no read-only mode. On a secured instance you hand machines their own scoped API keys — never the root token.

API keys

The root token is the operator’s login; you mint narrower credentials from it. In the console → Security → API keys, create a key with a subset of three scopes:

scopegrants
readqueries, timelines, catalog, an agent’s own views & subscriptions (the MCP read surface)
ingestPOST /ingest/*, /v1/*, write memories
adminsources / views / triggers, key management (implies the others)

Keys are shown once at creation, revocable, and never returned again. Typical holders: an MCP agent → read; a producer (Vercel drain, OTLP exporter, webhook) → its own ingest key; the Claude Code plugin → read + ingest. On a secured instance, creating a push source mints an ingest key for it and shows it once — hand that to the producer as Authorization: Bearer ….

The root token is the console login. For per-user SSO, put a proxy (oauth2-proxy, Tailscale, Caddy basic-auth) in front of the console; machines keep their scoped API keys.

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.

Last updated on