# TRANSITION (rename to killBottleneck): variables are named KB_*, but anyone # still holding old FLOWMAP_* names in .env must keep working — hence the # ${KB_X:-${FLOWMAP_X:-default}} form: new name wins, then old, then the default. # # The server understands the old name on its own too (helpers.js:env), but only # for variables that actually reach the container — and compose only lets through # what is listed here. So the two layers serve different users: this one handles # `docker compose` with an old .env, the one in the server handles # `docker run -e FLOWMAP_…`. # (It looks like the same thing handled twice. It isn't. Don't remove either # without verifying both.) services: killbottleneck: build: context: . args: # Instance version. Release archives ship with the real number stamped # in below, so there is nothing to set here. Without it the value stays # "dev" and the update check is not offered — exactly what we want for # work in progress. (Dev repo only: staging deploys go through # nasadit-staging.sh, which stamps the number — don't run compose by hand.) KB_VERSION: ${KB_VERSION:-v1.0.1} # Container name. Override with KB_NAME when running two instances side by # side on one host (a hard-coded name made the second stack collide). container_name: ${KB_NAME:-killbottleneck} ports: - "${KB_PORT:-${FLOWMAP_PORT:-8090}}:8090" volumes: - ./pb_data:/app/pb_data environment: # Instance time zone: the recurring-template cron creates projects in the # client's LOCAL time (both weekday and hour). Default UTC = no change; a # client in another zone sets e.g. Europe/Prague. Requires tzdata in the # image (see Dockerfile). TZ: ${TZ:-UTC} # Hour of automatic creation (0–23, local TZ), default 5. KB_AUTO_HOUR: ${KB_AUTO_HOUR:-${FLOWMAP_AUTO_HOUR:-5}} # Update check. The signed-in user's BROWSER asks the GitHub API; the # instance itself sends nothing anywhere (no telemetry). 0 = turn off. KB_UPDATE_CHECK: ${KB_UPDATE_CHECK:-1} # Where releases are read from (forks/tests only; empty = official repo). KB_UPDATE_REPO: ${KB_UPDATE_REPO:-} # 1 = offer pre-releases (release candidates, -rc) too; default 0 = stable only. KB_UPDATE_PRERELEASE: ${KB_UPDATE_PRERELEASE:-0} # Public address of the instance — ONLY for automations (AI agent / cron on # a goal). killBottleneck sends it to the agent as callback_url to report back # to when it finishes. Without it the "Application URL" from the PocketBase # settings is used, which after installation is http://localhost:8090 — an n8n # running elsewhere would then call itself. KB_PUBLIC_URL: ${KB_PUBLIC_URL:-${FLOWMAP_PUBLIC_URL:-}} # How many minutes an automation may run before the watchdog fails it. KB_AGENT_TIMEOUT_MIN: ${KB_AGENT_TIMEOUT_MIN:-${FLOWMAP_AGENT_TIMEOUT_MIN:-90}} # Allow the agent webhook into a PRIVATE network (self-host where n8n runs # on the same LAN). Disabled by default — otherwise the server could be used # to scan the internal network. KB_ALLOW_PRIVATE_WEBHOOKS: ${KB_ALLOW_PRIVATE_WEBHOOKS:-${FLOWMAP_ALLOW_PRIVATE_WEBHOOKS:-}} # Hosted instance (at a provider, next to other customers): stops the # administrator pointing the AI service into a private network. On your own # server leave this empty — a local Ollama is the normal setup there. KB_HOSTED: ${KB_HOSTED:-${FLOWMAP_HOSTED:-}} # Server-side rate limits per client IP (sign-in 20 attempts / 60 s, record creation # 20 / 5 s, 300 API requests / 10 s) — slows down password guessing. On by default, # 0 = off. Behind a reverse proxy (Caddy, nginx) set KB_TRUSTED_PROXY_HEADER to the # header the proxy fills with the client's IP (X-Forwarded-For), otherwise the # whole company counts as one client. Without a proxy leave it empty. KB_RATE_LIMITS: ${KB_RATE_LIMITS:-} KB_TRUSTED_PROXY_HEADER: ${KB_TRUSTED_PROXY_HEADER:-} # Cap on uploaded attachments across the whole instance, in MB. Empty = no # limit, 0 = uploading disabled (attachments as links only). KB_FILES_MB: ${KB_FILES_MB:-${FLOWMAP_FILES_MB:-}} # Assistant pages (HTML artifacts): switch, count per user, size in kB, token cap and page-writer timeout KB_PAGES: ${KB_PAGES:-} KB_PAGES_MAX: ${KB_PAGES_MAX:-} KB_PAGE_KB: ${KB_PAGE_KB:-} KB_PAGE_NUM_PREDICT: ${KB_PAGE_NUM_PREDICT:-} KB_PAGE_TIMEOUT: ${KB_PAGE_TIMEOUT:-} KB_AI_PAGE_VAHA: ${KB_AI_PAGE_VAHA:-} # Backwards compatibility: an instance where e-mails already arrive keeps # them by setting this to 1, even after per-user notification preferences. KB_NOTIFY_EMAIL_DEFAULT: ${KB_NOTIFY_EMAIL_DEFAULT:-${FLOWMAP_NOTIFY_EMAIL_DEFAULT:-}} # Invitation e-mails when sharing with an address without an account — daily caps # per sharing user and per instance (empty = 20 / 50). KB_SHARE_INVITE_DAILY_CAP: ${KB_SHARE_INVITE_DAILY_CAP:-} KB_SHARE_INVITE_INSTANCE_CAP: ${KB_SHARE_INVITE_INSTANCE_CAP:-} # Hour of deadline notices (0–23, local TZ), default 7. KB_DEADLINE_HOUR: ${KB_DEADLINE_HOUR:-${FLOWMAP_DEADLINE_HOUR:-7}} # Catch-up window (hours) for timed reminders after an outage, default 48. KB_REMINDER_CATCHUP_H: ${KB_REMINDER_CATCHUP_H:-48} # Hour of the daily e-mail digest (0–23, local TZ), default 8. KB_NOTIFY_DIGEST_HOUR: ${KB_NOTIFY_DIGEST_HOUR:-${FLOWMAP_NOTIFY_DIGEST_HOUR:-8}} # Come-back e-mails for users who have not signed in: 0 = off (default on # when SMTP is set), hour of sending (0–23, local TZ, default 9), ladder of # days without sign-in (default 3,7,14,30 — one e-mail per step, silence after # the last one until the next sign-in), daily cap per instance (default 10). KB_NAVRAT_MAILY: ${KB_NAVRAT_MAILY:-} KB_NAVRAT_HODINA: ${KB_NAVRAT_HODINA:-} KB_NAVRAT_DNY: ${KB_NAVRAT_DNY:-} KB_NAVRAT_MAX_DENNE: ${KB_NAVRAT_MAX_DENNE:-} # Notification budget per recipient per day — a runaway automation must not # bury someone. Above the in-app cap the rows collapse into one growing # summary; above the e-mail cap the e-mails are dropped. No "unlimited". KB_NOTIFY_DAILY_CAP: ${KB_NOTIFY_DAILY_CAP:-${FLOWMAP_NOTIFY_DAILY_CAP:-50}} KB_NOTIFY_EMAIL_DAILY_CAP: ${KB_NOTIFY_EMAIL_DAILY_CAP:-${FLOWMAP_NOTIFY_EMAIL_DAILY_CAP:-10}} # Minutes over which notices for the same recipient are merged into one. KB_NOTIFY_COALESCE_MIN: ${KB_NOTIFY_COALESCE_MIN:-${FLOWMAP_NOTIFY_COALESCE_MIN:-10}} # Create the sample map for every new user? 0 = start with an empty account. KB_UVODNI_MAPA: ${KB_UVODNI_MAPA:-${FLOWMAP_UVODNI_MAPA:-1}} # Ask the first admin what the instance is for (company / family / solo)? # 0 = never ask (company). Off automatically when KB_UVODNI_MAPA=0. KB_PURPOSE_ASK: ${KB_PURPOSE_ASK:-} KB_START_RELAX: ${KB_START_RELAX:-} # AI switch: none = no AI | openai = any OpenAI-compatible API (OpenAI, # OpenRouter, Groq, vLLM, LM Studio...) | ollama = your own local model # | api = remote AI service (URL + token) | custom = your own endpoint KB_AI_PROVIDER: ${KB_AI_PROVIDER:-${FLOWMAP_AI_PROVIDER:-none}} KB_AI_URL: ${KB_AI_URL:-${FLOWMAP_AI_URL:-}} KB_AI_TOKEN: ${KB_AI_TOKEN:-${FLOWMAP_AI_TOKEN:-}} KB_AI_MODEL: ${KB_AI_MODEL:-${FLOWMAP_AI_MODEL:-}} # provider=openai only: extra JSON fields for every chat request, e.g. # {"reasoning_effort":"none"} — the only way to switch reasoning off at # the provider (same idea as KB_CHAT_OPENAI_EXTRA below). KB_AI_OPENAI_EXTRA: ${KB_AI_OPENAI_EXTRA:-} # reasoning models (o-series, gpt-5*): reserve for reasoning + reasoning_effort (defaults 4000 / low) KB_AI_REASONING_BUDGET: ${KB_AI_REASONING_BUDGET:-} KB_AI_REASONING_EFFORT: ${KB_AI_REASONING_EFFORT:-} KB_AI_TRANSCRIBE_URL: ${KB_AI_TRANSCRIBE_URL:-${FLOWMAP_AI_TRANSCRIBE_URL:-}} # provider=openai only: transcription model (default whisper-1) KB_AI_TRANSCRIBE_MODEL: ${KB_AI_TRANSCRIBE_MODEL:-${FLOWMAP_AI_TRANSCRIBE_MODEL:-}} # provider=openai only: cap of AI operations per person per hour (money!) KB_AI_MAX_PER_HOUR: ${KB_AI_MAX_PER_HOUR:-${FLOWMAP_AI_MAX_PER_HOUR:-60}} KB_AI_MAX_TRANSCRIBE_PER_HOUR: ${KB_AI_MAX_TRANSCRIBE_PER_HOUR:-${FLOWMAP_AI_MAX_TRANSCRIBE_PER_HOUR:-20}} # Daily AI summaries (dashboard on /tasks): hour of morning generation # (0–23, local TZ, default 6) plus an optional dedicated model JUST for # summaries — when KB_SUMMARY_PROVIDER is set, summaries bypass the general # AI config (a smaller/faster model suits short formatted text). KB_SUMMARY_HOUR: ${KB_SUMMARY_HOUR:-${FLOWMAP_SUMMARY_HOUR:-6}} # Morning generation only for accounts signed in within the last N days # (0 = everyone); others get theirs when they open the app. KB_SUMMARY_ACTIVE_DAYS: ${KB_SUMMARY_ACTIVE_DAYS:-${FLOWMAP_SUMMARY_ACTIVE_DAYS:-14}} KB_SUMMARY_PROVIDER: ${KB_SUMMARY_PROVIDER:-${FLOWMAP_SUMMARY_PROVIDER:-}} KB_SUMMARY_URL: ${KB_SUMMARY_URL:-${FLOWMAP_SUMMARY_URL:-}} KB_SUMMARY_MODEL: ${KB_SUMMARY_MODEL:-${FLOWMAP_SUMMARY_MODEL:-}} KB_SUMMARY_TOKEN: ${KB_SUMMARY_TOKEN:-${FLOWMAP_SUMMARY_TOKEN:-}} # AI chat on the side (assistant): own model, else falls back to KB_SUMMARY_*, # then to the general AI settings. Compose passes ONLY listed variables. KB_CHAT_PROVIDER: ${KB_CHAT_PROVIDER:-} KB_CHAT_URL: ${KB_CHAT_URL:-} KB_CHAT_MODEL: ${KB_CHAT_MODEL:-} # Weekly AI credit quota of the organization (hosting/plan); the admin overrides it in the app, empty = no cap KB_AI_KVOTA_TYDEN: ${KB_AI_KVOTA_TYDEN:-} KB_CHAT_TOKEN: ${KB_CHAT_TOKEN:-} KB_CHAT_THINK: ${KB_CHAT_THINK:-} KB_CHAT_NUM_CTX: ${KB_CHAT_NUM_CTX:-} KB_CHAT_OPENAI_EXTRA: ${KB_CHAT_OPENAI_EXTRA:-} # Tool schemas sent to the assistant: auto (default) = GPT models get every tool, others # only the groups the conversation needs (saves tokens); groups = always only the groups; # all = every tool on every call. KB_CHAT_TOOLS: ${KB_CHAT_TOOLS:-} # Hybrid: a light model for reading/briefings, the main one for writes (KB_CHAT_HYBRID=mode,classifier,handover) KB_CHAT_HYBRID: ${KB_CHAT_HYBRID:-} # Card confirmation: app = for simple actions the app replies "Done." without calling the model (default), model = the model always replies KB_CHAT_POTVRZENI: ${KB_CHAT_POTVRZENI:-} KB_CHAT_MAX_TAHU: ${KB_CHAT_MAX_TAHU:-} KB_CHAT_MAX_TOOL_STARE: ${KB_CHAT_MAX_TOOL_STARE:-} KB_CHAT_LIGHT_PROVIDER: ${KB_CHAT_LIGHT_PROVIDER:-} KB_CHAT_LIGHT_URL: ${KB_CHAT_LIGHT_URL:-} KB_CHAT_LIGHT_MODEL: ${KB_CHAT_LIGHT_MODEL:-} KB_CHAT_LIGHT_TOKEN: ${KB_CHAT_LIGHT_TOKEN:-} KB_CHAT_LIGHT_THINK: ${KB_CHAT_LIGHT_THINK:-} KB_CHAT_LIGHT_NUM_CTX: ${KB_CHAT_LIGHT_NUM_CTX:-} KB_CHAT_LIGHT_OPENAI_EXTRA: ${KB_CHAT_LIGHT_OPENAI_EXTRA:-} # Pictures pasted into the assistant (Ctrl+V): transcription on your own model that can SEE # images (gpt-oss and qwen 3-bit cannot). Without KB_VISION_PROVIDER pictures are not accepted. # Measured and approved 16 Sep 2026: gemma4:26b (dense text and phone notes alike). KB_VISION_PROVIDER: ${KB_VISION_PROVIDER:-} KB_VISION_URL: ${KB_VISION_URL:-} KB_VISION_MODEL: ${KB_VISION_MODEL:-} KB_VISION_TOKEN: ${KB_VISION_TOKEN:-} KB_VISION_NUM_CTX: ${KB_VISION_NUM_CTX:-} KB_VISION_OPENAI_EXTRA: ${KB_VISION_OPENAI_EXTRA:-} # Fallback when the primary vision model is busy (OpenAI-compatible; note that DeepSeek REJECTS images) KB_VISION_ZALOHA_PROVIDER: ${KB_VISION_ZALOHA_PROVIDER:-} KB_VISION_ZALOHA_URL: ${KB_VISION_ZALOHA_URL:-} KB_VISION_ZALOHA_MODEL: ${KB_VISION_ZALOHA_MODEL:-} KB_VISION_ZALOHA_TOKEN: ${KB_VISION_ZALOHA_TOKEN:-} KB_VISION_ZALOHA_NUM_CTX: ${KB_VISION_ZALOHA_NUM_CTX:-} KB_VISION_ZALOHA_OPENAI_EXTRA: ${KB_VISION_ZALOHA_OPENAI_EXTRA:-} # Seconds per attempt (default 45) · retries on the first model (default 0) · picture cap in MB (1.2) KB_VISION_TIMEOUT: ${KB_VISION_TIMEOUT:-} KB_VISION_POKUSY: ${KB_VISION_POKUSY:-} KB_CHAT_MAX_IMG_MB: ${KB_CHAT_MAX_IMG_MB:-} # How many picture previews the history keeps (3) · how many turns of the hourly cap a picture costs (3) KB_CHAT_NAHLEDU: ${KB_CHAT_NAHLEDU:-} KB_AI_IMG_VAHA: ${KB_AI_IMG_VAHA:-} # How many turns of the hourly cap a turn with PDF text costs (2); the PDF itself is processed in the browser KB_AI_PDF_VAHA: ${KB_AI_PDF_VAHA:-} # Voice notes in the assistant: your own OpenAI-shaped transcription (speaches, whisper.cpp, OpenAI…), # otherwise the general AI; none = off. Max length (s), max recording (MB), weight in the hourly cap (2). KB_TRANSCRIBE_PROVIDER: ${KB_TRANSCRIBE_PROVIDER:-} KB_TRANSCRIBE_URL: ${KB_TRANSCRIBE_URL:-} KB_TRANSCRIBE_MODEL: ${KB_TRANSCRIBE_MODEL:-} KB_TRANSCRIBE_TOKEN: ${KB_TRANSCRIBE_TOKEN:-} # Silence filter (speaches/faster-whisper): on by default for your own transcriber other than OpenAI/Groq; 0 = off, 1 = always KB_TRANSCRIBE_VAD: ${KB_TRANSCRIBE_VAD:-} KB_CHAT_HLAS_MAX_S: ${KB_CHAT_HLAS_MAX_S:-} KB_CHAT_MAX_AUDIO_MB: ${KB_CHAT_MAX_AUDIO_MB:-} KB_AI_HLAS_VAHA: ${KB_AI_HLAS_VAHA:-} # Drawing pictures in the assistant (FLUX via the AI gateway, mode=image): gateway address (…/v1/advisor) and token, # or KB_IMAGE_PROVIDER=api = the gateway from KB_AI_*; none = off. KB_IMAGE_TEXT=1 allows pictures with text # (FLUX 3). Weight of a picture in the hourly cap (2). KB_OBRAZEK_KREDITY = fixed price of a picture in credits # (empty = the model's reference price; 0 = pictures cost no credits), KB_OBRAZKY_TYDEN = weekly picture cap of the instance. KB_OBRAZEK_KREDITY: ${KB_OBRAZEK_KREDITY:-} KB_OBRAZKY_TYDEN: ${KB_OBRAZKY_TYDEN:-} KB_IMAGE_PROVIDER: ${KB_IMAGE_PROVIDER:-} KB_IMAGE_URL: ${KB_IMAGE_URL:-} KB_IMAGE_TOKEN: ${KB_IMAGE_TOKEN:-} KB_IMAGE_TEXT: ${KB_IMAGE_TEXT:-} # Your own service: KB_IMAGE_PROVIDER=openai + KB_IMAGE_TOKEN (key), KB_IMAGE_URL (default api.openai.com/v1), KB_IMAGE_MODEL KB_IMAGE_MODEL: ${KB_IMAGE_MODEL:-} KB_AI_GEN_VAHA: ${KB_AI_GEN_VAHA:-} # Daily summary by e-mail (the user turns it on in Notifications): cap of mails per instance and day (default 100) KB_SUMAR_EMAIL_MAX: ${KB_SUMAR_EMAIL_MAX:-} # Where "Report a bug or idea" (under the person icon) sends to. EMPTY BY # DEFAULT — with no address the route returns 404 and the item is not even # shown, so a self-hosted instance never sends anything out on its own. # Set it to your own address if you want your users' reports. KB_REPORT_TO: ${KB_REPORT_TO:-} # Instance registration key: when set, every self-registration requires it # (hosted instances have a public domain). Empty = registration without a key. KB_SETUP_CODE: ${KB_SETUP_CODE:-${FLOWMAP_SETUP_CODE:-}} # Sign in with Google (optional): your own OAuth client from the Google Cloud # Console. When empty, the "Sign in with Google" button is not shown. KB_GOOGLE_CLIENT_ID: ${KB_GOOGLE_CLIENT_ID:-${FLOWMAP_GOOGLE_CLIENT_ID:-}} KB_GOOGLE_CLIENT_SECRET: ${KB_GOOGLE_CLIENT_SECRET:-${FLOWMAP_GOOGLE_CLIENT_SECRET:-}} KB_GOOGLE_PICKER_API_KEY: ${KB_GOOGLE_PICKER_API_KEY:-} restart: unless-stopped # Test-only switches (KB_TEST_CLOCK, KB_STUCK_DAYS, KB_RULES_DISABLED) are # deliberately NOT listed: they reach the server only via `docker run -e`. # Wiring to other services (reverse proxy, AI gateway on the same machine…) # goes into docker-compose.override.yml — see README.