Camera and NVR Streaming/Management
  • Python 64.8%
  • JavaScript 32.7%
  • Shell 1.3%
  • HTML 0.5%
  • Makefile 0.3%
  • Other 0.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
crypt 32c2e01059 fix(auth): make admin 2FA optional via OPUS_REQUIRE_ADMIN_MFA flag
The original design spec (docs/superpowers/specs/2026-09-08-admin-mfa-design.md)
listed mandatory MFA enforcement as an explicit non-goal — opt-in, self-service.
The implementation contradicted this by hard-gating every admin session behind
2FA enrollment, blocking all API calls and rendering a non-dismissible gate page
right after account creation.

This restores the opt-in intent:

- OPUS_REQUIRE_ADMIN_MFA env flag (default: false) controls the enforcement mode
- When false (default): admin can use the system without 2FA; a dismissible
  enrollment nudge appears once per session with a "Skip for now" confirmation
- When true: current hard-gate behavior (403 on all routes except enrollment
  allowlist, no self-disable, no skip option)
- /api/auth/me exposes require_admin_mfa so the frontend knows the policy
- Admin self-disable (/mfa/disable) is allowed when MFA is optional
- Frontend canDisable in TwoFactorSettings respects the flag
- Typo fix: "Enrol" → "Enroll" in MfaSetup.jsx

Tests: existing mandatory-mode tests now set the flag via fixture; 5 new tests
cover the optional (default) mode — unenrolled admin can access require_auth and
require_admin routes, go2rtc verify, /me reports the flag, and self-disable works.
2026-10-17 16:31:27 -05:00
.claude/skills docs(readme): how to run Grafana/Prometheus and where their credentials live 2026-09-25 08:20:57 -05:00
.github/workflows fix(playback): NVR clips start where you click — long spans cut into 4-min chunks 2026-10-02 08:56:59 -05:00
.scratch docs(wayfinder): correct the branch reference — prereqs are on dev 2026-08-17 10:29:40 -05:00
app fix(auth): make admin 2FA optional via OPUS_REQUIRE_ADMIN_MFA flag 2026-10-17 16:31:27 -05:00
deploy Merge branch 'worktree-trim-api-responses' into dev 2026-08-27 14:55:49 -05:00
docker fix(grafana): 1 GiB / 1 CPU for Grafana; per-core panel point cap; CPU in cores 2026-10-01 11:33:02 -05:00
docs docs(spec): Grafana CPU/memory/disk/health dashboards, node-exporter, NVR gauges 2026-10-01 11:11:03 -05:00
frontend fix(auth): make admin 2FA optional via OPUS_REQUIRE_ADMIN_MFA flag 2026-10-17 16:31:27 -05:00
mobile Update README.md 2026-03-30 14:18:51 -05:00
monitoring fix(grafana): 1 GiB / 1 CPU for Grafana; per-core panel point cap; CPU in cores 2026-10-01 11:33:02 -05:00
proxy fix(proxy): add blob: to Caddy worker-src, assert CSP parity with nginx 2026-09-21 16:49:15 -05:00
scripts chore(docs): second-pass audit — compose invocations, repo URL, stale refs 2026-09-24 13:27:58 -05:00
tests fix(auth): make admin 2FA optional via OPUS_REQUIRE_ADMIN_MFA flag 2026-10-17 16:31:27 -05:00
.dockerignore refactor(layout): move compose to docker/, proxy configs to proxy/ 2026-08-07 15:50:15 -05:00
.editorconfig chore(dev): add ruff, prettier, pre-commit, editorconfig, and Makefile lint/format targets 2026-05-15 14:31:23 -05:00
.env.example feat(nvr-health): nvr_rebooted; unreachable needs 2 misses; 15-min boot grace 2026-10-01 10:27:39 -05:00
.gitattributes for windows compatibility 2026-04-08 16:01:17 -05:00
.gitignore feat(monitoring): reach Grafana and Prometheus off-host, behind logins 2026-09-24 16:52:35 -05:00
.gitleaks.toml chore(security): flag raw-secret files in gitleaks 2026-08-26 13:55:55 -05:00
.pre-commit-config.yaml chore(lint): align ruff to 0.15.13, add gitleaks and bandit hooks 2026-08-25 09:55:11 -05:00
AGENTS.md chore(docs): reorganise and fact-check docs, skills, and agent notes 2026-09-24 10:21:54 -05:00
docker-entrypoint-opus.sh perf(recorder): stage segment cache on local disk; surface drain failures 2026-06-29 13:19:42 -05:00
LICENSE Update LICENSE 2026-03-04 16:00:53 -06:00
Makefile fix(playback): NVR clips start where you click — long spans cut into 4-min chunks 2026-10-02 08:56:59 -05:00
pyproject.toml feat(recordings): auto-calibration, NAS storage support, settings UI overhaul 2026-06-04 08:55:23 -05:00
README.md docs(readme): how to run Grafana/Prometheus and where their credentials live 2026-09-25 08:20:57 -05:00
requirements.txt feat(mfa): add pure TOTP/backup-code logic module 2026-09-09 11:26:54 -05:00
run.py 1. CRITICAL — Flask / nginx 502 (connection refused) 2026-04-15 08:50:42 -05:00
SECURITY.md chore(docs): second-pass audit — compose invocations, repo URL, stale refs 2026-09-24 13:27:58 -05:00

Opus NVR

Watch and record your security cameras, on your own hardware.

License: MIT Docker Python Status


Point your cameras at Opus and it records them around the clock. Open a browser for a live wall of cameras and a timeline you can scrub back through.

It runs on a machine you own. The video stays on your disk. No cloud account, no subscription.

  • Live view — all your cameras at once, or one full size. Under a second behind real life.
  • Recording — everything 24/7, or only when something moves. Set per camera.
  • Search — Opus can tell a person from a car from an animal, so you can skip the wind in the trees.
  • Users — give people access to some cameras and not others, and choose who can download footage (this stops casual saving, not a determined person).
  • Alerts — email or a webhook when a disk fills up or a camera drops off.

Install

You need a Linux machine, Docker, Git and make.

git clone https://git.willowcrest.world/elirtf/opus.git
cd opus
cp .env.example .env

Open .env and fill in the two keys at the top:

openssl rand -hex 32                        # SECRET_KEY
openssl rand -base64 32 | tr '+/' '-_'      # OPUS_ENCRYPTION_KEY

Set OPUS_ENCRYPTION_KEY once and never change it. It encrypts your camera passwords. Change it and they can never be read again — you would re-enter every camera by hand. Back it up.

make up

Open http://localhost and create your admin account. There is no default password.


Next steps

Add cameras — Opus can scan your network for them, or you can paste in an address.

Back up — scripts/backup.sh saves your database and your encryption key. One without the other is useless. How to run it.

Watch from elsewhere — use a private network like Tailscale. Don't expose Opus to the internet directly. Instructions.

Something broken? Usually a stale browser: press F12 → Application → Clear site data, then reload twice. Everything else.


Monitoring (optional)

Prometheus keeps 90 days of history; Grafana shows it as a dashboard — are the services up, which cameras dropped off, is motion detection keeping up, how full is the disk.

make up-monitoring

The first run adds three generated values to .env. Nothing to fill in by hand:

.env key What it is
OPUS_METRICS_TOKEN Lets Prometheus read the app's metrics. Also copied to monitoring/metrics_token.
GRAFANA_ADMIN_PASSWORD Grafana login, user admin.
PROMETHEUS_PASSWORD Prometheus login, user opus. Its hash goes to monitoring/prometheus_web.yml.

See them with grep -E 'GRAFANA|PROMETHEUS' .env.

Address
Grafana http://<host>:3000/d/opus-nvr
Prometheus http://<host>:9090

Only reachable from the Opus machine by default (<host> = 127.0.0.1). To open them to your LAN or Tailscale, add MONITORING_BIND=0.0.0.0 to .env and run make up-monitoring again. Both still ask for a login — but Docker skips the host firewall, so never port-forward 3000 or 9090 on your router.

Changing the Grafana password: editing .env only works before Grafana's first start. After that: docker exec opus-grafana grafana cli admin reset-admin-password <new>, then put the same value in .env.

Page won't load? docker ps | grep -E 'grafana|prometheus' — if neither is listed, run make up-monitoring. Details: Monitoring.


Documentation

Doc What it covers
Recording & storage Where footage goes, how long it's kept, using a NAS
Hardware sizing How much disk and CPU you need
Remote viewing Watching from outside your network
Object detection Telling people from cars from animals
Alerts & monitoring Email/webhook alerts, self-healing, Prometheus/Grafana
Motion notifications Telegram, Discord, Matrix setup
Migrating from an NVR Moving off an existing recorder
Home Assistant MQTT and smart-home integration
H.265 / HEVC Browser codec limits for live view
Installing without Docker Running directly on the machine
Caddy trial (experimental) Opt-in nginx alternative, side by side on its own port
Development Working on Opus itself
Release checklist Camera + phone checks before a release
Project scope What v1 covers
Security checklist OWASP SAMM (SDLC) + WSTG (runtime testing) checklist tailored to Opus

Contributing

Branch off dev, run make check and pytest -q, try it with make up, then open a pull request. One change per request.

Anything touching live view, playback or detection needs someone to look at the screen — the tests cannot see a black tile.

Details: docs/DEV_WORKFLOW.md.


MIT licensed — see LICENSE.