Brigada Málaga · FireFighter
Something is burning in Málaga. Who goes?
A call comes in. The dispatcher places it on the map, sees which crews are closest, assigns one and talks to it directly, browser to browser, while every decision is written to an audit trail.
Coordination app first built in 2017–2018, rebuilt with AbeFlow in 2026. Local training version with fictional data; not an approved emergency service, and not the fire brigade’s official emblem.
01
Central
Priority queue, hazards, elapsed time, crews assigned, map and crew board.
02
Mi equipo
Confirm or reject, leave, arrive, share position, finish. Big buttons.
03
Comunicaciones
Text, quick messages and voice notes, peer to peer, with receipts.
04
Registro
Every operational change, searchable and exportable to JSON.
The app
Six screens, one incident
A replica of the real V2 console with the training scenario: follow a house fire from the first call to the audit log. It plays by itself; use the arrows to move between screens.
Coordinación en tiempo real
Central de operaciones.
Intervenciones activas
00
Avisos abiertos
Equipos disponibles
05
5 equipos en el espacio
En intervención
00
En camino o en el lugar
Confirmaciones pendientes
00
Respuesta de los equipos
Architecture
One small server, many screens
The same Node.js process serves the frontend, the HTTP API and WebRTC signalling. Every operational command goes through the domain core and SQLite; chat goes browser to browser. It installs with npm ci and runs with no database server, no map key and no Docker.
Why one process?
Map & location
Where is everyone, and how sure are we?
Leaflet draws OpenStreetMap tiles; crews share their GPS position from the browser. Distances are straight lines from the last known position, and every position carries its age. Click the map to move the fire.
How are distances and positions handled?
Click anywhere on the map to move the incident
Closest first
- 1. B-02469 m
- 2. B-011.0 km
- 3. E-011.5 km
- 4. B-032.6 km
- 5. R-012.9 km
Great-circle distance from the last recorded position: a straight line, not a route or an ETA.
How old is B-03’s position?
20 s agoReciente
Positions are never refreshed artificially: under 30 s recent, under 2 min aging, then stale.
Could B-01 really be at La Malagueta?
The server rejects a jump longer than GPS accuracy + 100 m + 300 km/h × elapsed time.
Direct communication
Talking without the server in the middle
Browsers cannot find each other on their own. WebRTC needs a short introduction, called signalling: the server passes notes between them until they agree on a connection. After that, messages go straight from one browser to the other.
How can two browsers talk directly?
Step 1 · through the server (HTTP)
Join
Each tab asks the server for an ephemeral session on this incident. Only the dispatcher and assigned crews are allowed in.
What the server sees
Who is present, and the negotiation (SDP offers and answers, ICE candidates), in expiring in-memory mailboxes.
What it never sees
The messages and voice notes. The chat is temporary; orders and assignments still go through the server and the audit log.
| Transport | What it is | Used for | Trade-off |
|---|---|---|---|
| HTTP request | client asks, server answers | Orders, state, and here also signalling, polled every 1–3 s | every command is validated and logged |
| WebSocket | one open two-way pipe to the server | Signalling and positions in the 2017 prototype | still through the server |
| WebRTC data channel | a direct, encrypted pipe between two browsers | Text, quick messages and voice notes in V2 | no server in the middle; needs signalling to start |
Limits, stated in the project: no STUN/TURN yet, so it is tested between browsers on the same machine; identities are declared by the client, so it is not audited end-to-end encrypted messaging; voice notes are up to 20 s and 1 MB, text up to 4,000 characters, always rendered as text. The emergency radio remains the official channel.
2017 → 2026
Same idea, rebuilt
The first version was a freelance prototype. The new one keeps the old code as history and moves every rule into a core that can be tested, audited and eventually scaled.
| 2017 prototype | 2026 V2 | |
|---|---|---|
| Frontend | AngularJS 1 · Bower | Native ES modules · responsive CSS · dark theme, day mode |
| Server | Node.js · LoopBack 3 | One Node.js 24 process, no framework |
| Data | MongoDB | SQLite (node:sqlite), WAL, one transaction per command |
| Map | Google Maps | Leaflet 1.9.4 served locally · OpenStreetMap tiles, attributed |
| Signalling | WebSocket server | HTTP mailboxes polled every second |
| Chat & voice | WebRTC data channels | WebRTC data channels with receipts, size limits, backpressure |
| Rules | In the UI | Domain core: roles, transitions, idempotency, audit |
| Tests | — | Unit tests + Playwright end to end, including real WebRTC |