# Listen address for the Go server inside its container — override only if # you know why (e.g. a non-standard port mapping); the docker-compose.yml # port binding already assumes the default. LISTEN_ADDR=:3000 # Postgres (own database — separate from core's) POSTGRES_PASSWORD=change-me DATABASE_URL=postgres://production:change-me@postgres:5432/production?sslmode=disable # JWT — MUST equal core's JWT_SECRET exactly. Production has no login of its # own, it only verifies staff tokens core issued. JWT_SECRET=change-me-must-match-core-JWT_SECRET # MinIO (own instance — separate from core's) MINIO_ENDPOINT=minio:9000 MINIO_ACCESS_KEY=change-me MINIO_SECRET_KEY=change-me MINIO_BUCKET=production MINIO_USE_SSL=false # CORS (comma-separated origins the web frontend will be served from) CORS_ORIGINS=http://localhost:5173 # Core module registration — get both tokens via core's `POST /api/modules` # (owner-only, returned once). CORE_URL must be reachable from this # container (internal core address, not the public domain). CORE_URL=http://core-backend:3000 MODULE_NAME=production MODULE_TOKEN=change-me-token-from-core-POST-/api/modules # Lets core restart this process or flip it into maintenance mode # (internal/modulecontrol) — the "control_token" core's POST /api/modules # response returns alongside MODULE_TOKEN above. Unset disables both # actions entirely (authOK never succeeds against an empty stored secret). CORE_CONTROL_TOKEN=change-me-control-token-from-core-POST-/api/modules # Inbound webhook auth (internal/sale.Webhook) — online-store calls # POST /api/webhooks/online-store/sales with X-Module-Token set to its OWN # core module token, so this must equal the token core issued online-store # (not production's own MODULE_TOKEN above). Unset disables the endpoint # (fails closed, returns 503) rather than accepting unauthenticated writes. ONLINE_STORE_WEBHOOK_TOKEN=change-me-must-match-online-store-MODULE_TOKEN # Web build args (compile-time, baked into the JS bundle by Vite) — these are # what the *browser* calls, so loopback/public URLs, not the container-network # addresses above. Read by docker-compose.yml's web.build.args. WEB_CORE_URL=http://127.0.0.1:18090 WEB_PRODUCTION_URL=http://127.0.0.1:18091 # Business requisites for Счёт/Акт PDFs (internal/document, internal/pdfgen). # All optional — the business isn't legally registered yet, unset fields # render as clean placeholder blanks rather than breaking generation. Fill # these in once real registration details exist. BUSINESS_NAME= BUSINESS_INN= BUSINESS_KPP= BUSINESS_ADDRESS= BUSINESS_PHONE= BUSINESS_BANK_NAME= BUSINESS_BANK_ACCOUNT= BUSINESS_BANK_BIK= BUSINESS_BANK_CORR_ACCOUNT= # AI-приёмка (internal/aiintake, Phase 6) — free-text -> order-form field # extraction via Gemini's structured-output API (a Google AI Studio key, # free tier available, no card required as of this writing). Unset disables # the endpoint (fails closed, returns 503) rather than accepting requests it # can't fulfill. GEMINI_MODEL is overridable because Google's free-tier # flash-model lineup has moved fast (2.0 -> 2.5 -> 3.x observed within a # year) — bump this instead of the code default when the current one is # deprecated. GEMINI_API_KEY= GEMINI_MODEL=gemini-2.0-flash # IMEI-автозаполнение (internal/imei) — brand/model lookup by IMEI when # creating an order, HiCellTek's free-tier TAC API. Unset disables the # lookup silently (button stays but returns "not found"). IMEI_API_KEY= # Staff Telegram notifications (internal/notify, Phase 8) — new order/batch # created, status changes. Fire-and-forget: unset disables it silently # (no 503, nothing to fail — it's a convenience layer on top of the Kanban # board, not a delivery guarantee). TG_CHAT_ID is a single shared chat/group # for all staff, same pattern as site's lead bot. # # TG_API_BASE_URL points at a self-hosted Telegram Bot API server # (docker-compose.yml's telegram-bot-api service) instead of # api.telegram.org directly — by design, not a placeholder (see project # wiki). That server needs TELEGRAM_API_ID/TELEGRAM_API_HASH from # my.telegram.org (one-time manual registration, requires a phone number — # not something this codebase can automate) before it will even start; until # that's done, leave the telegram-bot-api service stopped and TG_BOT_TOKEN # as a placeholder — Send() fails soft exactly like it would against a real # but invalid token. TG_BOT_TOKEN= TG_CHAT_ID= TG_API_BASE_URL=http://telegram-bot-api:8081 # Credentials for docker-compose.yml's telegram-bot-api service itself (the # self-hosted server, not this Go app) — register an application at # https://my.telegram.org/apps to get these. Leave unset and the container # just exits on start; TG_BOT_TOKEN above stays a no-op placeholder either # way until both this and a real bot token from @BotFather are in place. TELEGRAM_API_ID= TELEGRAM_API_HASH= # Client-facing notifications (internal/clientnotify, internal/tgbot, # internal/smsgw — Phase 11). Reuses the same bot token/API base URL above # (TG_BOT_TOKEN/TG_API_BASE_URL), just a different recipient (the client's # own chat_id instead of the fixed staff chat). Everything below is also # editable live from the Settings page (internal/settings) — these are only # the first-boot fallback values. # # CLIENT_TG_BOT_USERNAME (no @) is used to build the t.me/?start= # deep link a client opens to link their chat. # # TG_WEBHOOK_SECRET is checked against Telegram's # X-Telegram-Bot-Api-Secret-Token header on every inbound webhook call — # unset means the webhook endpoint 401s everything, closed by default. # # TG_WEBHOOK_URL (infra-level, NOT owner-editable via Settings — it's how # THIS process's own public address gets registered with Telegram, same # category as CORE_URL) is the full public HTTPS URL Telegram should POST # updates to, e.g. https://your-domain/api/webhooks/telegram — requires a # reverse-proxy route into this container's /api/webhooks/telegram. Unset # skips webhook registration silently (see internal/tgbot.EnsureWebhook). # # PUBLIC_TRACKING_URL is the base URL for the client-facing tracking page # (site's /track route) — appended with "/" in the "ready" # notification. # # MAX_WEBHOOK_URL is MAX's (max.ru) equivalent of TG_WEBHOOK_URL above — # infra-level, NOT owner-editable via Settings, the full public HTTPS URL # MAX should POST updates to, e.g. https://your-domain/api/webhooks/max. # Bot token / webhook secret / client bot username ARE owner-editable in # Settings (see internal/maxbot.EnsureWebhook — unset skips registration # silently, same as Telegram). # # VK_WEBHOOK_URL is VK's (vk.com community Callback API) equivalent — # infra-level, NOT owner-editable via Settings, the full public HTTPS URL # VK should POST events to, e.g. https://your-domain/api/webhooks/vk. Group # token / group ID / secret key / confirmation code / community short name # ARE owner-editable in Settings (see internal/vkbot.EnsureWebhook — unset # skips registration silently, same as MAX/Telegram). Unlike MAX/Telegram, # VK's confirmation code is VK-generated and only ever shown in the # community's own admin panel — an owner still has to open that page once # regardless of whether EnsureWebhook auto-registered the server URL. # # SMS.ru (internal/smsgw) is the SMS fallback for clients without a linked # Telegram/MAX chat — get SMS_API_ID from https://sms.ru/?panel=api. SMS # costs money per message; sms_enabled stays off by default even with # credentials configured (owner must explicitly enable it in Settings). CLIENT_TG_BOT_USERNAME= TG_WEBHOOK_SECRET= TG_WEBHOOK_URL= MAX_WEBHOOK_URL= VK_WEBHOOK_URL= PUBLIC_TRACKING_URL= SMS_PROVIDER=smsru SMS_API_ID= SMS_FROM= # internal/scraper's technosuccess.ru source — a wholesale account's own # login (unlike Regard, which needs no auth). Unset skips registering this # source entirely, same pattern as the Telegram/MAX bot tokens above. TECHNOSUCCESS_EMAIL= TECHNOSUCCESS_PASSWORD= # internal/scraper's i-t-p.pro (b2b.i-t-p.pro) source — a wholesale # account's own login/password for their documented JSON-RPC B2B API # (https://b2b.i-t-p.pro/download/docs/api/api.html), not a site scrape. # Unset skips registering this source entirely, same pattern as # TECHNOSUCCESS_* above. ITPARTNER_LOGIN= ITPARTNER_PASSWORD= # internal/selfupdate — "Обновить" button in Settings → Обновления, proxies # to deploy-agent/ (see that directory's README.md for what it does and the # install steps). DEPLOY_AGENT_SOCKET_PATH is the path *inside this # container* (docker-compose.yml bind-mounts deploy-agent/ to # /run/deploy-agent), not a host path. DEPLOY_AGENT_TOKEN must exactly # match deploy-agent/agent.env's value. Unset either and every # /api/selfupdate/* route 503s — same fail-closed pattern as # TECHNOSUCCESS_*/ITPARTNER_* above, and matches that most deployments of # this repo won't have deploy-agent installed at all. DEPLOY_AGENT_SOCKET_PATH=/run/deploy-agent/agent.sock DEPLOY_AGENT_TOKEN=