Appearance
Remote control
"Remote control" here means controlling a Host from somewhere that is not the Host's own machine running its own CLI. There are three real shapes today:
- Same-machine, different process — another package's actor or a tool dialing the Host's NATS over
127.0.0.1. - Same-bus federation — a sibling Host federated by NATS leafnode so that one machine's bus is reachable from another.
- Device-Link heartbeat to HiveCast cloud — the Host pushes its inventory to
https://hivecast.ai/_auth/host-link/heartbeatover HTTPS.
There is no fourth shape today — there is no public, authenticated HTTP API on the Host for controlling it from arbitrary external clients. Per the host-service boundary rule, that surface belongs to a gateway package, not the Host. Don't add HTTP control to host-service.
Same-machine, different process
Any process on the same machine that can reach the Host's NATS URL can call its actors directly. Both host.supervisor and host.control accept the standard request-reply envelope.
Programmatic example using the helpers:
ts
import { invokeHostControlFromStatus, invokeHostSupervisorFromStatus } from '@open-matrix/host-control';
import { readFileSync } from 'node:fs';
const status = JSON.parse(readFileSync('/var/lib/hivecast/host.status.json', 'utf8'));
// host.control — friendlier, no pid suffix.
const list = await invokeHostControlFromStatus(status, 'runtime.list', {}, 5_000);
// host.supervisor — bypasses host.control, talks straight to the Host process.
const status2 = await invokeHostSupervisorFromStatus(status, 'status', {}, 5_000);The Host process must be running. The caller needs read access to host.status.json and to the NATS URL therein.
If the caller is in a Docker container, mount the Host's home read-only (-v /var/lib/hivecast:/host:ro) and read host.status.json from there.
Same-bus federation (NATS leafnode)
The Host's local NATS can be made reachable from another machine by running a leafnode connection. This is how the production HiveCast deployment fans cloud-side traffic out to a per-user user-owned Host without exposing user actors directly.
The leafnode link is outbound from the user's Host to HiveCast, not inbound — see repo MEMORY.md § "Deployment & Federation":
Leaf connection is outbound WebSocket — no open ports needed. User's daemon initiates WSS to hivecast.ai.
Once the leafnode is up, calls to the user's host.control from the cloud-side bus look identical to calls from 127.0.0.1. The Host's supervisor mailbox, however, has a PID-<pid> suffix that the cloud side cannot know in advance, so the cloud should call host.control not host.supervisor.
Configuring a leafnode is a NATS-server configuration concern, not a Host-service concern. The Host's host.json only knows about its own local NATS; leafnoding is added by editing nats-server.conf directly.
Device-Link heartbeat (HTTPS)
HostControlActor runs a heartbeat loop on a 15-second timer (HOST-CONTROL-ACTOR.ts:236-244). When the Host has a Device Link record (<home>/credentials/hivecast-link.json), each heartbeat:
- Reads the link record (
hostId,principalId,authorityRoot,cloudUrl,hostLinkTokenRef, etc.). - Lists the Host's runtime records, filtered to those whose
runtimeWireRootmatches the link'sauthorityRoot. - Calls
system.devices.devices.registerandsystem.devices.devices.runtimes.registerover the local bus (HostControlActor.ts:278-314). - If
cloudUrlis set, also POSTs to<cloudUrl>/_auth/host-link/heartbeatwith aBearer <hostLinkToken>header (HostControlActor.ts:463-498).
The cloud endpoint validates the token, projects the Host's runtime list onto its public Space registry, and from then on the cloud surface can route into this Host. This is the only outbound HTTP the Host performs.
Heartbeat-related configuration:
| Env var | Default | Effect |
|---|---|---|
MATRIX_DEVICE_HEARTBEAT_INTERVAL_MS | 15000 | how often heartbeats fire |
MATRIX_DEVICE_HEARTBEAT_TTL_MS | 45000 | TTL the cloud should treat the heartbeat as valid for |
MATRIX_DEVICE_HEARTBEAT_TIMEOUT_MS | 5000 | per-call timeout for both local and cloud heartbeat |
(HostControlActor.ts:76-84.)
What does NOT exist
- A REST or gRPC control surface on the Host. Use NATS or the gateway package.
- A public HTTPS endpoint exposed by host-service. The gateway runtime exposes HTTPS, but it routes into actors, not into supervisor control.
- Cross-machine
matrix invoke. The CLI requires a localhost.status.json; for remote Hosts use the programmatic invoke against the federated bus subject. - Authentication on
host.supervisor. The mailbox is implicitly protected by NATS account permissions (whoever can connect with matching credentials can call it). On a single-user laptop that's fine; on a federated cloud Host that's why the cloud useshost.controlwith bus-level auth in front.
See also
- Host supervisor actor
- Local actor invocation
- Host vs HiveCast cloud
- repo
MEMORY.md§ "Deployment & Federation"