Files
notify-bridge/README.md
T
alexei.dolgolyov 4335036c22 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.
2026-05-11 21:50:31 +03:00

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 Docker HEALTHCHECK (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