← Back to Blog

Project 09 — PAS: Perimeter Alert System

A self-hosted security platform that turns any Android phone into a sensor node — motion, sound, geofence and impact detection, live P2P video via WebRTC, and an admin dashboard, running entirely on Cloudflare's edge at zero cost.

The Problem

Commercial perimeter security — motion sensors, cameras, monitoring contracts — is expensive, and most of it is hardware you don’t own tied to a subscription you can’t leave. Almost everyone already owns a device with a camera, microphone, GPS and accelerometer: a phone. The brief was to turn spare Android phones into proper sensor nodes, with a real admin dashboard, live video, and alerting — without hardware, app store distribution, or a hosting bill.

Architecture: Phone as Node, Edge as Backend

The node itself is a Chrome PWA, not a native app — pas_node.html running under Chrome’s Wake Lock, camera, microphone, GPS and DeviceMotion APIs, installable straight from the browser with no app store review. Everything behind it runs on Cloudflare: Workers for the API, D1 (SQLite at the edge) for storage, and a Durable Object per node acting as a stateful signaling room:

[ Android PWA Node ] → HTTPS/WSS → [ Cloudflare Edge ]
                                      Workers (API)
                                      D1 (SQLite)
                                      Durable Objects (signaling)
                                    → WebRTC P2P → [ Admin Dashboard ]

Live video never touches the Worker at all — the Durable Object only negotiates the WebRTC handshake (SDP offer/answer, ICE candidates) between node and dashboard, then gets out of the way while video streams peer-to-peer. Snapshots on trigger events go to imgBB’s free API instead of R2, which keeps storage off the paid tier entirely.

Auth: Two Token Scopes, One Signing Key

Admins and nodes both authenticate with JWTs signed via Web Crypto HMAC-SHA256, but they carry different scopes — an admin token can manage every device, while a node token is bound to a single device ID and can’t reach admin routes at all. Pairing a new phone works like a lot of IoT onboarding: the dashboard generates a short-lived pairing token as a QR code, the node scans it, and the Worker exchanges it for a permanent device-scoped token.

Config Sync With a Version Counter

Each device’s device_config row carries trigger thresholds (motion, sound in dB, geofence radius, impact in g-force), alarm behavior (torch pattern, siren duration), and a config_version integer. Rather than pushing config over the signaling socket, the node polls and compares versions — a stale local config means a fresh pull is due. That keeps the config path decoupled from the real-time signaling path, so a node with a flaky WebSocket connection can’t fall permanently out of sync with its trigger settings.

Webhooks: HMAC-Signed, Retried, Filterable

Outbound webhooks are matched against a per-webhook events filter, and every delivery is HMAC-signed with a per-webhook secret so a receiving endpoint can verify the payload actually came from PAS. Failed deliveries are tracked in their own table — status, response code, attempt count, last-attempted timestamp — which is what makes retry logic and delivery debugging possible after the fact instead of disappearing into a fire-and-forget POST.

What This Project Demonstrates

  • Durable Objects used for what they’re actually good at — a small amount of coordination state (a signaling room) per entity, not as a database substitute
  • WebRTC P2P kept off the Worker’s request path entirely, so live video bandwidth costs nothing at the edge
  • Two-scope JWT auth (admin vs. per-device node tokens) instead of one flat permission model
  • A version-counter pattern for config sync that tolerates an unreliable real-time channel
  • HMAC-signed, retry-tracked webhooks — signed delivery and delivery history, not just a webhook URL and a hope

Live dashboard: https://pasv3.pages.dev/demo.html Node PWA: pasv3.pages.dev/pas_node.html GitHub: pasv3