Edge agent (apps/edge-agent)
The on-device runtime that runs on each Home Assistant host (Raspberry Pi 5). It provides a local REST API + WebSocket for the dashboard, talks to Home Assistant over MQTT, syncs state with the cloud API, and applies OTA updates.
- Workspace:
apps/edge-agent· package name:@signapps/edge-agent - Entry point:
src/index.ts→dist/index.js - Runtime: Express 5 + SQLite (better-sqlite3 + Prisma), default port 18080
Tech stack
| Concern | Choice |
|---|---|
| HTTP / realtime | Express 5 + ws (WebSocket) |
| DB | SQLite (better-sqlite3) via Prisma |
| Messaging | MQTT 5 (mqtt) |
| Auth | JWT, role hierarchy |
| Releases | adm-zip (extract), SHA-256 validation |
| House registry | js-yaml (YAML device config) |
Usage
Run in dev
pnpm --filter @signapps/edge-agent run dev # tsx watch src/index.ts
On boot (src/index.ts) it: loads env, creates the HTTP server, starts the
WebSocket server, connects the MQTT client, optionally loads the house registry,
and starts the background schedulers below.
Build
pnpm --filter @signapps/edge-agent run build # prisma generate && tsc
Deploy
Deployed as a Docker image (multi-arch: arm64 for the Pi, amd64 for dev), built from the monorepo root:
docker build -f apps/edge-agent/Dockerfile -t signapps/edge-agent:latest .
The image runs as a non-root user, mounts /data for the SQLite DB, and its
entrypoint.sh fixes bind-mount permissions, runs prisma migrate deploy, then
starts node dist/index.js. Version bumps and image build/push are handled by
deploy/scripts/release.mjs (pnpm release:edge-agent), which also creates the
git commit + tag for the agent version.
Configuration
| Variable | Default | Purpose |
|---|---|---|
PORT | 18080 | Local API/WebSocket port |
DATABASE_URL | file:/data/edge-agent.db | SQLite path (Prisma) |
JWT_SECRET | — | JWT verification |
HOUSE_MQTT_ENABLED | off | Enable the MQTT house control plane |
HOUSE_REGISTRY_PATH | /mnt/homeassistant/www/signapps/house_registry.yaml | House registry YAML |
CHECKIN_INTERVAL_MS | 300000 | Cloud check-in interval (5 min) |
AGENT_VERSION | 1.0.0 | Reported agent version |
HA_CONFIG_DIR | /mnt/homeassistant | Home Assistant config mount |
DATA_DIR | /data | Persistent data dir |
Local API
Routes (src/server.ts) are JWT-protected with a role hierarchy
customer (1) < support (2) < installer (3) < system (4):
/health public
/api/auth public (login)
/api/site site info (customer+)
/api/rooms rooms / zones
/api/devices device list + state
/api/registry house device registry
/api/scenes scenes
/api/dashboard dashboard config (served to the SPA)
/api/installer provisioning / Zigbee2MQTT (installer+)
/api/system system status (support+)
/api/automations automations catalog (static file)
OTA updates & check-in
The check-in service (src/cloud/checkin.ts) runs every 5 minutes:
The release manifest at /data/release-manifest.json records the installed module
lock, customer version, and dashboard version, so the agent only pulls when the
cloud reports a newer release.
House registry (optional)
When HOUSE_MQTT_ENABLED=1, the agent reads a house_registry.yaml describing
logical devices and maps them to Home Assistant entity IDs, reconciling live
state with the planned config (one DB row per logical ID). This is the MQTT
control-plane layer for whole-house automation.