Files
krow_backend/.env.example
2026-08-28 12:21:44 +05:30

132 lines
6.4 KiB
Plaintext

# ============================================================================
# Krow backend — example environment
#
# Copy to .env and fill in. .env is gitignored and must never be committed.
# Every value below is a placeholder or a safe local default: no real password,
# API key or token belongs in this file.
#
# cp .env.example .env
# ============================================================================
# ── Application ─────────────────────────────────────────────────────────────
APP_ENV=development # development | staging | production
LOG_LEVEL=info # debug | info | warn | error
# ── HTTP server ─────────────────────────────────────────────────────────────
HTTP_HOST=127.0.0.1
HTTP_PORT=8080
HTTP_READ_TIMEOUT=15s
HTTP_WRITE_TIMEOUT=30s
HTTP_IDLE_TIMEOUT=60s
HTTP_SHUTDOWN_TIMEOUT=10s
# Browser origins allowed to call this API cross-origin, comma-separated.
# Unset in development it defaults to the Vite dev server on both hostnames
# (localhost and 127.0.0.1 are different origins to a browser) plus `vite
# preview`. Unset anywhere else it defaults to empty, meaning same-origin only.
# Origins are matched exactly, echoed back one at a time, and "*" is rejected.
# HTTP_CORS_ORIGINS=http://localhost:5173,http://127.0.0.1:5173
# ── PostgreSQL ──────────────────────────────────────────────────────────────
# The local development database. DATABASE_NAME is mixed-case and hyphenated,
# so anything that interpolates it into SQL must quote it: "Krow-force".
DATABASE_HOST=127.0.0.1
DATABASE_PORT=5432
DATABASE_NAME=Krow-force
DATABASE_USER=postgres
DATABASE_PASSWORD=
DATABASE_SCHEMA=public
# sslmode: disable is fine for a loopback dev database. APP_ENV=production
# rejects `disable` at startup — use require or verify-full there.
DATABASE_SSLMODE=disable
# Pool and timeout tuning.
DATABASE_MAX_OPEN_CONNS=25
DATABASE_MIN_IDLE_CONNS=2
DATABASE_CONN_MAX_LIFETIME=30m
DATABASE_CONNECT_TIMEOUT=5s
DATABASE_STATEMENT_TIMEOUT=10s
# ── Migrations ──────────────────────────────────────────────────────────────
# Consumed by the Makefile, which builds the golang-migrate URL from the
# DATABASE_* values above. Keep it pointed at the repository's migrations/.
MIGRATIONS_DIR=./migrations
# ── Seed ────────────────────────────────────────────────────────────────────
# The demo fixture, generated from the frontend repository's src/api/seed.js.
# The Makefile passes an absolute path; this default suits running from the
# repository root.
SEED_FIXTURE_PATH=./seed/fixtures/seed.json
# ── Model gateway ───────────────────────────────────────────────────────────
# The one place this service talks to a language model. An agent spec declares
# a `reasoning` tier — fast | balanced | deep — never a model id, so the
# mapping below is a deployment decision and changes without editing a single
# definition.
#
# The key may be left empty outside production: migrations, seeding and every
# endpoint that is not an agent run work without one, and an agent run fails
# with a structured `gateway.not_configured` rather than the service refusing
# to boot. APP_ENV=production requires it.
ANTHROPIC_API_KEY=
# All three tiers default to the same model. They differ by *effort*, which the
# gateway fixes (fast=low, balanced=high, deep=xhigh) so that "deep" cannot
# mean two different things in two deployments. Point a tier at a different
# model only as a deliberate choice — never as a silent cost downgrade.
MODEL_FAST=claude-opus-5
MODEL_BALANCED=claude-opus-5
MODEL_DEEP=claude-opus-5
# Hard ceiling on a single unstreamed response. Not the run's token budget —
# that spans every call in a run and belongs to the runtime.
MODEL_MAX_OUTPUT_TOKENS=16000
# ── Knowledge layer (retrieval) ─────────────────────────────────────────────
#
# The dense half of hybrid retrieval needs an embedding model. Three options,
# and the choice is worth making deliberately: all three return vectors and
# retrieval works with any of them, so a deployment running the wrong one looks
# exactly like one running the right one — until somebody phrases a question
# differently.
#
# ollama A model on this machine. Real semantics, no credential, no
# per-token cost, and no tenant text leaving the host. Start here.
#
# brew install ollama
# ollama pull nomic-embed-text
#
# then EMBED_PROVIDER=ollama.
#
# voyage Hosted, and better on subtle retrieval over a large messy corpus.
# Needs VOYAGE_API_KEY. Anthropic does not serve embeddings, so
# this is a separate credential.
#
# lexical A deterministic stand-in that hashes words into a vector. NOT
# semantic — "annual leave" and "time off" are unrelated to it. It
# exists so the permission filter and the citation path can be
# tested without a network. Startup REFUSES it when
# APP_ENV=production.
#
# Leave EMBED_PROVIDER empty and the choice is inferred from what is set,
# preferring the local model. With nothing configured at all, retrieval runs
# keyword-only and says so on every result.
#
# CHANGING PROVIDER MEANS RE-EMBEDDING. Vectors from two models are not
# comparable, and every chunk records which model produced it — so after a
# switch the old vectors are simply not searched, and retrieval silently drops
# to keyword-only until you run:
#
# make reembed ORG=<slug>
#
EMBED_PROVIDER=ollama
EMBED_BASE_URL=http://localhost:11434
EMBED_MODEL=nomic-embed-text
EMBED_DIMENSIONS=768
# Only for EMBED_PROVIDER=voyage.
VOYAGE_API_KEY=
# Legacy switch for the stand-in. EMBED_PROVIDER=lexical is the current spelling.
EMBED_USE_LEXICAL=false