zoneminder

GitHub

ZoneMinder is a free, open source Closed-circuit television software application developed for Linux which supports IP, USB and Analog cameras.

RAW Rules

AGENTS.md

# AI Agent Development Guide for ZoneMinder

> **Note**: This file guides AI coding agents (Claude Code, GitHub Copilot, Cursor, etc.) working on ZoneMinder.
> CLAUDE.md is a symlink to this file.

## Quick Reference (MANDATORY RULES)

1. **Testing First**: Write tests BEFORE/DURING implementation - NEVER skip. Tests written "later" never get written.
2. **Build System**: CMake with C++17, out-of-source builds in `build/`. In-source builds pollute the repo.
3. **Feature Workflow**: GitHub Issue -> Feature Branch -> Implement FULLY -> Tests Pass -> Get Approval -> Merge to master. Feature branches keep master stable.
4. **Commits**: Conventional format (`feat:`/`fix:`/`test:`), reference issues (`refs #n` or `fixes #n`). Enables automated changelog.
5. **Pre-Commit**: Tests pass, build succeeds, linting clean, no warnings.
6. **Never merge without user approval. Never leave features half-implemented.**

---

## What is ZoneMinder?

Linux-based CCTV surveillance system: capture, analysis, recording, and monitoring of video cameras.

- **C++ daemons** (`src/`) - Capture (`zmc`), analysis (`zma`), streaming (`zms`), utility (`zmu`)
- **PHP web interface** (`web/`) - Bootstrap + jQuery UI, AJAX endpoints in `web/ajax/`, views in `web/views/`
- **REST API** (`web/api/`) - CakePHP 2.x, controllers in `web/api/app/Controller/`, JWT auth
- **Perl scripts** (`scripts/`) - Daemon control (`zmdc.pl`), migrations (`zmupdate.pl`), filtering (`zmfilter.pl`)
- **MySQL database** (`db/`) - Schema in `db/zm_create.sql.in`, migrations in `db/zm_update-*.sql` (60+ versions)

---

## Architecture

### Data Flow

```
Camera -> zmc (capture) -> Shared Memory -> zma (analysis) -> Event Recording
                              |                                    |
                           zms (streaming)                   Database + Disk
                              |                                    |
                        Web Browsers <- Web Interface/API <- MySQL Storage
```

### Key Patterns

- **Shared Memory**: `zmc` writes frames to `/dev/shm`; `zma` and `zms` read from same buffers. Zero-copy for performance — this is why ZM can handle many cameras on modest hardware.
- **Monitor-Centric**: `Monitor` class (`src/zm_monitor.cpp/h`) is the central orchestrator. One monitor = one DB row = one set of daemons. Most changes to camera handling flow through this class.
- **Pluggable Cameras**: Abstract `Camera` base with: `LocalCamera` (V4L2), `RemoteCameraRTSP`, `RemoteCameraHTTP`, `FFmpegCamera`, `LibVLCCamera`, `LibVNCCamera`. Add new camera types by subclassing Camera.
- **Event-Driven Recording**: Motion detection triggers `Event` objects with pre/post alarm buffers. Lifecycle: Create -> Record -> Close -> Archive/Delete. Events are the core unit of recorded footage.
- **Multi-Server Clustering**: Database-coordinated distributed architecture with shared monitors and storage.

### Directory Structure (Key Paths)

```
src/                      C++ core (86+ source files): zm_monitor.*, zm_camera.*, zm_event.*, zm_zone.*, zm_image.*, zm_ffmpeg*.*
web/                      PHP web interface
  ajax/                   AJAX handlers
  includes/               PHP libraries and functions
  views/                  UI templates
  skins/                  Themes (classic skin)
  js/, css/               Frontend assets
  api/app/Controller/     CakePHP REST API controllers
  api/app/Model/          CakePHP REST API models
scripts/                  Perl system management (.in templates)
db/                       Schema (zm_create.sql.in) and migrations (zm_update-*.sql)
tests/                    Catch2 unit tests + test data
misc/                     Server configs (apache, nginx, systemd)
dep/                      Vendored deps (catch2, jwt-cpp)
.github/workflows/        CI/CD pipelines
```

---

## Development Workflow (MANDATORY)

### For every feature or bug fix:

1. **Create GitHub Issue**: `gh issue create --title "..." --label enhancement|bug`
2. **Create Feature Branch**: `git checkout -b <issue>-<description>` from master
3. **Write failing test first** (TDD)
4. **Implement** - follow existing patterns, keep changes minimal
5. **Build & test** - must all pass (see Build and Testing sections)
6. **Commit** in logical chunks with conventional messages
7. **Request user approval** - never merge without it
8. **After approval**: merge to master, delete branch, push, verify issue closes

### Technology-Specific Notes

- **C++ changes** (`src/`): Rebuild required, run ctest. Daemons must be restarted to pick up changes.
- **PHP/JS changes** (`web/`): No rebuild needed — PHP is interpreted, changes are live immediately. Test in browser + ESLint.
- **Database schema** (`db/`): Create migration file `zm_update-X.X.X.sql` for existing installs, also update `db/zm_create.sql.in` for fresh installs. Both must result in the same schema.
- **Perl scripts** (`scripts/`): Edit `.in` template files (NOT generated scripts). cmake substitutes `@ZM_*@` variables to produce the final scripts. When testing perl changes, any perl script will try to read /etc/zm/zm.conf and needs to be run as a user with permission to read it. Best to run with sudo -u www-data
- **API changes** (`web/api/`): Clear CakePHP cache (`tmp/cache/`) if you change models or routes. Test endpoints with curl.

### Example Workflow

```bash
gh issue create --title "Add event favorites" --label enhancement
# Note issue number, e.g. #42
git checkout master && git pull && git checkout -b 42-event-favorites

# Write test first, implement, build & test
cd build && cmake --build . && ctest
npx eslint .

# Commit
git add <files>
git commit -m "feat: add favorites toggle to event view refs #42"

# Ask user for approval, then after approval:
git checkout master && git merge 42-event-favorites
git push origin master
git branch -d 42-event-favorites && git push origin --delete 42-event-favorites
```

---

## Build System

```bash
# Standard build (out-of-source required)
mkdir build && cd build
cmake ..
cmake --build .

# Debug build with tests
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_TEST_SUITE=ON ..
cmake --build .

# Build specific target
cmake --build . --target zmc
```

### Key CMake Options

| Option | Values | Default | Notes |
|--------|--------|---------|-------|
| `CMAKE_BUILD_TYPE` | Release/Debug/Optimised/Profile | Release | `Profile` = `-O2 -g -fno-omit-frame-pointer` for perf/flamegraph |
| `BUILD_TEST_SUITE` | ON/OFF | OFF | Enables Catch2 tests |
| `ENABLE_WERROR` | ON/OFF | OFF | Warnings as errors (CI uses ON) |
| `ASAN` | ON/OFF | OFF | AddressSanitizer |
| `TSAN` | ON/OFF | OFF | ThreadSanitizer (mutually exclusive with ASAN) |
| `ZM_CRYPTO_BACKEND` | openssl/gnutls | | |
| `ZM_JWT_BACKEND` | libjwt/jwt_cpp | | |
| `ZM_TARGET_DISTRO` | FreeBSD/fc/el/OS13 | (Debian) | Platform-specific paths |
| `ZM_ONVIF` | ON/OFF | ON | ONVIF camera support |

---

## Testing Requirements (MANDATORY)

### Workflow

1. Write a failing test that reproduces the issue or validates the feature
2. Implement the fix/feature
3. Run tests - verify they PASS
4. Run full test suite for regressions
5. Only then commit

### C++ Unit Tests (Catch2)

```bash
# Build with tests
cmake -DBUILD_TEST_SUITE=ON .. && cmake --build .

# Run all tests
ctest
# Or directly with more output
./tests/tests

# Run specific test / list tests
./tests/tests "[Box]"
./tests/tests --list-tests
./tests/tests "~[notCI]"
```

**Test location**: `tests/` directory
**Existing modules**: `zm_box.cpp`, `zm_comms.cpp`, `zm_crypt.cpp`, `zm_font.cpp`, `zm_poly.cpp`, `zm_utils.cpp`, `zm_vector2.cpp`
**Framework**: Catch2 with custom header `tests/zm_catch2.h`, main in `tests/main.cpp`
**Test data**: `tests/data/fonts/`

### JavaScript Linting

```bash
npx eslint .        # Lint all
npx eslint --fix web/js/              # Auto-fix
```

ESLint config: `eslint.config.js` (ESLint 9 flat config, Google style guide). Runs in CI via `.github/workflows/ci-eslint.yml`.

### PHP/API Testing

Manual testing required. Test endpoints with curl/browser. Verify JSON responses, auth (session + JWT), error handling, DB persistence.

### Database Migration Testing

```bash
sudo zmupdate.pl --check              # Check for updates
sudo zmupdate.pl                      # Apply migrations
```

When creating migrations: test upgrade path AND verify fresh install matches migrated schema.

### CI Notes

- Unit tests are **DISABLED** in CI (`BUILD_TEST_SUITE=0`) — some tests need hardware/network access. Running locally goes beyond CI requirements but is still expected for local development.
- CI does: multi-platform builds (Debian/Ubuntu/CentOS with GnuTLS/OpenSSL + libjwt/jwt_cpp matrix), ESLint, CodeQL security scanning

---

## Code Standards

### C++ (`src/`)

- **C++17** standard, follow existing patterns in file/module
- **Memory**: RAII, smart pointers where appropriate
- **Error handling**: Exceptions for exceptional cases, return codes for expected errors
- **Logging**: Use printf-style `Debug(1, "msg %s", val)`, `Info(...)`, `Warning(...)`, `Error(...)`, `Fatal(...)` — NOT iostream style. The number in Debug() is verbosity level (1-9).

### PHP (`web/`, `web/api/`)

- PSR-12 where practical, follow existing conventions
- **Security**: Validate ALL user input, use prepared statements (NOT string interpolation — legacy code has SQL injection bugs we're fixing), sanitize output with htmlspecialchars/json_encode, CSRF protection on forms
- CakePHP 2.x framework in `web/api/` — we're stuck on 2.x due to migration cost, don't try to upgrade it

### JavaScript (`web/js/`, inline in PHP)

- Google JavaScript Style Guide (ESLint configured)
- jQuery + Bootstrap (legacy codebase), ES6+ where browser support allows

### Perl (`scripts/`)

- Edit `.in` template files, NOT generated scripts — cmake substitutes variables like `@ZM_LOGDIR@` to produce the final scripts
- Use `ZoneMinder::` modules, `ZoneMinder::Logger` for logging
- Rerun cmake after editing `.in` files to regenerate the actual scripts

---

## Commit Standards

**Conventional format** with issue references:

```
feat|fix|test|docs|chore|refactor|perf|style: description refs #N
```

- `refs #N` references issue; `fixes #N` closes it
- Imperative mood ("add feature" not "added feature")
- Be detailed and descriptive, no vague summaries
- One logical change per commit
- **No superlative language** ("comprehensive", "critical", "major", "massive") — AI agents tend to use these; they make commit logs unreadable
- Split unrelated changes into separate commits — makes bisecting and reverting possible

---

## Pre-Commit Checklist (Single Source of Truth)

Before committing or claiming complete:

- [ ] Tests written/updated BEFORE or DURING implementation
- [ ] Build succeeds: `cmake --build .` (C++ changes)
- [ ] C++ tests pass: `ctest` or `./tests/tests`
- [ ] JavaScript linting passes: `npx eslint .`
- [ ] Manual testing done (PHP/web changes)
- [ ] No compiler warnings or linter errors
- [ ] Code follows existing patterns
- [ ] Commit messages follow conventional format with issue reference
- [ ] Feature is COMPLETE (not half-implemented)
- [ ] State which tests were run and their results

**NEVER commit if**: tests failing, tests missing for new code, build fails, warnings exist, feature incomplete.

---

## Debugging Quick Reference

```bash
# Debug build
cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .

# AddressSanitizer
cmake -DCMAKE_BUILD_TYPE=Debug -DASAN=ON .. && cmake --build .

# Debug logging
export ZM_DBG_LEVEL=9 ZM_DBG_LOG=/tmp/debug.log
./src/zmc -m 1

# Log locations: /var/log/zm/ (zmc_m1.log, zma_m2.log, etc.)
# Runtime log level: kill -USR1 <pid> (increase) / kill -USR2 <pid> (decrease)
```

---

## Contributing (For Humans)

- **Bug reports & features**: [GitHub Issues](https://github.com/ZoneMinder/zoneminder/issues) (read [posting rules](https://github.com/ZoneMinder/ZoneMinder/wiki/Github-Posting-Rules) first)
- **Support**: [Forums](https://forums.zoneminder.com), [Slack](https://zoneminder-chat.slack.com), [Discord](https://discord.gg/tHYyP9k66q)
- **PRs**: Fork -> feature branch -> implement + test -> PR. Maintainers merge after review.
- **Docs**: https://zoneminder.readthedocs.org (source in `docs/`)

---

## grepai - Semantic Code Search

**Use grepai as PRIMARY tool for code exploration and search.**

### When to Use grepai (REQUIRED)

Use `grepai search` INSTEAD OF Grep/Glob for:
- Understanding what code does or where functionality lives
- Finding implementations by intent ("authentication logic", "error handling")
- Exploring unfamiliar parts of the codebase

Only use Grep/Glob for exact text matching (variable names, imports, specific strings) or file path patterns.

If grepai fails (not running, index unavailable), fall back to Grep/Glob.

### Usage

```bash
grepai search "user authentication flow" --json --compact
grepai search "JWT token validation" --json --compact
```

### Call Graph Tracing

```bash
grepai trace callers "HandleRequest" --json
grepai trace callees "ProcessOrder" --json
grepai trace graph "ValidateToken" --depth 3 --json
```

### Workflow

1. `grepai search` to find relevant code
2. `grepai trace` to understand function relationships
3. `Read` tool to examine files from results
4. Grep only for exact string searches if needed