cctv-stack 0.3.0__tar.gz → 0.3.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/.gitignore +9 -3
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/PKG-INFO +198 -123
- cctv_stack-0.3.1/README.md +251 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/pyproject.toml +9 -1
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/__init__.py +2 -2
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/atomic.py +3 -3
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/cleanup.py +6 -5
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/clip.py +2 -2
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/config.py +45 -6
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/config_cli.py +82 -21
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/db.py +2 -2
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/ffprobe.py +3 -2
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/health.py +2 -2
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/paths.py +6 -6
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/storage.py +9 -9
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/timeutil.py +4 -4
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/uploader.py +5 -5
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/worker.py +1 -1
- cctv_stack-0.3.0/README.md +0 -178
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/LICENSE +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/dispatch.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/frigate.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/logging.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/materialise.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/migrations/0001_init.sql +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/migrations/0002_finding_kinds.sql +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/mqtt.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/reviews.py +0 -0
- {cctv_stack-0.3.0 → cctv_stack-0.3.1}/src/cctv/sweep.py +0 -0
|
@@ -14,6 +14,13 @@ venv/
|
|
|
14
14
|
.ruff_cache/
|
|
15
15
|
.mypy_cache/
|
|
16
16
|
|
|
17
|
+
# Editor state is per-machine, not project configuration
|
|
18
|
+
.vscode/
|
|
19
|
+
|
|
20
|
+
# Personal agent memory and local agent settings. This repository is public; nothing here is
|
|
21
|
+
# project configuration and nothing here belongs in its history.
|
|
22
|
+
.claude/
|
|
23
|
+
|
|
17
24
|
# uv.lock is deliberately COMMITTED — do not ignore it (ADR-0018).
|
|
18
25
|
.uv-cache/
|
|
19
26
|
|
|
@@ -51,6 +58,5 @@ config/frigate.generated.yml
|
|
|
51
58
|
# deliberately ignored and is deleted once its VERIFY items are measurements.
|
|
52
59
|
spike/
|
|
53
60
|
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
# data and Frigate's state out of git while leaving the harness itself reviewable.
|
|
61
|
+
# Internal planning and notes — not for public consumption.
|
|
62
|
+
.private/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cctv-stack
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A packaging-first self-hosted CCTV stack built on Frigate.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aravindmyd/cctv-stack
|
|
6
6
|
Project-URL: Repository, https://github.com/aravindmyd/cctv-stack
|
|
@@ -222,6 +222,8 @@ Requires-Python: >=3.12
|
|
|
222
222
|
Requires-Dist: pyyaml>=6.0
|
|
223
223
|
Requires-Dist: tzdata>=2024.1
|
|
224
224
|
Provides-Extra: dev
|
|
225
|
+
Requires-Dist: mkdocs-material>=9.6; extra == 'dev'
|
|
226
|
+
Requires-Dist: mkdocs<2,>=1.6; extra == 'dev'
|
|
225
227
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
226
228
|
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
227
229
|
Provides-Extra: mqtt
|
|
@@ -232,179 +234,252 @@ Description-Content-Type: text/markdown
|
|
|
232
234
|
|
|
233
235
|
# cctv-stack
|
|
234
236
|
|
|
237
|
+
[](https://pypi.org/project/cctv-stack/)
|
|
238
|
+
[](https://pypi.org/project/cctv-stack/)
|
|
235
239
|
[](https://github.com/aravindmyd/cctv-stack/actions/workflows/ci.yml)
|
|
236
|
-
[](https://github.com/aravindmyd/cctv-stack/actions/workflows/publish.yml)
|
|
237
240
|
|
|
238
|
-
|
|
239
|
-
|
|
241
|
+
`cctv-stack` is a Python toolkit and container image for a self-hosted CCTV pipeline built
|
|
242
|
+
on **[Frigate](https://github.com/blakeblackshear/frigate)**.
|
|
240
243
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
3. **Independent retention** — local cleanup that never deletes an un-uploaded clip.
|
|
244
|
-
4. **Health surfaces** — each service reports whether it is actually working.
|
|
244
|
+
Frigate does camera ingest, detection, recording and review segmentation. `cctv-stack`
|
|
245
|
+
adds the four pieces it leaves to the operator:
|
|
245
246
|
|
|
246
|
-
|
|
247
|
-
|
|
247
|
+
1. **Canonical clips** — one deterministic file per review item.
|
|
248
|
+
2. **Durable backup** — clips are uploaded to Backblaze B2 exactly once.
|
|
249
|
+
3. **Upload-aware retention** — local cleanup never deletes a clip that is not safely
|
|
250
|
+
uploaded.
|
|
251
|
+
4. **Health surfaces** — the uploader and cleanup expose `/health` and `/metrics`.
|
|
252
|
+
|
|
253
|
+
It is not a replacement for Frigate. It is the missing layer around it.
|
|
254
|
+
|
|
255
|
+
**Current release: `0.3.0`.** Clips, B2 upload, local retention and health are
|
|
256
|
+
implemented. Cloud retention, an aggregate status page and the one-command installer are
|
|
257
|
+
not — see [`CONTRIBUTING.md`](CONTRIBUTING.md) for the exact state and roadmap.
|
|
248
258
|
|
|
249
259
|
---
|
|
250
260
|
|
|
251
|
-
##
|
|
261
|
+
## Install
|
|
252
262
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
| Upload to Backblaze B2, exactly once | ✅ |
|
|
257
|
-
| Local retention (upload-aware) | ✅ |
|
|
258
|
-
| Cloud retention and reconciliation | ⬜ |
|
|
259
|
-
| Aggregate status page and alerting | ⬜ |
|
|
260
|
-
| One-command client installer and signed bundle | ⬜ |
|
|
261
|
-
| Backup, restore, upgrade | ⬜ |
|
|
263
|
+
```bash
|
|
264
|
+
pip install cctv-stack
|
|
265
|
+
```
|
|
262
266
|
|
|
263
|
-
|
|
264
|
-
why it is deferred, is in [`docs/plans/backlog.md`](docs/plans/backlog.md). The client-site
|
|
265
|
-
delivery plan is [`docs/plans/packaging.md`](docs/plans/packaging.md).
|
|
267
|
+
Requires **Python 3.12+**. The distribution is `cctv-stack`; the import package is `cctv`.
|
|
266
268
|
|
|
267
|
-
|
|
269
|
+
Optional extras:
|
|
268
270
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
| Extra | Adds | Needed by |
|
|
272
|
+
|---|---|---|
|
|
273
|
+
| `mqtt` | `paho-mqtt` | `cctv-clip-worker` when subscribing to `frigate/reviews` |
|
|
274
|
+
| `storage` | `boto3` | `cctv-uploader` for B2/S3 |
|
|
275
|
+
| `dev` | pytest, ruff | Contributors |
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
pip install "cctv-stack[mqtt,storage]"
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Container image
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
docker pull ghcr.io/aravindmyd/cctv-stack:0.3.0
|
|
273
285
|
```
|
|
274
286
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
287
|
+
Tags published per release: `:0.3.0`, `:0.3`, and the commit tag `:sha-<short>`.
|
|
288
|
+
A moving `:edge` tag follows `master`. If the registry package is private, run
|
|
289
|
+
`docker login ghcr.io` first. The image includes `ffmpeg`/`ffprobe`.
|
|
278
290
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
291
|
+
## Commands
|
|
292
|
+
|
|
293
|
+
| Command | What it does |
|
|
294
|
+
|---|---|
|
|
295
|
+
| `cctv-config validate --cameras cameras.yml` | Validate the camera configuration. Exits 78 (`EX_CONFIG`) on an invalid config |
|
|
296
|
+
| `cctv-config generate --cameras cameras.yml --out frigate.yml` | Generate Frigate's configuration from `cameras.yml` |
|
|
297
|
+
| `cctv-clip-worker` | Turn completed Frigate reviews into canonical clips |
|
|
298
|
+
| `cctv-uploader` | Upload clips to B2 through the persisted retry state machine |
|
|
299
|
+
| `cctv-cleanup` | Enforce local retention after a successful upload |
|
|
300
|
+
|
|
301
|
+
The three services are long-running and configuration-driven; they take no command-line
|
|
302
|
+
arguments. Run them under systemd, Docker, or Compose.
|
|
282
303
|
|
|
283
304
|
## Quick start
|
|
284
305
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
first real run pulls the Frigate image and builds the CCTV image, so expect several
|
|
288
|
-
minutes.
|
|
306
|
+
The only required input is a `cameras.yml`. This minimal file is valid; `${VAR}` values
|
|
307
|
+
are read from the environment and an unset variable is a hard error.
|
|
289
308
|
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
309
|
+
```yaml
|
|
310
|
+
site:
|
|
311
|
+
location: home
|
|
312
|
+
timezone: UTC
|
|
313
|
+
|
|
314
|
+
cameras:
|
|
315
|
+
front-door:
|
|
316
|
+
display_name: Front Door
|
|
317
|
+
rtsp:
|
|
318
|
+
main: ${CAMERA_FRONT_DOOR_RTSP}
|
|
319
|
+
sub: ${CAMERA_FRONT_DOOR_SUB}
|
|
294
320
|
```
|
|
295
321
|
|
|
296
|
-
|
|
322
|
+
Validate it and generate Frigate's config:
|
|
297
323
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
| `config/cameras.yml` | Camera names, clip padding, retention policy | **No** — gitignored |
|
|
324
|
+
```bash
|
|
325
|
+
export CAMERA_FRONT_DOOR_RTSP='rtsp://user:pass@192.168.1.10:554/stream1'
|
|
326
|
+
export CAMERA_FRONT_DOOR_SUB='rtsp://user:pass@192.168.1.10:554/stream2'
|
|
302
327
|
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
cctv-config validate --cameras cameras.yml
|
|
329
|
+
cctv-config generate --cameras cameras.yml --out frigate.yml
|
|
330
|
+
```
|
|
305
331
|
|
|
306
|
-
|
|
307
|
-
|
|
332
|
+
`validate` checks the camera model and the environment substitution. `generate` writes a
|
|
333
|
+
Frigate config with a `GENERATED FILE — DO NOT EDIT` header.
|
|
308
334
|
|
|
309
|
-
|
|
335
|
+
### Running the services
|
|
310
336
|
|
|
311
|
-
```
|
|
312
|
-
|
|
313
|
-
data
|
|
314
|
-
|
|
337
|
+
```bash
|
|
338
|
+
export CCTV_CONFIG_DIR="$PWD"
|
|
339
|
+
export CCTV_DATA_DIR="$PWD/data"
|
|
340
|
+
export CCTV_FRIGATE_URL="http://127.0.0.1:5000"
|
|
315
341
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
**UTC** — both are deliberate ([ADR-0017](docs/adr/0017-time-handling.md)).
|
|
342
|
+
cctv-clip-worker
|
|
343
|
+
```
|
|
319
344
|
|
|
320
|
-
|
|
321
|
-
|
|
345
|
+
Run each service in its own process. `cctv-clip-worker` works without MQTT (sweep-only);
|
|
346
|
+
set `CCTV_MQTT_HOST` to receive reviews immediately. `cctv-uploader` reports `DISABLED`
|
|
347
|
+
and uploads nothing when `CCTV_STORAGE_BUCKET` is empty; a bucket with no key exits `78`.
|
|
322
348
|
|
|
323
|
-
|
|
349
|
+
> `ffprobe` must be on `PATH` for the clip worker. The container image includes it; a
|
|
350
|
+
> `pip install` does not install ffmpeg.
|
|
324
351
|
|
|
325
|
-
|
|
326
|
-
|---|---|
|
|
327
|
-
| **Frigate** | Camera ingest, detection, recording and review segmentation |
|
|
328
|
-
| **clip-worker** | Materialises completed reviews at the canonical path, with a sidecar |
|
|
329
|
-
| **uploader** | Uploads to B2 through a storage adapter, with a persisted retry state machine. Reports `DISABLED` when no bucket is configured and exits `78` on a half-configuration |
|
|
330
|
-
| **cleanup** | Enforces local retention; refuses to delete a clip whose upload is not terminal-success |
|
|
331
|
-
| **config** | One-shot generator: `cameras.yml` → Frigate's config (INV-3) |
|
|
332
|
-
| **mosquitto** | MQTT broker, internal only |
|
|
333
|
-
| **status-collector** | ⬜ not built yet |
|
|
352
|
+
## Configuration
|
|
334
353
|
|
|
335
|
-
|
|
354
|
+
There are two kinds of configuration, and they must not be mixed:
|
|
336
355
|
|
|
337
|
-
|
|
|
356
|
+
| File | Holds | Secret? |
|
|
338
357
|
|---|---|---|
|
|
339
|
-
|
|
|
340
|
-
|
|
|
341
|
-
| Cleanup | `http://127.0.0.1:8083/health` | Local retention state |
|
|
342
|
-
| Frigate API, MQTT | not published | Reachable only on the compose network |
|
|
358
|
+
| `cameras.yml` | Site name, timezone, cameras, clip policy, retention | No — but `${VAR}` values come from the environment |
|
|
359
|
+
| environment / `.env` | RTSP URLs, B2 keys, paths, ports, intervals | **Yes** |
|
|
343
360
|
|
|
344
|
-
|
|
361
|
+
The full template is [`.env.example`](.env.example). The settings most consumers need:
|
|
345
362
|
|
|
346
|
-
|
|
363
|
+
| Variable | Default | Meaning |
|
|
364
|
+
|---|---|---|
|
|
365
|
+
| `CCTV_CONFIG_DIR` | `/config` | Directory containing `cameras.yml` |
|
|
366
|
+
| `CCTV_DATA_DIR` | `/data` | Root for `clips/` and the state database |
|
|
367
|
+
| `CCTV_DB` | `$CCTV_DATA_DIR/cctv.db` | SQLite state store |
|
|
368
|
+
| `CCTV_FRIGATE_URL` | `http://frigate:5000` | Frigate API base URL. Port 5000 must stay on a private network |
|
|
369
|
+
| `CCTV_MQTT_HOST` | unset | Unset means sweep-only; no broker required |
|
|
370
|
+
| `CCTV_SWEEP_INTERVAL_S` | `60` | Worst-case delay before a missed review is noticed |
|
|
371
|
+
| `CCTV_STORAGE_BUCKET` | empty | Empty means cloud backup `DISABLED` |
|
|
372
|
+
| `CCTV_STORAGE_KEY_ID` | empty | B2 application key ID |
|
|
373
|
+
| `CCTV_STORAGE_APPLICATION_KEY` | empty | B2 application key |
|
|
374
|
+
| `CCTV_STORAGE_REGION` | `us-west-004` | B2 region |
|
|
375
|
+
| `CCTV_UPLOADER_HEALTH_PORT` | `8082` | Uploader `/health` and `/metrics` |
|
|
376
|
+
| `CCTV_CLEANUP_HEALTH_PORT` | `8083` | Cleanup `/health` and `/metrics` |
|
|
377
|
+
| `INTERNAL_API_TOKEN` | empty | Enables the uploader admin endpoints when set |
|
|
378
|
+
|
|
379
|
+
Keep `.env` out of version control. RTSP URLs and B2 keys are credentials.
|
|
380
|
+
|
|
381
|
+
Camera names must be lowercase kebab-case (`front-door`): they become directory names and
|
|
382
|
+
cloud object keys.
|
|
383
|
+
|
|
384
|
+
## What it produces
|
|
347
385
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
386
|
+
```text
|
|
387
|
+
$CCTV_DATA_DIR/
|
|
388
|
+
├── clips/
|
|
389
|
+
│ └── <camera>/YYYY/MM/DD/HH-MM-SS.mp4
|
|
390
|
+
│ <camera>/YYYY/MM/DD/HH-MM-SS.mp4.json
|
|
391
|
+
└── cctv.db
|
|
392
|
+
```
|
|
351
393
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
[`docs/runbooks/rotate-credentials.md`](docs/runbooks/rotate-credentials.md).
|
|
394
|
+
The clips tree is rendered in the **site timezone**. The sidecar records the review id,
|
|
395
|
+
event ids, requested boundaries, measured coverage, codecs, size and SHA-256. The cloud
|
|
396
|
+
object key is `<location>/clips/<path-inside-clips>`.
|
|
356
397
|
|
|
357
|
-
|
|
358
|
-
|
|
398
|
+
A clip appears **after** the visit ends: Frigate keeps the review open until its alert
|
|
399
|
+
cutoff passes. There is no partial clip by design.
|
|
359
400
|
|
|
360
|
-
|
|
401
|
+
### Health endpoints
|
|
361
402
|
|
|
362
|
-
|
|
|
403
|
+
| Service | Endpoint | Notes |
|
|
363
404
|
|---|---|---|
|
|
364
|
-
|
|
|
365
|
-
|
|
|
366
|
-
|
|
|
367
|
-
| Python package | `pypi.org/project/cctv-stack` | A `v*` tag |
|
|
368
|
-
| Wheel and sdist | Workflow artefact, and GitHub Release on tags | Every merge / tag |
|
|
405
|
+
| `cctv-uploader` | `http://127.0.0.1:8082/health`, `/metrics` | `DISABLED` without a bucket |
|
|
406
|
+
| `cctv-cleanup` | `http://127.0.0.1:8083/health`, `/metrics` | Local retention state |
|
|
407
|
+
| Uploader admin | `/admin/queue`, `/admin/retry` | Mounted only when `INTERNAL_API_TOKEN` is set |
|
|
369
408
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
409
|
+
Bind these to loopback. Nothing in the package needs an inbound port from the LAN.
|
|
410
|
+
|
|
411
|
+
## Python API
|
|
412
|
+
|
|
413
|
+
The supported contract is the CLI, the environment variables, and the HTTP health
|
|
414
|
+
endpoints. The importable modules are tested, but pre-1.0 names may change between minor
|
|
415
|
+
versions.
|
|
416
|
+
|
|
417
|
+
```python
|
|
418
|
+
from cctv.config import ConfigError, load_cameras_config
|
|
419
|
+
|
|
420
|
+
try:
|
|
421
|
+
config = load_cameras_config("cameras.yml")
|
|
422
|
+
except ConfigError as exc:
|
|
423
|
+
raise SystemExit(f"invalid camera config: {exc}")
|
|
424
|
+
|
|
425
|
+
print(config.site.location)
|
|
426
|
+
for name, camera in config.enabled_cameras().items():
|
|
427
|
+
print(name, camera.display_name, camera.record.mode)
|
|
373
428
|
```
|
|
374
429
|
|
|
375
|
-
|
|
376
|
-
(`cctv-clip-worker`, `cctv-config`, `cctv-uploader`, `cctv-cleanup`) but **not** Frigate,
|
|
377
|
-
Compose, or site configuration. The complete client-site install is the signed release
|
|
378
|
-
bundle in [`docs/plans/packaging.md`](docs/plans/packaging.md).
|
|
430
|
+
Storage is available behind the `storage` extra:
|
|
379
431
|
|
|
380
|
-
|
|
432
|
+
```python
|
|
433
|
+
import os
|
|
434
|
+
from cctv.storage import S3StorageAdapter
|
|
381
435
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
436
|
+
adapter = S3StorageAdapter(
|
|
437
|
+
bucket=os.environ["CCTV_STORAGE_BUCKET"],
|
|
438
|
+
access_key_id=os.environ["CCTV_STORAGE_KEY_ID"],
|
|
439
|
+
secret_access_key=os.environ["CCTV_STORAGE_APPLICATION_KEY"],
|
|
440
|
+
region=os.environ.get("CCTV_STORAGE_REGION", "us-west-004"),
|
|
441
|
+
)
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
`cctv.__version__` reports the installed package version.
|
|
445
|
+
|
|
446
|
+
## Complete installation
|
|
387
447
|
|
|
388
|
-
|
|
389
|
-
|
|
448
|
+
For a full CCTV site — Frigate, MQTT, the worker, uploader and cleanup — use the Compose
|
|
449
|
+
deployment in this repository:
|
|
450
|
+
|
|
451
|
+
```bash
|
|
452
|
+
git clone https://github.com/aravindmyd/cctv-stack.git
|
|
453
|
+
cd cctv-stack
|
|
454
|
+
./install.sh
|
|
390
455
|
```
|
|
391
456
|
|
|
392
|
-
The
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
457
|
+
The first run creates `.env` and `config/cameras.yml`; edit both and run it again. See
|
|
458
|
+
[`docs/install.md`](docs/install.md) for the operator procedure and
|
|
459
|
+
[`CONTRIBUTING.md`](CONTRIBUTING.md) for how releases are built and delivered.
|
|
460
|
+
|
|
461
|
+
## Compatibility
|
|
462
|
+
|
|
463
|
+
| | |
|
|
464
|
+
|---|---|
|
|
465
|
+
| Python | 3.12 or newer |
|
|
466
|
+
| Frigate | 0.18 API and MQTT contract (verified) |
|
|
467
|
+
| Platforms | Linux x86_64/arm64; macOS for development |
|
|
468
|
+
| Cloud backup | Backblaze B2 through its S3-compatible endpoint; optional |
|
|
469
|
+
| Inbound network | None required |
|
|
396
470
|
|
|
397
471
|
## Documentation
|
|
398
472
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
- [
|
|
402
|
-
- [
|
|
403
|
-
- [
|
|
404
|
-
- [
|
|
405
|
-
- [
|
|
473
|
+
Full documentation: **https://aravindmyd.github.io/cctv-stack/**
|
|
474
|
+
|
|
475
|
+
- [Install](docs/install.md) — Docker Compose from the published image, or pip
|
|
476
|
+
- [Configure](docs/configure.md) — `cameras.yml`, `.env`, adding a camera
|
|
477
|
+
- [Frigate UI login](docs/frigate-ui.md)
|
|
478
|
+
- [Health](docs/health.md) — endpoints and what to check first
|
|
479
|
+
- [Recover failed uploads](docs/recover-uploads.md)
|
|
480
|
+
- [Glossary](docs/glossary.md) — the vocabulary these docs use
|
|
481
|
+
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — development, CI and release process
|
|
406
482
|
|
|
407
483
|
## License
|
|
408
484
|
|
|
409
|
-
Apache-2.0 — see [`LICENSE`](LICENSE).
|
|
410
|
-
[`docs/plans/opensourcing.md`](docs/plans/opensourcing.md).
|
|
485
|
+
Apache-2.0 — see [`LICENSE`](LICENSE).
|