Appearance
Server Internals
The HOS server (apps/server/) is a Node.js application built on Express, with an Electron wrapper for desktop deployment. It serves as the central hub — receiving commands from mobile clients via REST/SSE, dispatching actions to OS-local driver processes or external WebSocket drivers, and broadcasting state changes back to all connected clients.
ActionDispatcher
File: apps/server/src/bridge/action-dispatcher.tsInstantiation: Singleton via initBridge() in apps/server/src/bridge/index.ts
The ActionDispatcher is the command gateway between the REST layer and the driver layer.
Responsibilities:
- Receives
deviceId + action + datatriples from controllers - Looks up
ActionMapentries (outbound direction, driver-specific command mappings) - Resolves to the mapped command or passes the action directly if no map entry exists
- Calls
DeviceManager.executeTypedCommand()or a driver-specific dispatch path - HOS Controls Sync: Supports
Device.store()logic which fetches OS commands and populates HOS controls automatically. - Publishes events to ProtocolHub at each stage of dispatch
ProtocolHub events emitted:
| Event | When |
|---|---|
ACTION_REQUESTED | Immediately on dispatch entry |
ACTION_DISPATCHED | After successful command delivery |
ACTION_FAILED | On device not found, driver error, or map lookup failure |
DriverWSServer
File: apps/server/src/core/driver-ws-server.ts
The raw WebSocket server for external SDK-style drivers. Uses the ws library — not Socket.IO.
OS-local uploaded drivers do not need to open this socket. They are spawned by DriverManager from manifest metadata and exchange newline-delimited JSON over stdin/stdout.
Connection details:
| Property | Value |
|---|---|
| WebSocket path | /driver |
| Protocol version | 1 |
| Default port | 8080 (same as HTTP server) |
| Env var | DRIVER_WS_PORT |
Session record shape:
{
id: string,
driverKey: string,
instanceId: string,
name?: string,
protocolVersion?: number,
socket: WebSocket
}Validation rules:
- Driver key:
/^[A-Z0-9_]{2,64}$/ - Instance ID:
/^[a-zA-Z0-9:_-]{1,128}$/
Driver registration message:
json
{
"method": "driver.register",
"driverKey": "MY_DRIVER",
"instanceId": "my-driver:1",
"protocolVersion": 1
}Accepted inbound event types:
| Event Type | Purpose |
|---|---|
DEVICE_DISCOVERED | Driver announces a new device |
DEVICE_UPDATED | Driver updates device metadata |
DEVICE_REMOVED | Driver removes a device |
STATE_UPDATE | Driver pushes device state snapshot |
ACTION_RESULT | Driver responds to a dispatched command |
METRIC_UPDATE | Driver reports telemetry metrics |
Local Driver Processes
Files: apps/server/src/drivers/DriverManager.ts, apps/server/src/drivers/PythonDriverProcess.ts, apps/server/src/drivers/JavaScriptDriverProcess.ts, apps/server/src/drivers/LuaDriverProcess.ts
DriverManager starts OS-local drivers loaded from Integration Manager packages. The loader maps implementation extensions to runtime types:
| File extension | Runtime |
|---|---|
.py | python |
.js | javascript |
.lua | lua |
Local process drivers share the same high-level event contract as WebSocket drivers, but use line-delimited JSON:
- HOS writes command messages to driver stdin.
- Driver writes one JSON event per stdout line.
- Non-JSON stdout is forwarded as runtime output.
- stderr is forwarded as runtime diagnostics.
JavaScriptDriverProcess launches .js drivers with HOS_NODE_BINARY, process.execPath, or node fallback. In Electron builds, it sets ELECTRON_RUN_AS_NODE=1 when needed so the bundled executable can behave as Node. Stop behavior mirrors Python: SIGTERM first, then SIGKILL after a short timeout.
Uploaded JavaScript drivers are plain Node-compatible .js files. TypeScript can be used as an authoring format, but must be compiled before upload.
HOS Fleet Manager (Phase 06)
Purpose: Enterprise-grade management of HOS device fleets. Provides a centralized UI and API for monitoring, commanding, and categorizing multiple HOS hubs.
Key Components:
- Fleet UI: Web-based interface within the OS dashboard for fleet overview and device-level drill-down.
- Fleet Routes: (
apps/server/src/routes/fleet.ts)POST /device/hos/command: Unified endpoint for dispatching commands to fleet devices.POST /device/hos/category: Management of device categories for organizational filtering.
- Fleet Bridge Auto-Expose: (
apps/server/src/bridge/fleet-bridge-auto-expose.ts) Automatically exposes local devices to the fleet bridge for remote visibility and control.
Driver Ecosystem
Generic IR Driver & Fleet Sync
Driver Key: GENERIC_IRAdapter File: apps/server/src/drivers/generic-ir/generic-ir-adapter.ts
The Generic IR driver allows Haptique OS to control physical IR devices (TVs, ACs, Fans) via a network-attached Haptique IR Extender (hardware bridge).
Provisioning Flow
- Fleet Registration: IR devices are first registered or captured via the Fleet mobile app/API.
- Auto-Sync: The
DeviceManager.syncIrDevices()method (triggered on startup or via API hooks) scans the Fleet database for paired IR devices. - Logical Device Creation: For each Fleet IR device, a native OS
LogicalDeviceis created. - Payload Embedding: Raw IR pulse timings and carrier frequency data are extracted from the Fleet
controlsarray and embedded directly into the Logical Device'sproperties.irCommandsdictionary.
Command Dispatch
When a command (e.g., volume_up) is sent to a GENERIC_IR device:
- The
GenericIrAdapterretrieves the raw pulse array from the device's metadata. - It resolves the target hardware Extender's IP and Access Token.
- It dispatches a secured HTTP POST to
http://<extender-ip>/api/ir/send. - Payload Structure:json
{ "freq_khz": 38, "duty": 33, "repeat": 1, "raw": [microsecond_timings...] }
Android TV Driver
Driver Key: ANDROID_TVAdapter File: apps/server/src/drivers/android-tv/android-tv-adapter.ts
The Android TV driver provides ADB-over-TCP control for Android TV and Google TV devices.
Capabilities:
- Media Control: Play, pause, stop, next, previous.
- Power Management: Wake and sleep via ADB keyevents.
- App Launching: Direct intent-based app launching (YouTube, Netflix, etc.).
- D-Pad Navigation: Remote control simulation for UI navigation.
Technical Detail: Uses adb-shell or direct socket frames to communicate with the target device's ADB port (default 5555). Requires "Network Debugging" to be enabled on the TV.
ProtocolHub
File: apps/server/src/core/protocol-hub.ts
An EventEmitter singleton (protocolHub) that acts as the internal event bus bridging the driver runtime to all UI and debug consumers.
Key method:
protocolHub.publish(HaptiqueProtocolEvent)Broadcasts the event to all subscribers and stores the last 60 events in an in-memory ring buffer.
Event shape:
{
event: string,
driver: string,
device_id: string,
data?: unknown,
timestamp?: number
}Subscribers:
| Subscriber | Channel |
|---|---|
SSE controller (/state/stream) | Streams events to mobile HaptiqueContext |
Socket.IO /socket/haptique-protocol | Designer and debug consumers |
| Electron main window IPC | Desktop sidebar and debug panel |
DeviceStateManager
File: apps/server/src/state/DeviceStateManager.ts
Holds per-device runtime state snapshots in memory. All state reads from the mobile app go through this manager; all state writes from drivers arrive here.
State snapshot shape:
{
power: boolean | string,
volume?: number,
brightness?: number,
input?: string,
playback?: object,
extras?: Record<string, unknown>,
updatedAt: number
}Events emitted:
| Event | Consumers |
|---|---|
state_changed | SSE bridge, Socket.IO /socket/device-state channel |
Drivers push STATE_UPDATE messages to DriverWSServer, which writes to DeviceStateManager. SSE reads from DeviceStateManager to build the initial state snapshot on client connection.
AiServiceManager
File: apps/server/src/core/ai-service-manager.ts
Manages the lifecycle of the local AI inference engine and the proxy connection to cloud AI providers.
Responsibilities:
- Process Lifecycle: Spawns and monitors the
llama-servicechild process (the local inference engine). - Model Resolution: Identifies and verifies the latest GGUF model in the local storage directory.
- Resource Guardrails: Calculates model memory requirements and prevents service start if system RAM is critically low.
- Inference Queueing: Manages a request queue for AI prompts, supporting both synchronous (wait-for-result) and asynchronous (event-streamed) patterns.
- Auto-Stop Logic: Shuts down the local inference process after a period of inactivity to conserve system memory and CPU.
- Provider Proxy: Routes prompts to OpenRouter or other cloud providers if configured as the primary AI provider.
Key Method: aiServiceManager.prompt(text, options) — returns a requestId used to track the inference lifecycle.
ProtocolHub events emitted:
| Event | When |
|---|---|
ai_service_status | On service start, stop, or health change |
ai_inference_chunk | On every token generated (streaming) |
ai_inference_result | On successful completion of a prompt |
ai_inference_error | On generation failure or service timeout |
REST API Routes
The server mounts several route families for different client types. Each family uses its own auth middleware.
| Route Prefix | File | Auth | Purpose |
|---|---|---|---|
/app/os, /api/os, /v1/os | routes/os-api.ts | Bearer token (osApiAuth) | Main OS JSON API used by mobile app |
/app, /api | routes/app.ts | appAuth | Legacy app API |
/remote, /v1/remote | routes/remote.ts | remoteAuth | Remote-controller pairing and commands |
/admin | routes/admin.ts | Admin auth | Admin panel |
/os | routes/os/index.ts | — | Server-rendered EJS web UI |
/dev | (dev only) | — | Development tooling |
For the full endpoint inventory — all paths, HTTP methods, query parameters, and response shapes — see the API Reference page.
Socket.IO Layer
File: apps/server/src/socket/index.tsSocket path: /socket.io (default)
Socket.IO handles real-time connections from mobile clients and the designer. Driver connections use raw WebSocket (DriverWSServer), not Socket.IO.
| Namespace | Purpose | Key Events |
|---|---|---|
/socket/remote-connect | Remote pairing flow | remote:pairing, remote:pairing:status, pairing:code:verify |
/socket/device-ir-test | IR signal testing | ir:data |
/socket/device-state | Per-device live state (remote app) | device:state:update, device:state:snapshot |
/socket/haptique-protocol | OS protocol command/response bus | haptique:event, haptique:command, haptique:response |
/socket/designer | Designer live-push | designer:published, designer:refresh |
For the full Socket.IO event reference, see the API Reference page.
Data Flow
The following diagram shows the full request path from an incoming mobile command through to state broadcast:
Mobile / REST Client
|
v
Express Routes (/app/os, /app, /remote, /os)
|
v
Auth Middleware (osApiAuth / appAuth / remoteAuth)
|
v
Controllers (controllers/app/os.ts, etc.)
|
v
ActionDispatcher (bridge/)
| \
v v
DeviceManager DriverManager / DriverWSServer
| |
v v
Core Services Driver Processes (JS/Python/Lua/native/core)
(device-manager, space-service,
program-engine, zone-store,
media-services-manager, ...)
|
v
DeviceStateManager <---- STATE_UPDATE from drivers
|
v
ProtocolHub (event bus)
|
+---> SSE Controller (/state/stream) --> Mobile HaptiqueContext
|
+---> Socket.IO (/socket/haptique-protocol) --> Designer
|
+---> Socket.IO (/socket/device-state) --> Remote AppKey Source Files
| Module | File |
|---|---|
| ActionDispatcher | apps/server/src/bridge/action-dispatcher.ts |
| AiServiceManager | apps/server/src/core/ai-service-manager.ts |
| Bridge init | apps/server/src/bridge/index.ts |
| DriverWSServer | apps/server/src/core/driver-ws-server.ts |
| DriverManager | apps/server/src/drivers/DriverManager.ts |
| JavaScriptDriverProcess | apps/server/src/drivers/JavaScriptDriverProcess.ts |
| ProtocolHub | apps/server/src/core/protocol-hub.ts |
| DeviceStateManager | apps/server/src/state/DeviceStateManager.ts |
| Socket.IO setup | apps/server/src/socket/index.ts |
| OS API routes | apps/server/src/routes/os-api.ts |
| Server entry | apps/server/src/app.ts |
| Env config | apps/server/src/config/env.ts |
See Also
- API Reference — full endpoint inventory and Socket.IO event reference
- HOS Overview — conceptual introduction to the driver ecosystem
- AI Driver Builder MCP — generated OS-local driver packages and JavaScript runtime rules
- Dev Setup — running the server locally with a driver connected