Skip to content

Service install

The hivecast .deb installs the same artifacts as a manual hivecast install, but additionally registers two systemd units and a journald hardening profile. This page walks through each piece.

Status: Linux-only today. The macOS .pkg and Windows .msi build scripts exist (projects/matrix-3/packages/hivecast/scripts/build-{macos-pkg,windows-msi}-installer.js) but are not yet a regular release artifact.

What gets installed where

Layout produced by dpkg-deb --build:

PathWhat
/usr/bin/hivecastbash wrapper that runs /opt/hivecast/node/bin/node /opt/hivecast/bin/hivecast.mjs "$@"
/usr/bin/matrixbash wrapper that runs /opt/hivecast/node/bin/node /opt/hivecast/bin/matrix.mjs "$@"
/opt/hivecast/bin/hivecast.mjsthe wrapper script
/opt/hivecast/bin/matrix.mjsthe matrix CLI shim
/opt/hivecast/dist/host-service/cli.jsthe host-service CLI
/opt/hivecast/dist/bin/nats-serverthe bundled NATS binary (mode 0755)
/opt/hivecast/dist/node_modules/...every bundled package, copied from the workspace build
/opt/hivecast/node/bin/nodea vendored Node runtime (Node 20+)
/opt/hivecast/node/lib/node_modules/npm/a vendored npm
/opt/hivecast/libexec/hivecast-host-service-runthe ExecStart shim for hivecast-host.service
/lib/systemd/system/hivecast-nats.serviceNATS unit
/lib/systemd/system/hivecast-host.servicehost-service unit
/var/lib/hivecast/created by postinst, owned by hivecast user (or ${SUDO_USER} if available)

Both wrappers prepend /opt/hivecast/node/bin to PATH and set MATRIX_HOME=/var/lib/hivecast plus MATRIX_HOST_HOME=/var/lib/hivecast before exec.

The two systemd units

hivecast-nats.service

From scripts/build-deb-installer.js:107-132:

ini
[Unit]
Description=HiveCast local NATS service
After=network.target
StartLimitBurst=5
StartLimitIntervalSec=60

[Service]
Type=simple
User=hivecast
Group=hivecast
ExecStart=/opt/hivecast/dist/bin/nats-server -c /var/lib/hivecast/nats/service/nats-server.conf
Restart=on-failure
RestartSec=5s
WorkingDirectory=/var/lib/hivecast
NoNewPrivileges=true
LogRateLimitIntervalSec=10s
LogRateLimitBurst=200
MemoryHigh=512M
MemoryMax=1G
TasksMax=1024

[Install]
WantedBy=multi-user.target

The conf file at /var/lib/hivecast/nats/service/nats-server.conf (written by postinst) sets port 4222, websocket port 4223, JetStream store_dir /var/lib/hivecast/nats/service/jetstream. Bind host is 127.0.0.1 by default, 0.0.0.0 if the postinst detects a container.

StartLimitBurst=5 + StartLimitIntervalSec=60 means after 5 failed start attempts in 60 seconds, systemd stops trying. This is the floor that RestartPreventExitStatus would target — see CLI → exit codes and diagnostics.

hivecast-host.service

From scripts/build-deb-installer.js:134-161:

ini
[Unit]
Description=HiveCast local Host service
After=hivecast-nats.service
Requires=hivecast-nats.service
StartLimitBurst=5
StartLimitIntervalSec=60

[Service]
Type=simple
User=hivecast
Group=hivecast
ExecStart=/opt/hivecast/libexec/hivecast-host-service-run
Restart=on-failure
RestartSec=5s
WorkingDirectory=/var/lib/hivecast
Environment=MATRIX_HOME=/var/lib/hivecast
Environment=MATRIX_HOST_HOME=/var/lib/hivecast
LogRateLimitIntervalSec=10s
LogRateLimitBurst=200
MemoryHigh=1G
MemoryMax=2G
TasksMax=2048

[Install]
WantedBy=multi-user.target

After=hivecast-nats.service + Requires=hivecast-nats.service enforce ordering: NATS must be up before host-service. Restarting hivecast-host.service does NOT restart NATS — that's the entire point of splitting them. The dev wrapper mirrors this with detached sibling NATS.

hivecast-host-service-run shim

/opt/hivecast/libexec/hivecast-host-service-run (from scripts/build-deb-installer.js:216-270) does three things:

  1. Spawn host-service start --home /var/lib/hivecast and capture pid $host_pid.
  2. Wait up to 30s (120 * 0.25s) for /var/lib/hivecast/host.status.json to land.
  3. In the background, run hivecast bootstrap-default-runtimes (up to 6 attempts with 5s spacing). Wait on $host_pid so a Host crash terminates the whole unit.

Bootstrap output goes to /var/lib/hivecast/logs/host-bootstrap.log. The shim logs every attempt with a == header.

This shim is why the systemd unit doesn't directly ExecStart=node host-service/cli.js. The bootstrap step needs to happen after the supervisor is up, and a single command can't do both. Per CLAUDE.md's "P1.30 stop/start cycles" reference, the shim was born out of the need for clean lifecycles.

What the postinst script does

From scripts/build-deb-installer.js:272-401:

  1. Creates hivecast system user/group if absent.
  2. Falls back to ${SUDO_USER} for ownership if a real user invoked the install — keeps runtime files owned by the workstation user.
  3. Detects container/cgroup environment via /.dockerenv or /proc/1/cgroup. Sets HIVECAST_BIND_HOST=0.0.0.0 for containers, 127.0.0.1 otherwise.
  4. Creates /var/lib/hivecast/ and nats/service/jetstream/ with mode 0750.
  5. Writes /var/lib/hivecast/host.json and /var/lib/hivecast/nats/service/nats-server.conf with the bind host substituted.
  6. Runs hivecast seed --home /var/lib/hivecast to populate the package store.
  7. If the working user is not hivecast, drops drop-in unit files at /etc/systemd/system/hivecast-{nats,host}.service.d/10-workstation-user.conf overriding User= and Group=.
  8. Caps journald: writes /etc/systemd/journald.conf.d/hivecast.conf with SystemMaxUse=2G, SystemKeepFree=4G, SystemMaxFileSize=128M, MaxFileSec=1week, RuntimeMaxUse=200M, ForwardToSyslog=no, RateLimitIntervalSec=30s, RateLimitBurst=10000. Restarts systemd-journald to pick it up.
  9. systemctl daemon-reload, then systemctl enable && restart hivecast-{nats,host}.service.

The journald cap is launch-readiness work — without it, a runaway log emitter could fill the disk before rotation. 2 GB is the cap.

Workstation-user takeover

The drop-in pattern is the core operator-friendliness feature. If richard runs sudo dpkg -i hivecast.deb, the postinst:

  • creates the hivecast system user (idle)
  • chowns /var/lib/hivecast to richard:richard
  • writes /etc/systemd/system/hivecast-{nats,host}.service.d/10-workstation-user.conf with User=richard, Group=richard

So services run as richard. The hivecast system user exists but doesn't own anything live. This avoids the rabbit hole of "should the daemon run as root, or as a service user, or as me?" — the answer is: as you, by default.

The drop-in file is removed (and the hivecast user reverts to ownership) only on full purge — see Uninstall.

What prerm and postrm do

prerm (scripts/build-deb-installer.js:168-179):

  • systemctl stop hivecast-host.service, hivecast-nats.service
  • systemctl disable hivecast-host.service, hivecast-nats.service

postrm (scripts/build-deb-installer.js:180-195) — only on purge:

  • removes the workstation-user drop-in files
  • removes /etc/systemd/journald.conf.d/hivecast.conf
  • systemctl daemon-reload

/var/lib/hivecast/ is not removed on purge — it carries credentials and state. Manual cleanup is required. See Uninstall.

See also

Source: projects/matrix-3/packages/hivecast/scripts/build-deb-installer.js lines 104-162 (units), 164-195 (maintainer scripts), 216-270 (host-service shim), 272-401 (postinst).