4335036c22
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.
7.3 KiB
7.3 KiB
Notify Bridge
A generic bridge between service providers and notification targets.
Notify Bridge monitors services (like Immich photo servers) for changes and dispatches notifications to configurable targets (Telegram, webhooks) using customizable templates.
Architecture
- Service Providers — Connectors to external services (Immich, more coming)
- Trackers — Monitor specific collections within a provider for changes
- Tracking Configs — Define what events to watch for and scheduling rules
- Notification Targets — Where to send notifications (Telegram chats, webhook URLs)
- Template Configs — Jinja2 templates that format notifications per provider type
Project Structure
packages/
core/ — Shared library: providers, models, notifications, templates
server/ — FastAPI REST server with SQLite database
frontend/ — SvelteKit dashboard (Svelte 5, Tailwind CSS v4)
Quick Docker Deploy
docker run -d \
--name notify-bridge \
--restart unless-stopped \
-p 8420:8420 \
-v notify-bridge-data:/data \
-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
Then open http://localhost:8420 in your browser.
Environment Variables
Core settings (all prefixed with NOTIFY_BRIDGE_):
| Variable | Required | Default | Description |
|---|---|---|---|
SECRET_KEY |
Yes | — | Secret for JWT signing (min 32 chars). Default placeholders and known dev-only strings are rejected on startup. |
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. |
DATA_DIR |
No | /data (in Docker) |
Directory for SQLite DB, backups, and caches. Mount a volume here. |
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
services:
notify-bridge:
image: git.dolgolyov-family.by/alexei.dolgolyov/notify-bridge:latest
container_name: notify-bridge
restart: unless-stopped
ports:
- "8420:8420"
volumes:
- notify-bridge-data:/data
environment:
# 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:
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 DockerHEALTHCHECK(as the compose example above does).
Quick Start (Development)
# Backend
cd packages/server
pip install -e .
NOTIFY_BRIDGE_DATA_DIR=./test-data NOTIFY_BRIDGE_SECRET_KEY=your-secret-key-min-32chars \
python -m uvicorn notify_bridge_server.main:app --host 0.0.0.0 --port 8420
# Frontend
cd frontend
npm install
npm run dev
Supported Providers
- Immich — Photo/video server with album change detection