A lightweight, modern web UI for discovering and managing OpenSentry devices on your network. It supports mDNS discovery, optional device status verification, and OAuth2/OIDC login with an automatic local-login fallback if the identity provider is unavailable.
- Discovery via mDNS using service
_opensentry._tcp.local. - Optional /status fetch per device (with Bearer token)
- OAuth2/OIDC login (Authorization Code + PKCE) with confidential/public clients
- One-time local-login fallback if the OAuth2 server is down
- Device list management and basic test tools (health, stream, reachability)
- Simple REST API powering the UI
- Python 3.12+
- uv for zero-config runs
- Getting started: docs/getting-started.md
- Install uv: docs/install-uv.md
- Build from source: docs/build-from-source.md
- Docker: docs/docker.md
- Docker Compose: docs/docker-compose.md
- Tutorials: docs/tutorials.md
- Troubleshooting: docs/troubleshooting.md
- Start the UI (deps auto-installed on first run):
uv run main.pyThe app binds to port 5000 or the nearest open port above it (printed on startup). Visit the printed URL, e.g. http://127.0.0.1:5000.
- Environment overrides:
PORT: Force a specific port.HOST: Bind host (default0.0.0.0).SECRET_KEY: Flask session secret (defaults to a dev value; set in production).
Open the Settings page (/settings) and select:
- local: Built-in username/password login (
admin/admin) for development. - oauth2: External provider using OIDC discovery.
Settings are persisted to config_store.json at the repository root. You can also edit this file directly. Relevant fields:
{
"auth_mode": "local" | "oauth2",
"oauth2_base_url": "http://127.0.0.1:8000",
"oauth2_client_id": "<your-client-id>",
"oauth2_client_secret": "<optional-secret>",
"oauth2_scope": "openid profile email offline_access"
}When using auth_mode=oauth2, configure your provider:
- Discovery: Provide
oauth2_base_url(issuer base). The app fetches/.well-known/openid-configuration. - Redirect URI: Add
http://127.0.0.1:5000/oauth2/callbackto the client’s allowed list. If you access the UI via another host (e.g.http://10.0.0.246:5000), add that callback too. - Client type:
- Public: set token auth to
none. The app uses PKCE (S256) automatically. - Confidential: set token auth to
client_secret_postand populateoauth2_client_secretin Settings.
- Public: set token auth to
- Scopes: Ensure your client policy allows the scopes you request (default
openid profile email offline_access).
If the provider is temporarily unavailable, the app shows a dedicated page and offers a one-time local-login fallback. This fallback is session-scoped and does not modify persisted settings.
- Service type:
_opensentry._tcp.local.with TXT keys likeid,name,ver,caps,auth,api,path,proto. - Ensure devices advertise via mDNS (e.g.,
OPENSENTRY_MDNS_DISABLE=0) and multicast is allowed by your network/firewall. - In the UI:
- Provide a Bearer token if your devices protect
/status. - Toggle “Include /status” to fetch detailed capabilities.
- Adjust discovery timeout if your network is slow.
- Provide a Bearer token if your devices protect
You can run the discovery CLI directly:
uv run discover.py --timeout 3.0 --status --token <TOKEN>Key flags:
--timeoutseconds (default 3.0)--servicemDNS type (default_opensentry._tcp.local.)--jsonJSON output only--statusfetch/statusfor each device--tokenBearer token for/status
GET /api/discover?timeout=3.0&status=1&token=...- Returns discovered devices with optional
status.
- Returns discovered devices with optional
GET /api/devices·POST /api/devices·PATCH /api/devices·DELETE /api/devices- Manage the pinned device list used by the UI.
GET /api/status?ip=<ip>&port=<port>&token=<bearer>&timeout=1.5- Proxy
/statuswith optional Bearer token.
- Proxy
GET /api/device_status?ip=<ip>&port=<port>&token=<bearer>- Returns raw device
/statusor 502 on failure.
- Returns raw device
GET /stream_proxy?ip=<ip>&port=<port>&path=/video_feed&token=<bearer>- Minimal streaming proxy for testing.
- Test helpers:
GET /api/test/tcp,GET /api/test/health,GET /api/test/stream,GET /api/test/run_all - Settings:
GET /api/settings,POST /api/settings - OAuth utility:
GET /api/oauth2/test?base_url=...
-
Nothing shows up in discovery
- Increase timeout (e.g.,
6.0s). - Verify mDNS and multicast on your network.
- Test device directly:
curl -sS http://<ip>:<port>/health curl -sS -H "Authorization: Bearer $OPENSENTRY_API_TOKEN" http://<ip>:<port>/status
- Increase timeout (e.g.,
-
OAuth2 shows “Invalid OAuth2 callback”
- Ensure the callback URI in your provider exactly matches how you access the app (host/port).
- This app signs the
statevalue (HMAC) and also stores it in session; either form will validate. If the provider still fails, check app logs for:OAuth2 callback invalid: ...and adjust host/port or redirect URIs.
-
Provider down / maintenance
- The app displays an “OAuth2 Unavailable” page and offers to “Use local login for now”. This is session-only and does not change saved settings.
- Project metadata in
pyproject.toml(Python 3.12+, deps: Flask, requests, zeroconf). - App entrypoint:
main.py(Flask). - Templates in
templates/. - Discovery helpers:
discover.py(mDNS),probe.py(reachability, health, status tests). - Persistent stores:
config_store.json(settings),devices_store.json(devices) — created on first write.
- This is a development-focused tool. Do not expose it to the internet as-is.
- Set a strong
SECRET_KEYin production, serve via HTTPS, and setSESSION_COOKIE_SECURE=True.
MIT