4335036c22af85138108b184153a83562abf64c1
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.
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
Description
Bridge service events (Immich, …) to notification targets (Telegram, webhooks) via customizable Jinja2 templates and commands.
Releases
31
Notify Bridge 0.10.0
Latest
Languages
Python
59.9%
Svelte
26.1%
HTML
7.6%
TypeScript
3.7%
Jinja
2%
Other
0.6%