SAC Next: a community alarm platform
Residents raise an alert for security, fire or a medical emergency from a mobile app, a radio remote, a phone call or a text message. The alert sounds the sirens on nearby street panels and appears live in the console of the operations room. I built it as consulting work for a municipal programme. It is in development: it has run against simulated panels only, and it is not deployed.
- Devices
- Street panels with sirens
- Radio remotes
- Phone calls and text messages
- Server
- MQTT broker
- Fastify API
- PostgreSQL with PostGIS
- People
- Live console for the operations room
- Mobile app
- Push and text alerts
From a button to a siren
- Remote, app, call or text
- Checked and recorded
- Nearby sirens sound
- Seen live
- Every message is checked. Panels talk to the server over MQTT, and every message is validated against a schema before it reaches the database. Repeated presses of the same remote become one incident.
- The nearest panels, by distance. A PostGIS query picks the panels near the alert, and they receive the siren command.
- Commands are signed and expire. Each command carries a signature, a one-time value and an expiry, so a replayed or forged command is refused. Remotes use a rolling code.
- Silence is reported, not hidden. A command that no panel confirms is marked expired, logged on the incident and opened as a maintenance ticket, so the operator knows which siren did not sound.
An assistant that knows its limits
- Emergencies come first. Emergency detection runs before anything else, and an emergency gets a fixed answer with call buttons. It never reaches the language model.
- Known questions, known answers. Common questions are answered from a fixed list of topics. Only the rest go to the model, which is told to answer from the programme's knowledge base only.
- Personal data stays home. Emails, national ID numbers and phone numbers are removed before any text leaves the server, and if the model fails the assistant falls back to its own answers.
One deployment per client, personal data protected
- Each client gets its own stack. Database, broker, API and console run as one Docker Compose project on an internal network, and only the proxy is exposed. The broker refuses anonymous connections, and each panel can read only its own commands.
- Personal fields are encrypted. A keyed index still allows exact lookups without keeping the value in clear.
- The audit log is chained. Each entry is hashed with the one before it, so an edited or deleted entry can be detected.
- Backups are encrypted as they are written, so a plain dump never touches the disk, and a script tests restoring one.
- The code does not know its client. A test fails if any source file names the client or contains its coordinates, so the same code can serve another municipality.
Tests
14 Vitest files with 136 unit tests, and three scripted suites: one end to end through the API, MQTT and simulated panels, and two that drive the mobile web app and the console in Chrome. The end-to-end suite imports 1,100 households and 1,100 remotes and checks that importing twice creates no duplicates. Shell scripts test restoring a backup and upgrading with a rollback. There is no CI or load test yet, and nothing has run on real hardware or with users. The source belongs to the engagement and is private, and I am happy to walk through it in an interview.