OpenEye is an open, lightweight video management system for any ONVIF / RTSP camera. Pursuit-mode multi-cam, AI triage queue, forensic search, signed case manifests - and a companion Linux agent (Node + ffmpeg) that handles ONVIF discovery and RTSP-to-HLS transmuxing for you.
Real captures from the console against the bundled demo dataset - not mockups.

Spatial follow-the-suspect mode. Focus camera at the center, the 8 nearest cameras ringed around it by compass direction. Click any neighbor to recenter; the layout re-pivots in place.
All of this works against the bundled demo data - no hardware required to evaluate the UX.
Follow a suspect across the camera mesh. Focus camera at center, the 8 nearest cameras placed by compass direction from your floor plan. Click any neighbor to recenter; Smart Handoff highlights the neighbor where fresh person/vehicle detections fire.
1×1 / 2×2 / 3×3 / 4×4 grids, multi-select picker, saved views, sync play/pause/mute, per-tile digital zoom + pan, fullscreen, full hotkey set.
Severity-coded inbox with rationale, confidence, signal tag and zone. Acknowledge / escalate / resolve / dismiss in one key. Escalation fires notifications automatically.
Scrub the archive by object class, color, time, site and confidence. Thumbnails carry overlaid bounding boxes and deep-link straight to the moment in playback.
Bundle clips, events and detections into cases. Generate signed JSON manifests with a deterministic SHA-256 over the case contents for chain of custody (video bundling is on the roadmap).
24-hour scrubber with variable speed, recording-segment markers, and bookmark-to-case for the current frame range.
Upload a floor plan per building level, calibrate scale, place each camera with yaw and FOV cone. The same geometry powers Pursuit's neighbor solver.
Guided 4-step wizard: pick a site, WS-Discovery scan on the LAN (run by the agent), vendor URL templates (Axis/Hikvision/Dahua/Reolink), ffprobe validation, save.
The agent transmuxes each RTSP stream to LL-HLS on demand and signs short-lived URLs (HMAC). No separate re-streamer required for the live grid.
ONVIF ContinuousMove and preset recall from the live tile overlay; agent forwards commands to the camera.
Door registry plus an ingest endpoint (HID/Brivo/etc.) - badge events appear in the live access dashboard next to camera events.
Per-channel routing for webhook / Slack (email channel is queued, no SMTP relay shipped). Severity thresholds, delivery log, and a manual dispatch button per triage event.
Per-agent liveness, per-camera FPS / bitrate / codec, recording storage and disk usage - at a glance, with red/amber/green thresholds.
11 signals (Intrusion, Tailgating, Loitering, PPE, Crowd, Weapon, Vehicle-in-zone…) with admin enable/disable toggles, ready to be evaluated by your inference worker.
Email + Google sign-in. First user is admin; everyone else is viewer. Per-camera ACLs and row-level security on every table.
Collapsible sidebar grouped by Monitor / Investigate / Manage / System, command palette (⌘K), live status bar, light/dark themes, mobile view.
These are deliberately delegated to open-source projects that already do them well. Wiring is documented in Setup.
The agent can register clips and the console plays them back, but there is no built-in 24/7 disk writer, retention enforcer, or storage pool manager.
Use instead: Frigate or MediaMTX for continuous recording + retention; OpenEye indexes the resulting clips.
Triage, search and policy signals are surfaced beautifully, but actual object/person/vehicle detection happens in an external worker that posts to /api/public/v1/detections and /events.
Use instead: Frigate (with Coral/CPU/GPU), CodeProject.AI, or a custom YOLO worker that hits OpenEye's ingest endpoints.
PTZ is supported; backchannel audio and intercom are not.
Use instead: go2rtc handles ONVIF backchannel today if you need it.
Toggling a policy in the UI marks it active, but evaluation against a live stream is the inference worker's job - OpenEye just stores the resulting events with their policy id.
Use instead: Frigate zones/objects or a custom analytics worker tagged with the policy id.
For the quick start you only need a Linux box and our agent - that's enough to discover cameras, stream live, and run AI triage on seeded events. The other items are optional upgrades when you want continuous recording or your own inference pipeline.
One box to run the agent (and optionally Frigate). 4 cores + 8 GB RAM handles ~16 cameras of transmuxing; add a Coral USB or a small GPU only if you also want detection.
Handles ONVIF WS-Discovery, ffprobe-based camera validation, RTSP→LL-HLS transmuxing with signed URLs, PTZ commands, snapshots and heartbeats. Generate a token and grab the docker-compose from /setup.
OpenEye indexes recordings (POST /api/public/v1/recordings) and plays them back, but does not write continuous 24/7 video to disk. Pair with an NVR for retention.
If you want the triage queue, forensic search and policies to fire from real video, run an inference worker that POSTs to /api/public/v1/events and /detections.
Modern browsers require HTTPS for camera/microphone, WebRTC and Service Workers. The generated Caddyfile terminates TLS in front of the agent and console.
The minimum useful setup is: OpenEye console + the OpenEye agent on a Linux host. The agent discovers your cameras over ONVIF and transmuxes RTSP to LL-HLS that the browser can play directly.
docker-compose.yml and Caddyfile. The bundle wires the agent to your console with a signed bearer token.# On your Linux box mkdir -p /opt/openeye && cd /opt/openeye # (drop the docker-compose.yml + Caddyfile downloaded from /setup here) docker compose up -d docker compose logs -f agent
GetProfiles / GetStreamUri calls auto-fill the RTSP URL. ffprobe validates the stream before you save.frigate-config.yml and a docker-compose service for it.# docker-compose.yml fragment (also generated for you in /setup)
services:
frigate:
image: ghcr.io/blakeblackshear/frigate:stable
devices: ["/dev/bus/usb:/dev/bus/usb"] # Coral
environment:
OPENEYE_URL: https://vms.example.com
OPENEYE_TOKEN: ${AGENT_TOKEN}
volumes:
- ./frigate-config.yml:/config/config.yml:ro
- /mnt/nvr:/media/frigatevms.example.com and proxies the agent + console. WebRTC and modern browsers require it.# Caddyfile (auto-generated)
vms.example.com {
reverse_proxy /api/agent/* agent:8787
reverse_proxy /hls/* agent:8787
reverse_proxy * openeye:8080
} ┌─────────────────────┐ ONVIF/RTSP ┌───────────────────────┐ LL-HLS ┌───────────────────┐
│ IP cameras (any) │ ────────────► │ OpenEye agent │ ───────► │ OpenEye console│
│ Axis · Hikvision │ WS-Discovery │ (single Go/Node bin) │ signed │ (this app) │
│ Dahua · Reolink │ ◄──── PTZ ─── │ ffmpeg transmux │ URLs └─────────┬─────────┘
└─────────────────────┘ └─────────┬─────────────┘ │
│ webhook ▼
┌──────────────── ┼ ─────────────┐ ┌───────────────────┐
│ Optional add-ons │ events │ Notifications │
│ Frigate (NVR + AI) │ ──────► │ Slack/webhook │
│ go2rtc, MediaMTX (re-stream) │ └───────────────────┘
└─────────────────────────────────┘Everything inside the agent box ships with OpenEye. Frigate / go2rtc / MediaMTX are optional drop-ins when you want continuous NVR recording or your own inference pipeline.
The console runs against demo data out of the box - first user becomes admin. Wire a real camera whenever you're ready.
Open the console