# Настройки окружения Crank настраивается через переменные окружения. Один и тот же набор переменных используется при запуске из исходников и при запуске готовых Docker-образов. | Environment | Semantic path | Process | Type/unit | Default | Bounds | Sensitivity | Mode | |---|---|---|---|---|---|---|---| | `CRANK_DATABASE_URL` | `database.url` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` | | `POSTGRES_HOST` | `database.host` | `Shared` | `string/-` | `postgres` | `-` | `Internal` | `Effective` | | `POSTGRES_PORT` | `database.port` | `Shared` | `u16/port` | `5432` | `1..=65535` | `Public` | `Effective` | | `POSTGRES_DB` | `database.name` | `Shared` | `string/-` | `crank` | `-` | `Internal` | `Effective` | | `POSTGRES_USER` | `database.user` | `Shared` | `string/-` | `crank` | `-` | `Internal` | `Effective` | | `POSTGRES_PASSWORD` | `database.password` | `Shared` | `secret/-` | `configured` | `-` | `Secret` | `Effective` | | `POSTGRES_MAX_CONNECTIONS` | `database.pool.max_connections` | `Shared` | `u32/connections` | `20` | `1..=1024` | `Public` | `Effective` | | `POSTGRES_MIN_CONNECTIONS` | `database.pool.min_connections` | `Shared` | `u32/connections` | `2` | `0..=1024` | `Public` | `Effective` | | `POSTGRES_ACQUIRE_TIMEOUT_MS` | `database.pool.acquire_timeout_ms` | `Shared` | `u64/milliseconds` | `5000` | `1..=300000` | `Public` | `Effective` | | `POSTGRES_IDLE_TIMEOUT_MS` | `database.pool.idle_timeout_ms` | `Shared` | `u64/milliseconds` | `600000` | `1000..=86400000` | `Public` | `Effective` | | `POSTGRES_MAX_LIFETIME_MS` | `database.pool.max_lifetime_ms` | `Shared` | `u64/milliseconds` | `1800000` | `1000..=86400000` | `Public` | `Effective` | | `CRANK_MASTER_KEY` | `runtime.master_key` | `Shared` | `secret/-` | `required/blank` | `>=32` | `Secret` | `Effective` | | `CRANK_BASE_URL` | `runtime.base_url` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` | | `CRANK_RUNTIME_MAX_CONCURRENT_UNARY` | `runtime.max_concurrent_unary` | `Shared` | `u32/requests` | `64` | `1..=65535` | `Public` | `Effective` | | `CRANK_CACHE_BACKEND` | `cache.backend` | `Shared` | `enum/-` | `memory` | `-` | `Public` | `Effective` | | `CRANK_CACHE_URL` | `cache.url` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` | | `CRANK_CACHE_DEFAULT_TTL_MS` | `cache.default_ttl_ms` | `Shared` | `u64/milliseconds` | `blank` | `1..=86400000` | `Public` | `DeprecatedNoEffect` | | `CRANK_OUTBOUND_ALLOWED_HOSTS` | `outbound.allowed_hosts` | `Shared` | `host_list/-` | `` | `-` | `Internal` | `Effective` | | `CRANK_OUTBOUND_DENIED_HOSTS` | `outbound.denied_hosts` | `Shared` | `host_list/-` | `` | `-` | `Internal` | `Effective` | | `CRANK_OUTBOUND_MAX_REQUEST_BYTES` | `outbound.max_request_bytes` | `Shared` | `u64/bytes` | `4194304` | `1..=67108864` | `Public` | `Effective` | | `CRANK_OUTBOUND_MAX_RESPONSE_BYTES` | `outbound.max_response_bytes` | `Shared` | `u64/bytes` | `4194304` | `1..=67108864` | `Public` | `Effective` | | `CRANK_ENVIRONMENT` | `observability.environment` | `Shared` | `label/-` | `development` | `-` | `Public` | `Effective` | | `CRANK_LOG_LEVEL` | `observability.log_filter` | `Shared` | `string/-` | `blank` | `-` | `Public` | `Effective` | | `CRANK_SENTRY_DSN` | `observability.sentry_dsn` | `Shared` | `url/-` | `blank` | `-` | `Secret` | `Effective` | | `CRANK_METRICS_ENABLED` | `observability.metrics.enabled` | `Shared` | `bool/-` | `true` | `-` | `Public` | `Effective` | | `CRANK_METRICS_BEARER_TOKEN` | `observability.metrics.bearer_token` | `Shared` | `secret/-` | `blank` | `-` | `Secret` | `Effective` | | `OTEL_EXPORTER_OTLP_ENDPOINT` | `observability.otlp.endpoint` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` | | `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | `observability.otlp.traces_endpoint` | `Shared` | `url/-` | `blank` | `-` | `Internal` | `Effective` | | `OTEL_EXPORTER_OTLP_PROTOCOL` | `observability.otlp.protocol` | `Shared` | `enum/-` | `http/protobuf` | `-` | `Public` | `Effective` | | `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | `observability.otlp.traces_protocol` | `Shared` | `enum/-` | `blank` | `-` | `Public` | `Effective` | | `OTEL_EXPORTER_OTLP_TIMEOUT` | `observability.otlp.timeout` | `Shared` | `duration/milliseconds` | `10000` | `1..=300000` | `Public` | `Effective` | | `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` | `observability.otlp.traces_timeout` | `Shared` | `duration/milliseconds` | `blank` | `1..=300000` | `Public` | `Effective` | | `OTEL_EXPORTER_OTLP_HEADERS` | `observability.otlp.headers` | `Shared` | `headers/-` | `blank` | `-` | `Secret` | `Effective` | | `OTEL_EXPORTER_OTLP_TRACES_HEADERS` | `observability.otlp.traces_headers` | `Shared` | `headers/-` | `blank` | `-` | `Secret` | `Effective` | | `OTEL_BSP_MAX_QUEUE_SIZE` | `observability.otlp.max_queue_size` | `Shared` | `u32/spans` | `2048` | `1..=65536` | `Public` | `Effective` | | `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` | `observability.otlp.max_export_batch_size` | `Shared` | `u32/spans` | `512` | `1..=65536` | `Public` | `Effective` | | `OTEL_BSP_SCHEDULE_DELAY` | `observability.otlp.schedule_delay` | `Shared` | `duration/milliseconds` | `5000` | `1..=300000` | `Public` | `Effective` | | `OTEL_BSP_EXPORT_TIMEOUT` | `observability.otlp.export_timeout` | `Shared` | `duration/milliseconds` | `30000` | `1..=300000` | `Public` | `Effective` | | `CRANK_ADMIN_BIND` | `admin.bind` | `AdminApi` | `socket/-` | `0.0.0.0:3001` | `-` | `Internal` | `Effective` | | `CRANK_ADMIN_METRICS_BIND` | `admin.metrics_bind` | `AdminApi` | `socket/-` | `127.0.0.1:9464` | `-` | `Internal` | `Effective` | | `CRANK_STORAGE_ROOT` | `admin.storage_root` | `AdminApi` | `absolute_path/-` | `/var/lib/crank/storage` | `-` | `Internal` | `Effective` | | `CRANK_ADMIN_RATE_LIMIT_RPS` | `admin.rate_limit.rps` | `AdminApi` | `u32/requests_per_second` | `30` | `1..=100000` | `Public` | `Effective` | | `CRANK_ADMIN_RATE_LIMIT_BURST` | `admin.rate_limit.burst` | `AdminApi` | `u32/requests` | `60` | `1..=1000000` | `Public` | `Effective` | | `CRANK_INVOCATION_LOG_RETENTION_DAYS` | `admin.invocation_log_retention_days` | `AdminApi` | `u32/days` | `30` | `1..=36500` | `Public` | `Effective` | | `CRANK_SESSION_SECRET` | `admin.session.secret` | `AdminApi` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` | | `CRANK_PASSWORD_PEPPER` | `admin.password_pepper` | `AdminApi` | `secret/-` | `required/blank` | `-` | `Secret` | `Effective` | | `CRANK_SESSION_TTL_HOURS` | `admin.session.ttl_hours` | `AdminApi` | `u32/hours` | `24` | `1..=8760` | `Public` | `Effective` | | `CRANK_TRUST_FORWARDED_HEADERS` | `admin.trust_forwarded_headers` | `AdminApi` | `bool/-` | `blank` | `-` | `Public` | `DeprecatedNoEffect` | | `CRANK_TRUSTED_PROXY_IPS` | `admin.trusted_proxy_ips` | `AdminApi` | `ip_list/-` | `` | `-` | `Internal` | `Effective` | | `CRANK_BOOTSTRAP_ADMIN_EMAIL` | `admin.bootstrap.email` | `AdminApi` | `string/-` | `required/blank` | `-` | `Internal` | `Effective` | | `CRANK_BOOTSTRAP_ADMIN_PASSWORD` | `admin.bootstrap.password` | `AdminApi` | `secret/-` | `blank` | `-` | `Secret` | `Effective` | | `CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME` | `admin.bootstrap.display_name` | `AdminApi` | `string/-` | `Crank Owner` | `-` | `Internal` | `Effective` | | `CRANK_DEMO_SEED` | `admin.demo_seed` | `AdminApi` | `bool/-` | `false` | `-` | `Public` | `Effective` | | `CRANK_MCP_BIND` | `mcp.bind` | `McpServer` | `socket/-` | `0.0.0.0:3002` | `-` | `Internal` | `Effective` | | `CRANK_MCP_METRICS_BIND` | `mcp.metrics_bind` | `McpServer` | `socket/-` | `127.0.0.1:9465` | `-` | `Internal` | `Effective` | | `CRANK_MCP_REFRESH_MS` | `mcp.refresh_ms` | `McpServer` | `u64/milliseconds` | `5000` | `100..=3600000` | `Public` | `Effective` | | `CRANK_MCP_RATE_LIMIT_RPS` | `mcp.rate_limit.rps` | `McpServer` | `u32/requests_per_second` | `60` | `1..=100000` | `Public` | `Effective` | | `CRANK_MCP_RATE_LIMIT_BURST` | `mcp.rate_limit.burst` | `McpServer` | `u32/requests` | `120` | `1..=1000000` | `Public` | `Effective` | | `CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS` | `runtime.max_concurrent_sessions` | `McpServer` | `u32/sessions` | `16` | `1..=65535` | `Public` | `Effective` | ## PostgreSQL Обязательные параметры: - `POSTGRES_HOST` - `POSTGRES_PORT` - `POSTGRES_DB` - `POSTGRES_USER` - `POSTGRES_PASSWORD` Параметры пула соединений: - `POSTGRES_MAX_CONNECTIONS`, по умолчанию `20`; - `POSTGRES_MIN_CONNECTIONS`, по умолчанию `2`; - `POSTGRES_ACQUIRE_TIMEOUT_MS`, по умолчанию `5000`; - `POSTGRES_IDLE_TIMEOUT_MS`, по умолчанию `600000`; - `POSTGRES_MAX_LIFETIME_MS`, по умолчанию `1800000`. Если используется PgBouncer, укажите его адрес в `POSTGRES_HOST` и порт в `POSTGRES_PORT`. `CRANK_DATABASE_URL` — compatibility-форма для существующих установок. Она содержит credentials и поэтому никогда не выводится в diagnostics или fingerprint. URL нельзя смешивать с явно заданными `POSTGRES_HOST`, `POSTGRES_PORT`, `POSTGRES_DB`, `POSTGRES_USER` или `POSTGRES_PASSWORD`: конфликт останавливает startup. Для новых установок canonical-формой остаются раздельные `POSTGRES_*` параметры. ## HTTP-сервисы - `CRANK_ADMIN_BIND` - адрес `admin-api`, например `0.0.0.0:3001`. - `CRANK_MCP_BIND` - адрес `mcp-server`, например `0.0.0.0:3002`. - `CRANK_PUBLISH_BIND` - адрес публикации портов в Docker Compose. - `CRANK_BASE_URL` - публичный URL веб-интерфейса. Для сервера за reverse proxy обычно подходит: ```env CRANK_PUBLISH_BIND=127.0.0.1 ``` Если reverse proxy работает на другом host: ```env CRANK_PUBLISH_BIND=0.0.0.0 CRANK_TRUSTED_PROXY_IPS=192.0.2.10 ``` `CRANK_TRUSTED_PROXY_IPS` — comma-separated allowlist immediate peer IPs. Только эти peers могут задавать `X-Real-IP`/`X-Forwarded-For`; от всех остальных клиентов forwarding headers игнорируются. Старый `CRANK_TRUST_FORWARDED_HEADERS` оставлен только как deprecated/no-effect совместимость и не включает доверие к proxy. ## Авторизация администратора - `CRANK_SESSION_SECRET` - ключ подписи браузерных сессий. - `CRANK_PASSWORD_PEPPER` - дополнительный секрет для хэширования паролей. - `CRANK_SESSION_TTL_HOURS` - срок жизни сессии в часах. - `CRANK_BOOTSTRAP_ADMIN_EMAIL` - email для локального bootstrap-контракта первого пользователя. - `CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME` - отображаемое имя первого пользователя. - `CRANK_BOOTSTRAP_ADMIN_PASSWORD` - deprecated compatibility-поле для старых dev/demo запусков; production startup не должен создавать или обновлять администратора из этого значения. Первый production-admin создается локальной операторской командой, а не статическим startup-паролем: ```bash crank-migrate admin-auth bootstrap-create --email owner@example.local ``` Команда выводит одноразовый bootstrap token. Оператор вводит его на странице логина и задает первый пароль. Повторное использование token отклоняется. ## Шифрование секретов - `CRANK_MASTER_KEY` - ключ шифрования сохраненных секретов. Минимум 32 bytes; генерируйте случайное значение и храните его вне product backup set. Этот ключ нужен `admin-api` и `mcp-server`. Оба процесса до readiness сравнивают non-secret fingerprint/epoch с PostgreSQL identity registry. Если ключ не совпадает с активной identity, startup завершается bounded diagnostic `master_key_identity_mismatch` без вывода raw key, fingerprint fragment, ciphertext или decrypt details. Не меняйте значение `CRANK_MASTER_KEY` напрямую. Используйте операторскую процедуру: ```bash crank-migrate master-key preflight --current-key-file /secure/current.key --target-key-file /secure/target.key crank-migrate master-key rotate --current-key-file /secure/current.key --target-key-file /secure/target.key crank-migrate master-key verify --target-key-file /secure/target.key crank-migrate master-key promote --target-key-file /secure/target.key ``` Команда читает database config через `CRANK_DATABASE_URL`/`POSTGRES_*`, а master keys — только из указанных локальных файлов. Значения ключей не должны передаваться как shell arguments или попадать в committed `.env`. ## Демо-данные - `CRANK_DEMO_SEED=true` создает пример Frankfurter при старте. - `CRANK_DEMO_SEED=false` отключает demo seed. Demo seed идемпотентный: повторный старт не создает дубликаты. В стандартном примере создаются upstream `Frankfurter`, операция `frankfurter_latest_rate`, агент `currency-rates` и пример API-ключа. ## MCP - `CRANK_MCP_REFRESH_MS` - как часто `mcp-server` обновляет опубликованный каталог инструментов. - `CRANK_MCP_RATE_LIMIT_RPS` - лимит запросов в секунду. - `CRANK_MCP_RATE_LIMIT_BURST` - допустимый короткий всплеск запросов. ## Admin API - `CRANK_ADMIN_RATE_LIMIT_RPS` - лимит запросов в секунду. - `CRANK_ADMIN_RATE_LIMIT_BURST` - допустимый короткий всплеск запросов. ## Runtime limits - `CRANK_RUNTIME_MAX_CONCURRENT_UNARY` - `CRANK_RUNTIME_MAX_CONCURRENT_SESSIONS` Эти настройки ограничивают параллельное выполнение unary-запросов и MCP-сессий. ## Исходящие HTTP-запросы По умолчанию Crank обращается только к публичным IP-адресам. Локальные, частные, служебные и link-local сети блокируются после разрешения DNS-имени. Автоматические HTTP-перенаправления и системный прокси отключены. - `CRANK_OUTBOUND_ALLOWED_HOSTS` - исключения для разрешённых внутренних узлов через запятую. Публичные узлы разрешены независимо от этого списка. Для внутреннего API укажите его имя или IP явно. Поддерживаются маски вида `*.example.internal`. - `CRANK_OUTBOUND_DENIED_HOSTS` - список узлов, запрещённых независимо от списка разрешённых. - `CRANK_OUTBOUND_MAX_REQUEST_BYTES` - максимальный размер JSON-тела запроса к внешнему API; проверяется до отправки байтов. - `CRANK_OUTBOUND_MAX_RESPONSE_BYTES` - максимальный размер ответа внешнего API; по умолчанию `4194304` байт. Пример доступа только к двум внутренним API: ```env CRANK_OUTBOUND_ALLOWED_HOSTS=crm.example.internal,192.168.1.50 CRANK_OUTBOUND_DENIED_HOSTS=metadata.example.internal CRANK_OUTBOUND_MAX_REQUEST_BYTES=4194304 CRANK_OUTBOUND_MAX_RESPONSE_BYTES=4194304 ``` Одинаковые значения должны передаваться в `admin-api` и `mcp-server`: первый проверяет операции при сохранении, второй применяет политику при каждом соединении. Максимальный `execution_config.timeout_ms` операции равен `300000` мс. ## Кэш По умолчанию Crank работает без внешнего кэша: ```env CRANK_CACHE_BACKEND=memory ``` Для Valkey или Redis: ```env CRANK_CACHE_BACKEND=valkey CRANK_CACHE_URL=redis://valkey:6379/0 ``` `CRANK_CACHE_DEFAULT_TTL_MS` в прежних шаблонах не имел runtime consumer. Непустое значение теперь отклоняется как deprecated no-effect configuration; TTL задаётся владельцем конкретного cache operation. Внешний кэш используется для служебного краткоживущего состояния: rate limiting, replay guard и опубликованные каталоги MCP-инструментов. ## Логи - `CRANK_ENVIRONMENT` - короткая метка окружения. При локальном запуске по умолчанию используется `development`, Community deployment явно передаёт `production`. - `CRANK_LOG_LEVEL` - фильтр `tracing`, например `info`, `debug`, `warn` или `admin_api=debug,tower_http=info`. `admin-api` и `mcp-server` пишут в stdout по одному JSON-объекту на строку. Поля с паролями, токенами, ключами, cookie, authorization, query, полным телом или результатом очищаются до сериализации. `crank-migrate` использует только database-поля этого контракта. Ему не требуются и не должны передаваться master key, session/bootstrap secrets, runtime, metrics или OTLP configuration. Команды и recovery contract описаны в [migrations.md](migrations.md). Пример: ```env CRANK_ENVIRONMENT=development CRANK_LOG_LEVEL=info ``` Внешний сборщик журналов не обязателен. Его отсутствие не влияет на `/health` и `/ready`. `CRANK_INVOCATION_LOG_RETENTION_DAYS` задаёт срок хранения подробной истории вызовов в днях. Допустимый диапазон: от `1` до `36500`, значение по умолчанию: `30`. Значение вне диапазона останавливает запуск до создания фоновой задачи очистки. ## Критические ошибки - `CRANK_SENTRY_DSN` — DSN внешнего Sentry-совместимого приёмника. Пустое или отсутствующее значение отключает канал. Неверное непустое значение останавливает запуск безопасной ошибкой без вывода DSN. Оба сервиса используют одно значение, но передают собственные `service`, `release` и `environment`. ```env CRANK_SENTRY_DSN= ``` Community не разворачивает GlitchTip или другой приёмник. Подробный состав события и правила очистки приведены в [документе о наблюдаемости](observability.md). ## Prometheus - `CRANK_METRICS_ENABLED` — включает отдельные listener-ы, по умолчанию `true`; - `CRANK_ADMIN_METRICS_BIND` — адрес показателей `admin-api`, по умолчанию `127.0.0.1:9464`; - `CRANK_MCP_METRICS_BIND` — адрес показателей `mcp-server`, по умолчанию `127.0.0.1:9465`; - `CRANK_METRICS_BEARER_TOKEN` — отдельный токен, обязательный для любого non-loopback bind. Значения проверяются до запуска рабочих listener-ов. Для отключения поверхности: ```env CRANK_METRICS_ENABLED=false ``` Подробная модель доступа, список показателей и пример настройки сборщика приведены в [документе о наблюдаемости](observability.md). ## Распределённые трассы OTLP Crank экспортирует через OTLP только трассы. Метрики остаются в Prometheus, а эксплуатационные журналы — в stdout. Если оба endpoint пусты, tracer provider и фоновый экспортёр не создаются. - `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` — полный HTTP endpoint трасс; имеет приоритет; - `OTEL_EXPORTER_OTLP_ENDPOINT` — общий endpoint, к которому Crank добавляет `/v1/traces`; - `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` и резервный `OTEL_EXPORTER_OTLP_PROTOCOL` — только `http/protobuf`; - `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` и резервный `OTEL_EXPORTER_OTLP_TIMEOUT` — предел HTTP-запроса экспорта в миллисекундах; - `OTEL_EXPORTER_OTLP_TRACES_HEADERS` — заголовки только для трасс; - `OTEL_EXPORTER_OTLP_HEADERS` — резервные общие заголовки; - `OTEL_BSP_MAX_QUEUE_SIZE` — конечная очередь spans, по умолчанию `2048`; - `OTEL_BSP_MAX_EXPORT_BATCH_SIZE` — пакет, по умолчанию `512`, не больше очереди; - `OTEL_BSP_SCHEDULE_DELAY` — период отправки в миллисекундах, по умолчанию `5000`; - `OTEL_BSP_EXPORT_TIMEOUT` — совместимый предел пакетного экспортёра в миллисекундах, по умолчанию `30000`. Фактический HTTP-запрос экспорта ограничивается более строгим из `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT`/`OTEL_EXPORTER_OTLP_TIMEOUT` и `OTEL_BSP_EXPORT_TIMEOUT`. Это сохраняет оба верхних предела при работе стабильного потокового `BatchSpanProcessor`. Допустимы только HTTP/HTTPS URL без учётных данных, query и fragment. Некорректная явно заданная конфигурация останавливает запуск безопасной типизированной ошибкой, не содержащей значений окружения. ```env OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=https://otel.example.com/v1/traces OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf OTEL_EXPORTER_OTLP_TIMEOUT=10000 ``` Заголовки обычно содержат токен приёмника. В production их следует хранить в OpenBao как `OTEL_EXPORTER_OTLP_TRACES_HEADERS`; CD передаёт значение в runtime `.env`, но не выводит его в журнал. Не записывайте токен в репозиторий или Compose.