Architecture
GEM is a single Node.js server that owns every device connection and all automation, with thin clients connecting to it over REST and WebSocket. This page describes the moving parts.
The server
server.js is the entry point. It boots the core services, connects to PostgreSQL, loads every configured device and brings its driver online, seeds the attribute registry, and begins serving clients. The server is the single source of truth for live state — clients never talk to devices directly.
Singletons
Most core subsystems are singletons, accessed via getInstance() rather than constructed ad hoc — the server, the data layer, auth, control, backup, monitoring, discovery, access control, and crypto utilities. This guarantees one authoritative instance of each per process.
In-memory state
A defining characteristic of GEM: the server keeps live, in-memory collections of devices, zones, subsystems, AV zones, AV sources, and more, with each entity's current attribute values merged onto the object. Reads of current state come from these collections, not from database queries — the database holds configuration and history, while memory holds now. This is what lets the system answer "what's the kitchen brightness right now?" instantly.
Drivers
Every device is driven by a driver — a class that knows one equipment type's protocol. Drivers extend a base (device_base.js) or one of the generic transports (TCP, HTTP, serial, IR) and implement connect(), command(), response(), and disconnect(). A driver translates GEM's generic verbs (on, off, open, set_level) into the device's native commands, and parses device feedback back into attribute updates. GEM ships with 100+ drivers.
The API surfaces
Clients reach the server three ways:
- REST —
/api/data/[entity]for CRUD on configuration,/api/control/[action]for commands. - WebSocket — Socket.io for real-time state, subscriptions, and most interactive operations. Every socket event is automatically enumerated for role-based access control.
- Web Services —
/web_service/[service]for external integrations.
See the API Reference for details.
The data layer
PostgreSQL, accessed through Sequelize models in the gem schema, stores all configuration and history. The same attribute mechanism carries both stored configuration and runtime state; the attribute registry catalogs what each attribute means (its type, defaults, options, and flags). Secure attributes (passwords, keys) are encrypted at rest with AES-256-GCM.
Clients
The web app and touchpanel UIs are built with Svelte; the mobile app wraps the same client in a native shell. All of them are clients of the one server — there is no separate application server per client type.