Skip to main content

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.tsdist/index.js
  • Runtime: Express 5 + SQLite (better-sqlite3 + Prisma), default port 18080

Tech stack

ConcernChoice
HTTP / realtimeExpress 5 + ws (WebSocket)
DBSQLite (better-sqlite3) via Prisma
MessagingMQTT 5 (mqtt)
AuthJWT, role hierarchy
Releasesadm-zip (extract), SHA-256 validation
House registryjs-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

VariableDefaultPurpose
PORT18080Local API/WebSocket port
DATABASE_URLfile:/data/edge-agent.dbSQLite path (Prisma)
JWT_SECRETJWT verification
HOUSE_MQTT_ENABLEDoffEnable the MQTT house control plane
HOUSE_REGISTRY_PATH/mnt/homeassistant/www/signapps/house_registry.yamlHouse registry YAML
CHECKIN_INTERVAL_MS300000Cloud check-in interval (5 min)
AGENT_VERSION1.0.0Reported agent version
HA_CONFIG_DIR/mnt/homeassistantHome Assistant config mount
DATA_DIR/dataPersistent 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.