Architecture overview
The building blocks behind SCAD Maps: web app, API, realtime layer, storage and the one-instance-per-event model.
SCAD Maps is deliberately small. Each event or organisation gets its own instance: one web address, one database, one set of files and one set of accounts. Nothing is shared between instances, so a busy festival cannot slow down a conference, and a problem in one place cannot leak into another. This page describes the parts of an instance and how they work together, for technical reviewers and for anyone who wants to know what happens when they tap the map.
The parts of an instance#
| Part | What it does |
|---|---|
| Web app | The installable progressive web app that runs in the browser. It is plain JavaScript on a WebGL map engine, with no framework to load on the first screen. It is served as static files by a web server that also serves the site plans and photos. |
| API | One Node.js service that holds all the rules: sign-in, permissions, notes, areas, tasks, chat, uploads, search, integrations and export. It also runs the live connection and the periodic clean-up jobs. |
| Database | PostgreSQL, the durable store for accounts, content, history and settings. |
| Live cache | Redis, used for the live-location store, which expires by itself, and to fan out live messages. |
| File storage | Plain folders on disk for site-plan tiles, uploaded photos and documents, and voice notes. They are mounted into the instance, so they survive an upgrade. |
| Reverse proxy | Terminates HTTPS and forwards to the web server. It is part of the operator's environment, not of SCAD Maps. |
The web server sends everything under /api and the live connection to the API, and serves all other files itself. Map tiles and images are read straight from disk, which is why a crowd panning across a large site does not put load on the API.
One process per instance#
The API is a single process on purpose. Its short-lived state, such as who is online, which sessions were just revoked and how many requests each address has made, lives in that process's memory and is therefore always the single truth. Adding a second copy would create disagreement about exactly those things: a revoked session could still be accepted by one copy, and a person could appear online on one and offline on another. Instances are sized for one event, and one process comfortably serves the crowd of a large one, so you scale by giving each event its own instance rather than by adding processes to one. The database and live cache would allow more, and the design would have to be revisited before running several API copies.
On shutdown, the API finishes what it is doing and exits within a few seconds, so an upgrade interrupts people for the length of a restart and no longer.
How a request flows#
- The page loads from the web server. The service worker takes over, so later visits open instantly, even offline.
- The app signs in or restores its session and receives a short-lived access token.
- The app asks for what is on screen: notes, areas and tasks inside the visible map rectangle, page by page. The API filters by what this person may see and answers.
- The app opens a live connection and joins the map regions and channels that cover the screen. From then on it receives only changes for those regions.
- A change is made by any device. The API checks permissions, writes to the database, records it in the activity log, and sends the change to everyone who has that region or channel open.
The rules of the map, such as which people are shown a note and which are not, are applied in step three and step five by the same server code. The events themselves are listed in Realtime events and the requests in REST API.
Realtime layer#
The live connection uses Socket.IO over WebSockets, with a fallback to long polling on networks that block WebSockets. Two channels of it are used:
- The main connection carries content changes (notes, areas, tasks), presence, chat, and administrator notifications.
- The location connection carries live positions. Positions are grouped by a small geographic cell, sent to the server at most 10 times a second per device, and delivered to viewers in batches every half second. A cell that has nobody in it for a while is retired automatically.
Content is addressed to map regions of roughly 0.1 degrees, about 11 kilometres from north to south, so a phone looking at one part of a venue is not sent changes from another part. An area that spans several regions is sent to all of them. Presence is combined and delivered once per second, so a crowd joining at the gates costs a single small update rather than hundreds.
Data model#
The core records are deliberately few:
- Users, roles and sessions. Access level, groups (custom roles), status and settings.
- Notes, areas and work items. Each has a position or outline, optional tags, groups and checklist, and a history of changes. Tasks and incidents share one record type with a kind field.
- Chat. Channels for the whole instance, per group and per pair of people, with messages and read markers.
- Attachments. Photos, documents and voice notes, linked to the record they belong to.
- Activity. The append-only log behind the History panel, the admin views and export.
- Settings. Views, layer order, QR and access presets, and integration connections.
Positions are stored as plain numbers and searched with a rectangle range scan, and shapes are stored as text outlines. That is enough for the size of an event, and it keeps the database simple to back up and to restore anywhere. Changes to the structure of the data are applied automatically when the API starts, and a failed change stops the start-up instead of running against a half-migrated database.
The map#
- Engine. WebGL vector and raster rendering on a MapLibre-based engine, with drawing tools for areas and a separate Three.js viewer for 360-degree photos.
- Site plans. Served as tile archives or loose tiles from disk, gated by the viewer's layer permissions. See Venue maps.
- Base maps. Public imagery providers, or an optional self-hosted vector basemap.
- Dark mode. The map background is dimmed and site-plan tiles are inverted on a background thread, so plans stay readable in the dark without a second set of tiles.
- 3D. Optional building and drawing-model layers; see 3D buildings and models.
Offline behaviour#
A service worker caches the app itself and, once a venue is downloaded, its tiles in a dedicated store. With venue mode on, tiles are served from that store only, and a tile that was not downloaded is answered with a transparent one rather than a network request. Notes, areas and tasks you have seen are mirrored into the browser's database, and changes made offline wait in an outbox until the connection returns. The full picture for users is in Mobile use and offline.
Background jobs#
The API runs a few timers so no one has to remember maintenance:
- Expired sessions are deleted every hour.
- Uploads that were never attached to anything are removed after a few hours.
- Voice-note audio is deleted when its 7 days are up.
- Activity older than the retention period and old location history are trimmed.
- Overdue incidents are escalated to the next tier, and due-date reminders are sent when notifications are set up.
- Integrations with monday.com and Dropbox run their own retries and reconciliation.
Monitoring and versions#
- The API writes one structured log line per minute with memory use, number of live connections and people online, plus lines for sign-in events and errors from browsers.
- Every build carries a version stamp made of the source revision and a timestamp. The web app sends it with sign-in requests, and the API reports it on a public version endpoint, so it is always possible to tell which build an instance is running.
- A separate fleet panel can watch many instances at once through their own web API: reachability, version, uptime, counts and sign-in state. It does not need access to the servers or the database.
Running your own#
An instance is a small set of containers: web, API, database and cache. Configuration comes from a single environment file; the options are listed in Instance configuration. Security posture, retention and data flows are covered in Security and privacy. If you want SCAD Maps run for you, get in touch.