Configuration
Who is this for
Section titled “Who is this for”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 # commentbreaks the value. - With
APP_ENV=proda 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=1in.env, the/pausecommand, and the automatic pause after a provider “out of money” answer. The.envflag wins and cannot be lifted by/resume. - The full variable table is in Environment variables. This page covers operations only.
How configuration is loaded
Section titled “How configuration is loaded”- Both
sticker-bot.serviceandsticker-api.servicedeclareEnvironmentFile=/opt/ai-sticker-pack/bot/.env(unit files). systemd puts the file into the process environment. bot/config.pyalso loads the samebot/.envitself, 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 thebot/folder, not from the current directory. - Values are read once at import. Nothing is re-read while the process runs.
File rules
Section titled “File rules”| 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 |
Changing a setting
Section titled “Changing a setting”On the server:
E=/opt/ai-sticker-pack/bot/.envsetenv() { grep -q "^$1=" $E && sed -i "s/^$1=.*/$1=$2/" $E || echo "$1=$2" >> $E; }
setenv WORKER_SLOTS 2bash /opt/ai-sticker-pack/deploy/update.sh --yessetenv 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.
Which service needs the restart
Section titled “Which service needs the restart”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 |
Validation at start
Section titled “Validation at start”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:
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 |
Errors in prod, warnings in dev
Section titled “Errors in prod, warnings in dev”| 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 |
Warnings only (the service starts)
Section titled “Warnings only (the service starts)”- Only one AI key is set: there is no fallback provider.
PRIMARY_AI_PROVIDERequalsFALLBACK_AI_PROVIDER: fallback is effectively off.ADMIN_USER_IDSempty: nobody can use/stats,/admin_stats,/pause,/resume.ADMIN_PANEL_PASSWORDshorter than 12 characters.LEADS_SECRETset butLEADS_CHAT_IDempty: leads are stored but never published.LEADS_ADMIN_IDSempty: nobody can press status buttons or use/stickers_*.
Production profile
Section titled “Production profile”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:
journalctl -u sticker-bot -n 60 --no-pager -o cat | grep -E 'STARTUP|Воркер очереди запущен'Feature flags for quick rollback
Section titled “Feature flags for quick rollback”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 |
Maintenance mode
Section titled “Maintenance mode”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/startstill work. - Kiosk API: new session, outfit choice and photo upload answer
503with codemaintenance; 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.
curl -s https://stickers.eventtalks.uz/health # "maintenance": "env" | "admin" | "auto" | nullsqlite3 -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
Section titled “EVENT_MODE”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.
Where to go next
Section titled “Where to go next”- Environment variables — the full table with defaults.
- Secrets — the values that must never be printed and how to rotate them.
- Deploy and rollback —
update.sh, restarts and rollbacks. - Monitoring and alerts — how to see the effect of a change.
Last verified: 2026-11-13 against ai-sticker-pack@b715418 and ai-stickers-landing@e350ff5.