Skip to Content
TaresDeployment

Deployment

Tares 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 taresd and one data volume. Do not run multiple replicas against the same data.

Local

Install the package and run tares up:

uv tool install tares # or: pipx install tares tares up # console on http://127.0.0.1:8787, data in ~/.tares

tares up accepts --host, --port, --data-dir, --open, and --auth (see Authentication, off by default). To expose the MCP endpoint for external agents, also run tares mcp (see Connecting agents).

Docker

The published image runs the daemon by default:

docker run -p 8787:8787 -v tares-data:/data \ ghcr.io/glassflow/tares: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: tares services: taresd: image: ghcr.io/glassflow/tares:${TARES_VERSION:-latest} command: ["tares", "up", "--host", "0.0.0.0", "--data-dir", "/data"] environment: TARES_AUTH_TOKEN: "${TARES_AUTH_TOKEN:?set TARES_AUTH_TOKEN}" volumes: [tares-data:/data] expose: ["8787"] restart: unless-stopped mcp: image: ghcr.io/glassflow/tares:${TARES_VERSION:-latest} command: ["tares", "mcp", "--transport", "streamable-http", "--host", "0.0.0.0", "--port", "8788", "--taresd", "http://taresd:8787"] environment: TARES_AUTH_TOKEN: "${TARES_AUTH_TOKEN:?set TARES_AUTH_TOKEN}" expose: ["8788"] depends_on: [taresd] restart: unless-stopped caddy: image: caddy:2 ports: ["80:80", "443:443"] environment: TARES_DOMAIN: "${TARES_DOMAIN:-:80}" volumes: ["./Caddyfile:/etc/caddy/Caddyfile:ro", "caddy-data:/data"] depends_on: [taresd, mcp] restart: unless-stopped volumes: tares-data: caddy-data:
# Caddyfile: one hostname; /mcp and /sse go to the MCP server, the rest to the daemon {$TARES_DOMAIN} { handle /mcp* { reverse_proxy mcp:8788 } handle /sse* { reverse_proxy mcp:8788 } handle { reverse_proxy taresd:8787 } }

Set the domain and token, then start it:

export TARES_AUTH_TOKEN=$(openssl rand -hex 24) TARES_DOMAIN=tares.example.com docker compose up -d

A real TARES_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/tares: :latest (the default branch) and a tag per release (:0.1.0, :0.1). Pin TARES_VERSION for reproducible deploys.

Configuration

taresd is configured by environment variables.

variabledefaultdescription
TARES_DBtares.duckdbpath to the DuckDB file (tares up uses ~/.tares)
TARES_HOST127.0.0.1bind address (0.0.0.0 to expose)
TARES_PORT8787bind port
TARES_CATALOGcatalog.yamlcatalog YAML imported on first boot if the DB is empty
TARES_CATALOG_SYNCunsetif set, re-import the catalog YAML on every boot (file is source of truth)
TARES_AUTH_TOKENunsetrequire this bearer token on the API, console, MCP, and ingest (what tares up --auth sets)
TARES_OTLP_GRPC_PORT4317OTLP/gRPC receiver port (off to disable; needs the otlp-grpc extra)
ANTHROPIC_API_KEYunsetmodel key for the Ask assistant and Tares agents (or set one in the console)
TARES_AGENT_MODELclaude-sonnet-4-6model used by the Ask assistant and Tares agents

The MCP server (tares mcp / tares-mcp) reads TARESD_URL, TARES_AUTH_TOKEN, and TARES_MCP_TRANSPORT / TARES_MCP_HOST / TARES_MCP_PORT.

Authentication

Auth is one switch, set at launch:

  • tares up: open. No login; the API, console, and ingest are all reachable without a credential. The local default.
  • tares up --auth: secured. 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 TARES_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.

Upgrading from 0.x

1.0 renamed the project from NavFlow to Tares. There is no compatibility layer: the old names are not read at all. Two of the renames would otherwise fail silently, so the daemon refuses to start instead of guessing.

Environment variables. Every NAVFLOW_* variable is now TARES_*, with the same meaning. Nothing falls back to the old name. If any NAVFLOW_* variable is still set, tares up and taresd exit and print the exact renames you need:

tares 1.0 renamed every NAVFLOW_* environment variable to TARES_*, and does NOT read the old names. Still set in this environment: NAVFLOW_DB -> TARES_DB Rename them and start again.

Three variables are gone rather than renamed: NAVFLOW_INGEST_TOKEN and NAVFLOW_READONLY were folded into --auth plus scoped API keys in 0.2.0, and NAVFLOW_ANTHROPIC_KEY became the standard ANTHROPIC_API_KEY.

The database file. navflow.duckdb is now tares.duckdb. DuckDB creates a missing file happily, so an upgrade that left the old file in place would come up healthy and completely empty with your data untouched beside it. Instead the daemon stops:

found a pre-1.0 database at ~/.tares/navflow.duckdb, and none at ~/.tares/tares.duckdb.

Migrating is one move, with the daemon stopped:

mv ~/.tares/navflow.duckdb ~/.tares/tares.duckdb # or /data/… in a container

The data directory itself also moved, from ~/.navflow to ~/.tares. If you never set --data-dir or TARES_HOME, move the whole directory (mv ~/.navflow ~/.tares) before renaming the file inside it.

Everything else is a straight rename, with no runtime guard: the package and command (navflow to tares), the daemon (navflowd to taresd), the MCP proxy (navflow-mcp to tares-mcp), the --navflowd flag (--taresd), the image (ghcr.io/glassflow/navflow to ghcr.io/glassflow/tares), and the Claude Code plugin (navflow@navflow to tares@tares).

Backups

The data is the DuckDB file in the volume. Back it up by snapshotting the volume (or the host) or copying /data/tares.duckdb; restore by putting the file back before start.

Last updated on