Skip to content

Configuration

An operator who has to change a setting on the server, prepare the production profile for an event, or switch a feature off during an incident.

  • One file configures everything: /opt/ai-sticker-pack/bot/.env. Both services read it once, at start.
  • A change takes effect only after a restart. The safe default is bash /opt/ai-sticker-pack/deploy/update.sh --yes, which restarts both services and checks /health.
  • Comments must be on their own lines. KEY=value # comment breaks the value.
  • With APP_ENV=prod a missing token, AI key, alert chat or admin password stops the service at start. Typos in enumerated settings stop it in any mode.
  • Maintenance mode has three sources: MAINTENANCE_MODE=1 in .env, the /pause command, and the automatic pause after a provider “out of money” answer. The .env flag wins and cannot be lifted by /resume.
  • The full variable table is in Environment variables. This page covers operations only.
  • Both sticker-bot.service and sticker-api.service declare EnvironmentFile=/opt/ai-sticker-pack/bot/.env (unit files). systemd puts the file into the process environment.
  • bot/config.py also loads the same bot/.env itself, so tools started by hand (tools/*.py) see the same values. A variable that is already in the process environment wins over the file.
  • Relative paths (DATA_DIR, DB_PATH, TEMPLATES_DIR, LOG_DIR) are resolved from the bot/ folder, not from the current directory.
  • Values are read once at import. Nothing is re-read while the process runs.
Rule Why
KEY=value, one per line systemd EnvironmentFile format
Comments only on separate lines starting with # systemd does not strip # … after a value; it becomes part of the value
LF line endings With CRLF, systemd keeps a carriage return at the end of every value. update.sh converts CRLF to LF automatically.
Owner sticker:sticker, mode 600 The file holds secrets. update.sh and push.ps1 re-apply the mode.
Never committed, never printed See Secrets

On the server:

Terminal window
E=/opt/ai-sticker-pack/bot/.env
setenv() { grep -q "^$1=" $E && sed -i "s/^$1=.*/$1=$2/" $E || echo "$1=$2" >> $E; }
setenv WORKER_SLOTS 2
bash /opt/ai-sticker-pack/deploy/update.sh --yes

setenv replaces the line when the key exists and appends it when it does not. It is a shell function, so define it again in every new SSH session. Do not use it for values that contain /, & or spaces (for example URLs); edit those with an editor: nano /opt/ai-sticker-pack/bot/.env.

update.sh without --yes asks before restarting when jobs are in flight. What a restart does to those jobs: Deploy and rollback.

update.sh always restarts both. When you want to restart only one service by hand (systemctl restart sticker-api or systemctl restart sticker-bot), use this table.

Variables Read by Restart
GEMINI_API_KEY, OPENAI_API_KEY, PRIMARY_AI_PROVIDER, FALLBACK_AI_PROVIDER, *_MODEL, AI_TIMEOUT_SEC, WORKER_SLOTS, CUT_MAX_PARALLEL, AI_MAX_PARALLEL_IMAGES, CONSISTENCY_*, LIKENESS_MODE, EDGE_*, SKIN_NORM_MAX_DE, JOB_MAX_COST_USD, PHOTO_TTL_HOURS, ADMIN_CHAT_ID, ADMIN_USER_IDS, EVENT_NAME Bot (worker, alerts, cleanup) sticker-bot
LEADS_CHAT_ID, LEADS_ADMIN_IDS, LEADS_CARD_PII Bot (leads loop and group handlers) sticker-bot
ADMIN_PANEL_USER, ADMIN_PANEL_PASSWORD, KIOSK_IDLE_SEC, KIOSK_IDLE_PROMPT_SEC, KIOSK_THEME, SHOW_PLACEHOLDER_TEMPLATES, ART_STYLE API (admin panel, kiosk, job creation) sticker-api
LEADS_SECRET, LEADS_IP_SALT, LEADS_IP_LIMIT, LEADS_IP_WINDOW_SEC, LEADS_HOURLY_LIMIT API (POST /leads) sticker-api
TELEGRAM_BOT_TOKEN, TELEGRAM_BOT_USERNAME, LEADS_ENABLED, MAINTENANCE_MODE, STICKER_RENDER_MODE, ALLOW_MULTIPLE_PACKS, PRICE_*, BUDGET_USD, IMAGE_RPD_*, DISPLAY_UTC_OFFSET_HOURS, BASE_URL, APP_ENV, paths Both both
EVENT_MODE, TEST_GEMINI_API_KEY Tool scripts only (the bot only logs one line about EVENT_MODE) none

config.validate runs in both services before anything else (bot/config.py). Every problem is logged as Конфигурация некорректна: <reason> (“configuration is invalid”) and the process exits with code 1. systemd then restarts it every 3 seconds, so a bad value shows up as a restart loop:

Terminal window
journalctl -u sticker-bot -n 30 --no-pager -o cat | grep 'Конфигурация'
journalctl -u sticker-api -n 30 --no-pager -o cat | grep 'Конфигурация'

APP_ENV defaults to prod when it is missing. APP_ENV=dev turns the “missing key” errors into warnings and logs APP_ENV=dev — проверки ключей ослаблены, НЕ запускать так на ивенте (“key checks relaxed, do NOT run an event like this”).

Errors in any mode (typos and impossible values)

Section titled “Errors in any mode (typos and impossible values)”
Setting Rule
PRIMARY_AI_PROVIDER, FALLBACK_AI_PROVIDER gemini or openai
PHOTO_TTL_HOURS 1 to 72
STICKER_OUTLINE_MODE partial or none
STICKER_RENDER_MODE composite or figure. An empty value is an error; a missing line means composite.
ART_STYLE realistic_illustration, comic_popart, stylized_3d or cute_cartoon
FIGURE_SHEET_ATTEMPTS at least 1
FIGURE_SHEET_ASPECT 2:3 or 3:2
SHEET_LAYOUT 2x6 or 3x4
AI_MAX_PARALLEL_IMAGES, CUT_MAX_PARALLEL, WORKER_SLOTS at least 1
JOB_MAX_COST_USD greater than 0
IMAGE_RPD_LIMIT, IMAGE_RPD_WARN_FRAC limit at least 1; fraction above 0 and at most 1
ANCHOR_VERIFY_TIMEOUT_SEC, CONSISTENCY_SKIN_DE_MAX greater than 0
CONSISTENCY_SKIN_STRENGTH, ANCHOR_SKIN_STRENGTH, HYBRID_SELFIE_PULL 0 to 1
SKIN_NORM_MAX_DE, CONSISTENCY_REGEN_BUDGET_SEC, CONSISTENCY_MAX_REGEN, EDGE_REGEN_TIMEOUT_SEC not negative
LIKENESS_MODE empty, classic or hybrid
KIOSK_THEME light or dark
BASE_URL starts with https:// (or http:// locally) when set
LEADS_IP_LIMIT, LEADS_IP_WINDOW_SEC, LEADS_HOURLY_LIMIT at least 1
TELEGRAM_BOT_TOKEN (bot only) must be set, even in dev
ADMIN_CHAT_ID, LEADS_CHAT_ID, ADMIN_USER_IDS, LEADS_ADMIN_IDS, prices must be numeric; a non-numeric value raises at import, before validation
Condition Component
TELEGRAM_BOT_USERNAME empty bot and API
TELEGRAM_BOT_TOKEN empty API (in the bot it is always an error)
Neither GEMINI_API_KEY nor OPENAI_API_KEY set bot and API
ADMIN_CHAT_ID empty bot
ADMIN_PANEL_PASSWORD empty API
GEMINI_API_KEY equals TEST_GEMINI_API_KEY bot and API
LEADS_SECRET set but shorter than 24 characters bot and API
LEADS_SECRET set but LEADS_IP_SALT empty bot and API
  • Only one AI key is set: there is no fallback provider.
  • PRIMARY_AI_PROVIDER equals FALLBACK_AI_PROVIDER: fallback is effectively off.
  • ADMIN_USER_IDS empty: nobody can use /stats, /admin_stats, /pause, /resume.
  • ADMIN_PANEL_PASSWORD shorter than 12 characters.
  • LEADS_SECRET set but LEADS_CHAT_ID empty: leads are stored but never published.
  • LEADS_ADMIN_IDS empty: nobody can press status buttons or use /stickers_*.

Values for an event server. “Template” means bot/.env.example, which a test keeps in sync with the code (tests/test_deploy.py).

Setting Recommended Why it matters at an event
APP_ENV prod Strict start-up checks
TELEGRAM_BOT_TOKEN, TELEGRAM_BOT_USERNAME set The QR deep link and the pack name suffix use the username
ADMIN_CHAT_ID set Every alert goes here; without it failures are only in the log
ADMIN_USER_IDS set Who may use /stats, /admin_stats, /pause, /resume
ADMIN_PANEL_PASSWORD long and random, 12+ characters The panel is reachable from the internet
GEMINI_API_KEY set Primary provider
OPENAI_API_KEY set if available Without it there is no fallback provider
STICKER_RENDER_MODE figure Template value
FIGURE_SHEET_ASPECT 3:2 Template value
SHEET_LAYOUT 3x4 Template value
WORKER_SLOTS 2 See the note below
CUT_MAX_PARALLEL empty (= number of vCPU) Lower it to 1 to cut the memory peak
ALLOW_MULTIPLE_PACKS 0 One pack per Telegram account; extra packs are granted in the admin panel
SHOW_PLACEHOLDER_TEMPLATES 0 Guests see only finished outfits
JOB_MAX_COST_USD 1.5 Hard cost cap per job
IMAGE_RPD_LIMIT / IMAGE_RPD_WARN_FRAC 1000 / 0.7 Daily image quota of the provider tier and the alert threshold
PRICE_* current provider prices Empty prices make the spend estimate too low; the admin panel shows a warning
BUDGET_USD starting budget Basis for the balance estimate until a real balance is entered in the admin panel
PHOTO_TTL_HOURS 72 (maximum allowed) Legal requirement
BASE_URL https://stickers.eventtalks.uz Logs and the panel link in /admin_stats
KIOSK_THEME dark or light Tablet screen theme; needs an API restart
EVENT_NAME a short label for this event Prefix of every alert: [<EVENT_NAME>]
EVENT_MODE 1 from rehearsal until the event ends Blocks paid test scripts on the production key
MAINTENANCE_MODE 0 before guests arrive 1 blocks all guests
LEADS_* see Deploy and rollback Only when the landing sends leads to this server

Check what the running process actually uses:

Terminal window
journalctl -u sticker-bot -n 60 --no-pager -o cat | grep -E 'STARTUP|Воркер очереди запущен'

Each flag is a line in bot/.env. Set it with setenv, then run update.sh --yes. Source of the list: docs/EVENT_RUNBOOK.md checked against bot/config.py.

Flag and value Effect Cost
CONSISTENCY_MODE=0 Generation without the anchor portrait and the pack-level QA Faster, but appearance can drift between the 12 stickers
LIKENESS_MODE=classic Face comes only from the selfie and the face crop; no anchor, no hard locks, no skin normalisation. Overrides CONSISTENCY_MODE. Loses the consistency features. To switch it off again set the value to empty with an editor.
EDGE_ALLOW_TORSO_CROP=0 Any figure touching the sheet edge counts as a defect and is regenerated More regenerations, more time and cost per pack
EDGE_FLAT_CUT_CHECK=0 Stops looking for an arm cut by a straight vertical line Fewer regenerations; cut arms can reach the guest
SKIN_NORM_MAX_DE=0 Removes the cap on the skin colour shift Patches can return on very light skin
FIGURE_FALLBACK_ENABLED=0 A failed figure sheet fails the job instead of building a composite pack More failed jobs
STICKER_RENDER_MODE=composite The older render mode: AI head on a template body Different look; use only if figure is broken
QA_IDENTITY_ENABLED=false Skips the per-sheet identity check One vision call less per sheet; wrong faces are not caught
CUT_MAX_PARALLEL=1 One sheet is cut at a time About 0.4 GB lower memory peak, about 3 s longer pack (docs/EVENT_RUNBOOK.md)
WORKER_SLOTS=1 One job at a time Lower memory and provider load; a queue forms with more than one guest per pack time
PRIMARY_AI_PROVIDER=openai Swaps primary and fallback (also set FALLBACK_AI_PROVIDER=gemini) Needs OPENAI_API_KEY; different cost and look
LEADS_ENABLED=0 /leads answers 404, the bot stops handling leads The landing falls back to direct send; those requests are not in the database
MAINTENANCE_MODE=1 Guests get the maintenance text, the kiosk starts no sessions, the worker takes no jobs Nobody is served until it is set back to 0 and restarted

Source: bot/services/maintenance.py, bot/handlers/maintenance.py.

Source Turned on by Turned off by Survives restart
env MAINTENANCE_MODE=1 in bot/.env and a restart MAINTENANCE_MODE=0 and a restart yes
admin /pause in a private chat with the bot, from a user in ADMIN_USER_IDS /resume yes (stored in the database)
auto The AI provider answered “out of money” (HTTP 402) /resume after topping up yes (stored in the database)

Precedence. env is checked first. While MAINTENANCE_MODE=1 is set, the reported source is env and /resume answers that the mode stays on. /resume only removes the database record. The admin and auto sources share one database record: /pause during an automatic pause answers “already on”.

What it does.

  • Bot: every private message or button press from a guest gets Сейчас идёт техобслуживание, загляни чуть позже 🛠 (“maintenance in progress, check back a little later”). Admin commands other than /start still work.
  • Kiosk API: new session, outfit choice and photo upload answer 503 with code maintenance; the tablet shows a notice.
  • Worker: takes no new jobs. Jobs already started are finished. Queued jobs wait, and the 5 and 15 minute delivery deadlines keep running, so a long pause fails them.
  • The leads group is not affected: status buttons and /stickers_* keep working.

How to see it.

Terminal window
curl -s https://stickers.eventtalks.uz/health # "maintenance": "env" | "admin" | "auto" | null
sqlite3 -readonly /opt/ai-sticker-pack/bot/data/app.db "SELECT value, updated_at FROM settings WHERE key='maintenance';"

The admin panel shows a banner, and /admin_stats starts with 🛠 РЕЖИМ ОБСЛУЖИВАНИЯ (“maintenance mode”). Without bot access, the database record can be removed directly: sqlite3 /opt/ai-sticker-pack/bot/data/app.db "DELETE FROM settings WHERE key='maintenance';".

EVENT_MODE=1 protects the guests’ daily image quota and budget from test scripts (bot/services/event_guard.py).

TEST_GEMINI_API_KEY EVENT_MODE Paid tool scripts (tools/load_test.py, consistency_run.py, style_compare.py, make_outfit_previews.py, face_box_check.py)
set, different from the production key any Run on the test key; the production key and the fallback provider are cleared for them
empty 1 Refuse to start
empty 0 Run on the production key

The bot and the API do not change behaviour and do not need a restart for this flag. Set it in the server bot/.env and in every local .env that holds the production key.


Last verified: 2026-11-13 against ai-sticker-pack@b715418 and ai-stickers-landing@e350ff5.