Skip to content

# Runbook (operational guide)

This document covers running the application, checking health, logs, common errors, and database operations.

## Starting and stopping

### Local

- Start: From the repository root, with virtualenv activated: `bash python main.py` Or after `pip install -e .`: `duty-teller`
- Stop: `Ctrl+C`

### Docker

- Dev (code mounted; no rebuild needed for code changes): `bash docker compose -f docker-compose.dev.yml up --build` Stop: `Ctrl+C` or `docker compose -f docker-compose.dev.yml down`.

- Prod (built image; restarts on failure): `bash docker compose -f docker-compose.prod.yml up -d --build` Stop: `docker compose -f docker-compose.prod.yml down`.

On container start, `entrypoint.sh` runs Alembic migrations then starts the app as user `botuser`. Ensure `.env` (or your orchestrator’s env) contains `BOT_TOKEN` and any required variables; see configuration.md.

## Health check

- HTTP: The FastAPI app serves the API and static webapp. A simple way to verify it is up is to open the interactive API docs: `GET /docs` (e.g. `http://localhost:8080/docs`). If that page loads, the server is running.
- There is no dedicated `/health` endpoint; use `/docs` or a lightweight API call (e.g. `GET /api/duties?from=...&to=...` with valid auth) as needed.

## Logs

- Local: Output goes to stdout/stderr; redirect or use your process manager’s logging (e.g. systemd, supervisord).
- Docker: Use `docker compose logs -f` (with the appropriate compose file) to follow application logs. Adjust log level via Python `logging` if needed (e.g. environment or code).

## Common errors and what to check

### "hash_mismatch" (403 from `/api/duties` or Miniapp)

- Cause: The server that serves the Mini App (e.g. production host) uses a different `BOT_TOKEN` than the bot from which users open the Mini App (e.g. test vs production bot). Telegram signs initData with the bot token; if tokens differ, validation fails.
- Check: Ensure the same `BOT_TOKEN` is set in `.env` (or equivalent) on the machine serving `/api/duties` as the one used by the bot instance whose menu button opens the Miniapp.

### Miniapp "Open in browser" or direct link — access denied

- Cause: When users open the calendar via “Open in browser” or a direct URL, Telegram may not send `tgWebAppData` (initData). The API requires initData (or `MINI_APP_SKIP_AUTH` / private IP in dev).
- Action: Users should open the calendar via the bot’s menu button (e.g. ⋮ → «Календарь») or a Web App inline button so Telegram sends user data.

### 403 "Open from Telegram" / no initData

- Cause: Request to `/api/duties` (or calendar) without valid `X-Telegram-Init-Data` header. In production, only private IP clients can be allowed without initData (see `_is_private_client` in `api/dependencies.py`); behind a reverse proxy, `request.client.host` is often the proxy (e.g. 127.0.0.1), so the “private IP” bypass may not apply to the real user.
- Check: Ensure the Mini App is opened from Telegram (menu or inline button). If behind a reverse proxy, see README “Production behind a reverse proxy” (forward real client IP or rely on initData).

### Mini App URL — redirect and broken auth

- Cause: If the Mini App URL is configured without a trailing slash (e.g. `https://your-domain.com/app`) and the server redirects `/app` → `/app/`, the browser can drop the fragment Telegram sends, breaking authorization.
- Action: Configure the bot’s menu button / Web App URL with a trailing slash, e.g. `https://your-domain.com/app/`. See README “Mini App URL”.

### User not in allowlist (403)

- Cause: Telegram user’s username is not in `ALLOWED_USERNAMES` or `ADMIN_USERNAMES`, and (if using phone) their phone (set via `/set_phone`) is not in `ALLOWED_PHONES` or `ADMIN_PHONES`.
- Check: configuration.md for `ALLOWED_USERNAMES`, `ADMIN_USERNAMES`, `ALLOWED_PHONES`, `ADMIN_PHONES`. Add the user or ask them to set phone and add it to the allowlist.

## Database and migrations

- Default DB path (SQLite): `data/duty_teller.db` (relative to working directory when using default `DATABASE_URL=sqlite:///data/duty_teller.db`). In Docker, the entrypoint creates `/app/data` and runs migrations there.
- Migrations (Alembic): From the repository root: `bash alembic -c pyproject.toml upgrade head` Config: `pyproject.toml` → `[tool.alembic]`; script location `alembic/`; metadata and URL from `duty_teller.config` and `duty_teller.db.models.Base`.
- Rollback: Use with care; test in a copy of the DB first. Example to go back one revision: `bash alembic -c pyproject.toml downgrade -1` Always backup the database before downgrading.

For full list of env vars (including `DATABASE_URL`), see configuration.md. For reverse proxy and Mini App URL details, see the main README.