Skip to content

Access control — BirdLense Hub

How optional passwords split view, contribute, and admin capabilities. Default install: no password = full local trust.

Русский


Configuration keys

general:
  # Full access: settings, feeder, system, processor restart
  settings_password: ""

  # Optional: labeling & exports without admin (empty = single-password mode)
  contributor_password: ""

Rules:

  • Both empty → legacy “open hub” (same as today for home labs).
  • Only settings_password → one tier; unlock behaves as admin for all gated actions.
  • Both set → verify-password returns role: admin (matches settings_password first) or contributor.

Roles

Role Typical user Scope
Viewer Guest, read-only share Browse UI, exports that stay public in your policy
Contributor Volunteer labeler Unknowns, species fixes, iNaturalist crop, dataset export, reports
Admin Owner Everything Contributor has plus settings, feeder dispense, storage purge, processor restart

Exact UI gates follow settings_check_access() (admin) and contributor_or_admin_access() (contributor + admin) in code.


Permission matrix

Viewer (not unlocked)

Action Allowed
Overview, Timeline, Live, species pages
Play video on recording page (/api/ui/videos/:id/stream) ✅ (unless general.require_auth_for_video_stream is true)
PDF report, timeline CSV/JSON/eBird (if you expose them without lock) ✅*
Correct species / Unknowns
iNaturalist export crop
Feeder dispense
Settings
System (purge, scan, regenerate, logs…)
Dataset ZIP export

*Depends on route-level checks; sensitive exports require Contributor+.

Contributor

Action Allowed
Everything Viewer
Species correction, Unknowns
iNaturalist
Dataset export (where exposed to contributor)
Feeder dispense
Settings
Destructive system actions

Admin

Action Allowed
Everything Contributor
Feeder POST /api/ui/feed/dispense
Settings
System tools, restart processor

Session

After successful POST /api/ui/settings/verify-password:

session['access_role'] = 'admin' | 'contributor'
session['settings_unlocked'] = True   # admin path; contributor may differ

Server checks access_role on each gated request.

MCP: Valid Authorization: Bearer <MCP_TOKEN> can satisfy admin-level checks for automation (settings_check_access()), so protect tokens like root passwords.

Rate limiting (verify-password)

Brute-force protection on POST /api/ui/settings/verify-password:

Rule Value
Window 60 seconds (rolling, per client IP)
Failed attempts 5 wrong passwords → further requests get 429 until the window cools down
HTTP 429 + JSON {"ok": false, "error": "Too many attempts"} + header Retry-After: 60 (seconds)
Success A correct password clears the failure counter for that IP

Client IP behind nginx: the app uses X-Real-IP, then the first address in X-Forwarded-For, then remote_addr (client_ip_for_rate_limit() in util.py). Nginx sets X-Real-IP and X-Forwarded-For for /api in nginx/standalone.conf.template. If Gunicorn is exposed without a trusted reverse proxy, clients could spoof these headers — restrict who can reach port 8000.

Tests: TestVerifyPasswordRateLimit in app/web/tests/test_api.py (run via make test-web).


Feeder API

POST /api/ui/feed/dispense requires settings_check_access()Admin (or valid MCP Bearer where implemented). Otherwise 403.


Security notes

  • Passwords in YAML are plain text today — prefer restricted file permissions; consider env-based secrets for production.
  • Use HTTPS when exposing the UI beyond localhost so session cookies are not leaked.
  • Processor and MCP use separate secrets (PROCESSOR_SECRET, MCP_TOKEN).

Future ideas (not roadmap commitments)

Community / donation UX (leaderboards, “unlock with support”, badges) is listed under Future work candidates in ROADMAP. Config hook general.donate_url adds optional support links in the app chrome and Food card — see CONFIGURATION.


See also

CONFIGURATION · API · SECURITY · GLOSSARY