docker-compose.yml
Rama main · réplica de lectura del repositorio original.
# 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.