docs: sync README deploy section with actual env vars

Fix CORS default (was incorrectly listed as `*`, which is rejected on
startup) and document the env vars exposed by config.py and
docker-compose.yml — proxy/SSRF, auth, logging, retention, and
integration settings. Sync the Docker Compose example with the
hardened compose file at the repo root.
This commit is contained in:
2026-05-11 21:50:31 +03:00
parent 5d41a39406
commit 4335036c22
+92 -6
View File
@@ -15,7 +15,7 @@ notifications to configurable targets (Telegram, webhooks) using customizable te
## Project Structure ## Project Structure
``` ```text
packages/ packages/
core/ — Shared library: providers, models, notifications, templates core/ — Shared library: providers, models, notifications, templates
server/ — FastAPI REST server with SQLite database server/ — FastAPI REST server with SQLite database
@@ -31,6 +31,7 @@ docker run -d \
-p 8420:8420 \ -p 8420:8420 \
-v notify-bridge-data:/data \ -v notify-bridge-data:/data \
-e NOTIFY_BRIDGE_SECRET_KEY=$(openssl rand -hex 32) \ -e NOTIFY_BRIDGE_SECRET_KEY=$(openssl rand -hex 32) \
-e NOTIFY_BRIDGE_CORS_ALLOWED_ORIGINS=http://localhost:8420 \
git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge:latest git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge:latest
``` ```
@@ -38,12 +39,59 @@ Then open `http://localhost:8420` in your browser.
### Environment Variables ### Environment Variables
Core settings (all prefixed with `NOTIFY_BRIDGE_`):
| Variable | Required | Default | Description | | Variable | Required | Default | Description |
| -------- | -------- | ------- | ----------- | | -------- | -------- | ------- | ----------- |
| `NOTIFY_BRIDGE_SECRET_KEY` | Yes | — | Secret key for JWT tokens (min 32 chars) | | `SECRET_KEY` | Yes | — | Secret for JWT signing (min 32 chars). Default placeholders and known dev-only strings are rejected on startup. |
| `NOTIFY_BRIDGE_PORT` | No | `8420` | Server listen port | | `CORS_ALLOWED_ORIGINS` | Recommended | `http://localhost:5175` | Comma-separated browser origins. Wildcard `*` is **rejected** because credentials are enabled. Set this to the URL you load the UI from. |
| `NOTIFY_BRIDGE_CORS_ALLOWED_ORIGINS` | No | `*` | Comma-separated allowed CORS origins | | `DATA_DIR` | No | `/data` (in Docker) | Directory for SQLite DB, backups, and caches. Mount a volume here. |
| `NOTIFY_BRIDGE_DEBUG` | No | `false` | Enable debug logging | | `DATABASE_URL` | No | `sqlite+aiosqlite:///<DATA_DIR>/notify_bridge.db` | Override DB connection string. |
| `HOST` | No | `0.0.0.0` | Bind address. |
| `PORT` | No | `8420` | Server listen port. |
| `DEBUG` | No | `false` | Enable debug logging. |
Reverse proxy / network:
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `FORWARDED_ALLOW_IPS` | `127.0.0.1` | Trusted proxy IPs whose `X-Forwarded-For` / `X-Forwarded-Proto` headers are honored. Set to your reverse proxy IP (e.g. `172.17.0.1` for the default Docker bridge). Use `*` only when the container is not directly internet-reachable. |
| `EXTERNAL_URL` | — | Public base URL (e.g. `https://notify.example.com`). Used to build webhook URLs shown in the UI. Also settable from the Settings page. |
| `ALLOW_PRIVATE_URLS` | unset | Set to `1` to allow requests to RFC1918 / loopback / link-local hosts (homelab scenario: Immich/Gitea on the same LAN). **Do not enable on a publicly exposed instance.** |
Auth & tokens:
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `15` | Lifetime of access JWTs. |
| `REFRESH_TOKEN_EXPIRE_DAYS` | `30` | Lifetime of refresh tokens. |
| `JWT_ISSUER` | `notify-bridge` | `iss` claim. |
| `JWT_AUDIENCE` | `notify-bridge-api` | `aud` claim. |
Logging (all are also live-editable in the Settings page, except `log_format`):
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `LOG_LEVEL` | `INFO` | Root level: `DEBUG` / `INFO` / `WARNING` / `ERROR`. |
| `LOG_FORMAT` | `text` | `text` or `json`. Switching requires a restart. |
| `LOG_LEVELS` | — | Per-module overrides, e.g. `notify_bridge_core.notifications.telegram.client=DEBUG,sqlalchemy.engine=INFO`. |
Retention & maintenance:
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `EVENT_LOG_RETENTION_DAYS` | `30` | Days of `event_log` history to keep. `0` disables the retention job. |
| `PRE_MIGRATE_SNAPSHOT_KEEP` | `5` | Number of pre-migration DB snapshots to keep in `<DATA_DIR>/backups/`. `0` disables snapshotting. |
| `GRACEFUL_SHUTDOWN_SECONDS` | `60` | Time to wait for in-flight requests / scheduler jobs on SIGTERM before force-killing. |
Integrations & misc:
| Variable | Default | Description |
| -------- | ------- | ----------- |
| `TELEGRAM_WEBHOOK_SECRET` | — | Shared secret for Telegram bot webhooks. Also settable from the Settings page. |
| `TIMEZONE` | `UTC` | IANA timezone (e.g. `Europe/Warsaw`) used by the scheduler. Also settable from the Settings page. |
| `STATIC_DIR` | `/app/static` (in Docker) | Frontend static files directory. The Docker image sets this; don't override unless you're running outside the image. |
| `SUPERVISED` | auto-detect | Set to `1` to tell the backup endpoint that an external supervisor will restart the process. |
### Docker Compose ### Docker Compose
@@ -58,12 +106,50 @@ services:
volumes: volumes:
- notify-bridge-data:/data - notify-bridge-data:/data
environment: environment:
- NOTIFY_BRIDGE_SECRET_KEY=your-secret-key-min-32-characters # REQUIRED — any 32+ byte random string. `openssl rand -hex 32` is one way.
- NOTIFY_BRIDGE_SECRET_KEY=${NOTIFY_BRIDGE_SECRET_KEY:?Set NOTIFY_BRIDGE_SECRET_KEY (min 32 chars)}
# Comma-separated list of allowed browser origins. Wildcard `*` is
# rejected on startup because credentials are enabled.
- NOTIFY_BRIDGE_CORS_ALLOWED_ORIGINS=${NOTIFY_BRIDGE_CORS_ALLOWED_ORIGINS:-http://localhost:8420}
# Trusted proxy IPs whose X-Forwarded-For / X-Forwarded-Proto we honor.
# Set this to your reverse proxy's IP (e.g. 172.17.0.1 for the default
# docker bridge, or `*` only if the container is NOT reachable from the
# public internet).
- NOTIFY_BRIDGE_FORWARDED_ALLOW_IPS=${NOTIFY_BRIDGE_FORWARDED_ALLOW_IPS:-127.0.0.1}
# Opt-in SSRF bypass for private/loopback/link-local hosts (homelab
# scenario — tracking an Immich/Gitea instance on the same LAN). DO NOT
# enable on a publicly exposed instance.
# - NOTIFY_BRIDGE_ALLOW_PRIVATE_URLS=1
healthcheck:
# Use /api/ready (not /api/health) so the container is only reported
# healthy after migrations and the scheduler finish booting.
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8420/api/ready', timeout=3)"]
interval: 30s
timeout: 5s
retries: 3
start_period: 30s
read_only: true
tmpfs:
- /tmp
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
mem_limit: 512m
cpus: 1.0
pids_limit: 256
volumes: volumes:
notify-bridge-data: notify-bridge-data:
``` ```
A ready-to-use `docker-compose.yml` lives at the repo root.
### Health & Readiness
- `GET /api/health` — process is up. Use for liveness probes.
- `GET /api/ready` — migrations + scheduler have booted. Use for readiness probes and Docker `HEALTHCHECK` (as the compose example above does).
## Quick Start (Development) ## Quick Start (Development)
```bash ```bash