Help centre

Edge agent

The small program that watches your DVR and ships only the moments with motion.

Edge agent v2 — September 2026

The edge agent is a small program that runs on the shop's network — on the Suraag Raspberry Pi appliance or on any Linux, Windows or Mac PC. It watches your DVR's cameras, records a short rolling buffer continuously without re-encoding, and uploads only the moments with motion to your Suraag server, with a few seconds of pre-roll so the approach is never missed. It survives power cuts and internet outages: clips wait in a durable local queue and are retried with back-off.

At a typical shop's 5 % motion duty, four cameras upload about 1 GB a day.

1. What you need

2. Install

pip install -r requirements.txt        # ffmpeg must also be on the PATH

On the Pi appliance the agent lives in /opt/suraag-edge and is already installed.

3. Enrol

python main.py enroll --server https://suraag.xyz --code <ONE-TIME-CODE> --device-id <DEVICE-ID>

The server exchanges the single-use code for a long-lived device token, which the agent stores in ~/.suraag-edge/device.json (readable by its owner only). A used or expired code is refused — ask your administrator for a fresh one. The token can be revoked from the console at any time.

4. Discover the DVR's cameras

python main.py discover                                  # ONVIF broadcast on the LAN
python main.py discover --ip 192.168.1.108 --user admin --password <PASSWORD>   # probe one DVR

Discovery tries, in order: ONVIF WS-Discovery with GetStreamUri per channel, then the known RTSP URL patterns for the Hikvision, Dahua / CP Plus and XMEye families. Every candidate URL is checked with a six-second ffprobe before it is reported, and the output is a ready-to-paste channels: block for the configuration file. If nothing is found: check the DVR's IP and credentials, enable ONVIF in its network settings, or copy the RTSP URL from its manual.

RTSP cheat-sheet:

FamilyMain streamSubstream
Hikvisionrtsp://user:pass@IP:554/Streaming/Channels/101…/Channels/102
CP Plus / Dahuartsp://user:pass@IP:554/cam/realmonitor?channel=1&subtype=0…&subtype=1
XMEyertsp://IP:554/user=USER&password=PASS&channel=1&stream=0.sdp…&stream=1.sdp

5. Configure

cp config.example.yaml config.yaml
work_dir: ~/.suraag-edge/work

channels:
  front_counter:
    rtsp: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=1&subtype=0"
    rtsp_substream: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=1&subtype=1"
    camera_id: cam_shop_front_counter    # stable id — keeps analysis history together
    segment_seconds: 15
    quiet_seconds: 10                     # motion window closes after this much stillness
    # always_upload: true                 # ship everything (testing only — costly)
  entrance:
    rtsp: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=2&subtype=0"
    rtsp_substream: "rtsp://admin:PASSWORD@192.168.1.108:554/cam/realmonitor?channel=2&subtype=1"
    camera_id: cam_shop_entrance

6. Check the motion gate offline

python main.py test-motion --video some_clip.mp4

Prints the windows the gate would ship, e.g. 120s video → 3 windows, 34s motion (28% duty), so you can tune quiet_seconds before going live.

7. Run

python main.py run --config config.yaml

The agent prints one line per upload (uploaded front_counter_…mp4 (queued) or (dedup) when the server already had that clip) and a back-off notice with the pending count when the uplink is down. Every minute it sends a heartbeat with the pending count and channel list — this is what drives Last seen in the console and the live / quiet / offline status on Live Watch. Uploads larger than the single-request limit go through the chunked, resumable upload endpoint.

As a service (Linux)

systemd/suraag-edge.service runs the agent as user suraag from /opt/suraag-edge, restarts it on failure and starts it on boot:

sudo cp systemd/suraag-edge.service /etc/systemd/system/
sudo systemctl enable --now suraag-edge
journalctl -u suraag-edge -f

8. What leaves the shop

Only motion clips from the channels you configured, the heartbeat described above, and the enrolment request. The agent never opens ports inbound, never streams continuously, and never uploads anything while always_upload is off and there is no motion. Clips are purged from the server after the retention period your administrator set (14 days by default), together with everything derived from them.

9. Updating

The agent is plain Python. To update, replace the files in the install directory with the new release, keep config.yaml and ~/.suraag-edge/, and restart the service (sudo systemctl restart suraag-edge). Enrolment survives updates; you do not need a new code unless the device was revoked.

10. Troubleshooting

SymptomCheck
enrollment failed: 4xxThe code was used, expired or revoked — request a new one. Confirm --server includes https://.
No streams foundDVR IP and credentials; ONVIF enabled on the DVR; try --ip with the DVR's address; paste the RTSP URL from the manual.
upload failed … retry with backoffInternet uplink. Clips are safe in the spool and go out when it returns.
Tile shows SIGNAL LOST in the consoleThe heartbeat has not arrived for over an hour — the agent is stopped or the PC is off.
Alerts arrive lateCheck the analysis engine's state in System Health; uploads queue while it is stopped.
Too many clipsRaise quiet_seconds, lower the substream resolution, or make sure always_upload is not set.

Support: support@suraag.xyz with the device ID, the last lines of the agent's output and the DVR model.