Skip to main content

opennvr-app-sdk

The SDK for building vision apps on OpenNVR, the self-hosted AI video platform. Cameras, Tier-0 detections, pluggable inference, an event bus, recordings, an operator alert inbox, users and per-camera permissions, and a catalog every deployment opens — all already running. You write the rule, the model or the workflow; this package is the only import you need.

Apache-2.0 — ship your app under any licence, closed included, with no fee to OpenNVR. Why that holds even though the platform core is AGPL: LICENSING.md.

pip install opennvr-app-sdk

An app in one function

from opennvr_app_sdk import App

app = App("loitering", name="Loitering", version="1.0.0", category="perimeter")
app.param("dwell_s", float, default=30.0)
app.metric("alerted", label="Alerts fired")

@app.on_detection("person", zone="driveway", dwell="$dwell_s")
def loitering(event):
    event.alert(f"Person loitering on {event.camera}", severity="high")
    app.store["alerted"] = app.store.get("alerted", 0) + 1

if __name__ == "__main__":
    raise SystemExit(app.run())

That app subscribes to the platform's detections, serves the registry contract (/health, /state, /manifest, config form, actions), registers itself in the App Catalog, is issued its own credential, and fires alerts that reach the operator inbox — none of which you wrote. The zone becomes an editor named driveway that the operator draws on a camera still; dwell_s becomes a field in the config form they can tune; alerted becomes a tile on the app's dashboard. The presence timer, the once-per-episode latch, the alert envelope and the config file are the SDK's.

Declaring a surface implements it: @app.action adds an operator button, @app.ui an embedded page, @app.on_license the gate for a paid app, @app.state / app.metric a dashboard, and inside a rule event.publish() reaches other apps while event.nvr is the platform.

See it fire before you touch Docker:

opennvr-app new loitering        # a runnable app + tests
opennvr-app dev                  # run it against a simulated camera
opennvr-app validate .           # what a reviewer would check
opennvr-app spec                 # its OpenAPI 3.1 document

Your app self-describes: the contract server it gets for free serves OpenAPI 3.1 at /openapi.json and AsyncAPI 3.0 at /asyncapi.json, both generated from your manifest — a declared action is a path with a typed body, a declared param is a schema. Point Swagger UI or a client generator at a running app and it works.

The reference

opennvr.org/sdk — every export, in tiers, generated from these docstrings. Start at the front door: six names, and most apps need nothing else.

One example per class

cookbook/ is a runnable file per class — how it is constructed or subclassed, and which APIs it uses. Every file is imported and exercised by the test suite, so an example that references a name the SDK no longer exports breaks in CI rather than misleading you months later. Start at 01_app_facade.py.

…or the class underneath it

App compiles to a Detector. When a rule outgrows the decorators, write the Detector directly — same process, same manifest, same alerts:

from opennvr_app_sdk import Alert, AppManifest, Detector, Param, app

class Loitering(Detector):
    manifest = AppManifest(
        id="loitering", name="Loitering", version="1.0.0", category="perimeter",
        requires_tasks=["object_detection"],
        params=[Param("dwell_s", float, default=30.0)],
    )

    def setup(self):
        self.present = self.keyed_state(ttl=10.0)   # forgets a camera 10 s after the last person

    def on_detections(self, camera_id, detections, event):
        people = [d for d in detections if d.get("label") == "person"]
        if not people:
            return
        rec = self.present.touch(camera_id)
        if rec.age >= self.cfg.dwell_s and not rec.alerted:
            rec.alerted = True
            yield Alert(title="Loitering", description=f"{len(people)} person(s) for {rec.age:.0f}s",
                        camera_id=camera_id, severity="medium")

if __name__ == "__main__":
    raise SystemExit(app(Loitering).run())

FrameApp drives its own inference, AlertSubscriber consumes the alert bus and DomainEventSubscriber consumes contracted domain events — same shape, different input.

The platform, from an app

from opennvr_app_sdk import OpenNVR            # or opennvr_app_sdk.aio.AsyncOpenNVR

nvr = OpenNVR()                                 # OPENNVR_URL + the app's key
for cam in nvr.cameras():                       # only cameras assigned to this app
    jpeg = nvr.snapshot(cam)
    det = nvr.ai.infer("yolov8", jpeg, task="object_detection", camera_id=cam.handle)
nvr.state.set("last_seen", {"cam1": 12.5})      # durable, survives restarts
clips = nvr.recordings("cam1").list(start="2026-09-05T00:00:00Z")
mine = nvr.alerts.inbox(unacked=True)

Where to go next

Compatibility: the app-registry contract is versioned (api_version) and tested in the OpenNVR repository on every change; min_sdk_version only moves when an older SDK would misbehave. Issues tagged sdk at open-nvr/open-nvr.

Metadata

Release files for opennvr-app-sdk 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for opennvr-app-sdk 0.7.0
File Size Uploaded
opennvr_app_sdk-0.7.0.tar.gz 260.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for opennvr-app-sdk 0.7.0
File Interpreter ABI Platform
opennvr_app_sdk-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 454.5 kB

Release files / opennvr_app_sdk-0.7.0.tar.gz

Download URL opennvr_app_sdk-0.7.0.tar.gz
Size 260.8 kB
Tags Source
SHA-256 checksum
How to use checksums
8d82528bfce1a6a52df55fa6121b7949322e604a8ba79a7fa9f2989174063fd2
BLAKE2b-256 checksum
How to use checksums
61cf70997fe63307026900f730f1eaf1a79b15bb0906952bb5fc9ef3df84f4a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / opennvr_app_sdk-0.7.0-py3-none-any.whl

Download URL opennvr_app_sdk-0.7.0-py3-none-any.whl
Size 193.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cca2bbc78c11af71a105a220ae2a4db2a0333efebca2356bb616ed2026bd437e
BLAKE2b-256 checksum
How to use checksums
6ad83f4148bb649aee243cc3de61944c78fed972524dc6a84c95086543724a39
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.8.0

2 release files

This release

0.7.0 This release

2 release files

0.5.0

2 release files

0.4.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page