feat(deploy): systemd user unit + install script for membershipd
Add deploy/unibus-membershipd.service (Restart=always, binds both planes to 0.0.0.0 for LAN reachability), an idempotent deploy/install.sh that builds the binary, symlinks the unit, and enables+starts it, plus deploy/README.md with operate/health instructions. Restart=always is deliberate: a clean SIGTERM exits 0 and Restart=on-failure would not restart it, leaving the service silently dead (the sqlite_api gotcha). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,67 @@
|
|||||||
|
# Running membershipd as a systemd user service
|
||||||
|
|
||||||
|
`membershipd` is the unibus control plane (rooms, members, sealed keys, blob
|
||||||
|
store) and, unless you point it at an external NATS with `--nats-url`, it also
|
||||||
|
runs the embedded NATS + JetStream data plane. Running it as a **systemd user
|
||||||
|
service** keeps it alive across logout/reboot and restarts it if it crashes.
|
||||||
|
|
||||||
|
The unit (`unibus-membershipd.service`) binds both planes to `0.0.0.0`:
|
||||||
|
|
||||||
|
| Plane | Port | Reachable from |
|
||||||
|
|--------------|-------|----------------|
|
||||||
|
| HTTP control | 8470 | LAN (`http://<host-ip>:8470/healthz`) |
|
||||||
|
| NATS data | 4250 | LAN (`nats://<host-ip>:4250`) |
|
||||||
|
|
||||||
|
## Install (idempotent)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/fn_registry/projects/message_bus/apps/unibus
|
||||||
|
./deploy/install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This builds the binary, symlinks the unit into `~/.config/systemd/user/`,
|
||||||
|
reloads systemd, and enables + starts the service.
|
||||||
|
|
||||||
|
## Manual steps (what install.sh does)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/fn_registry/projects/message_bus/apps/unibus
|
||||||
|
|
||||||
|
# 1. Build the pure-Go binary (no CGO).
|
||||||
|
CGO_ENABLED=0 go build -o membershipd ./cmd/membershipd
|
||||||
|
|
||||||
|
# 2. Link the unit into the systemd user directory.
|
||||||
|
mkdir -p ~/.config/systemd/user
|
||||||
|
ln -sf "$PWD/deploy/unibus-membershipd.service" ~/.config/systemd/user/unibus-membershipd.service
|
||||||
|
|
||||||
|
# 3. Reload, enable (start on login) and start now.
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now unibus-membershipd.service
|
||||||
|
|
||||||
|
# (optional) survive logout without an active session:
|
||||||
|
# sudo loginctl enable-linger "$USER"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Operate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user status unibus-membershipd.service # is it active?
|
||||||
|
systemctl --user restart unibus-membershipd.service # after a rebuild
|
||||||
|
systemctl --user stop unibus-membershipd.service
|
||||||
|
systemctl --user disable unibus-membershipd.service # stop starting on login
|
||||||
|
journalctl --user -u unibus-membershipd.service -f # follow logs
|
||||||
|
|
||||||
|
# Health (local and from another LAN host):
|
||||||
|
curl -fsS http://127.0.0.1:8470/healthz
|
||||||
|
curl -fsS http://<host-lan-ip>:8470/healthz
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Writable state (SQLite DB, JetStream store, blobs) lives under `local_files/`
|
||||||
|
relative to `WorkingDirectory`, which the unit sets to the app directory.
|
||||||
|
- After editing the app code, rebuild (`CGO_ENABLED=0 go build -o membershipd
|
||||||
|
./cmd/membershipd`) and `systemctl --user restart unibus-membershipd.service`.
|
||||||
|
- To run against an external NATS instead of the embedded one, append
|
||||||
|
`--nats-url nats://<host>:4222` to `ExecStart` and re-run `daemon-reload` +
|
||||||
|
`restart`.
|
||||||
Executable
+31
@@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build membershipd and install/enable/start it as a systemd user service.
|
||||||
|
# Idempotent: safe to re-run after a code change to rebuild and restart.
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
UNIT="unibus-membershipd.service"
|
||||||
|
USER_UNIT_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
|
||||||
|
|
||||||
|
cd "$APP_DIR"
|
||||||
|
|
||||||
|
echo "==> building membershipd (CGO_ENABLED=0)"
|
||||||
|
CGO_ENABLED=0 go build -o membershipd ./cmd/membershipd
|
||||||
|
|
||||||
|
echo "==> linking unit into $USER_UNIT_DIR"
|
||||||
|
mkdir -p "$USER_UNIT_DIR"
|
||||||
|
ln -sf "$APP_DIR/deploy/$UNIT" "$USER_UNIT_DIR/$UNIT"
|
||||||
|
|
||||||
|
echo "==> reloading systemd and (re)starting the service"
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now "$UNIT"
|
||||||
|
# If the service was already running, enable --now does not restart it; do so to
|
||||||
|
# pick up the freshly built binary.
|
||||||
|
systemctl --user restart "$UNIT"
|
||||||
|
|
||||||
|
echo "==> status"
|
||||||
|
systemctl --user --no-pager status "$UNIT" || true
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Health check:"
|
||||||
|
echo " curl -fsS http://127.0.0.1:8470/healthz"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=unibus membershipd — control plane (rooms, keys, blobs) + embedded NATS/JetStream
|
||||||
|
Documentation=https://gitea-dgg044oo04woo4ggcsws4gk0.organic-machine.com/dataforge/unibus
|
||||||
|
After=network-online.target
|
||||||
|
Wants=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
WorkingDirectory=%h/fn_registry/projects/message_bus/apps/unibus
|
||||||
|
# --bind 0.0.0.0 exposes BOTH the HTTP control plane (:8470) and the embedded
|
||||||
|
# NATS data plane (:4250) to the LAN so phones / other PCs can connect.
|
||||||
|
ExecStart=%h/fn_registry/projects/message_bus/apps/unibus/membershipd --bind 0.0.0.0
|
||||||
|
# Restart=always (NOT on-failure): a clean SIGTERM shutdown exits 0, and
|
||||||
|
# on-failure would then NOT restart, leaving the service silently dead. always
|
||||||
|
# brings it back regardless of exit code. See .claude/rules/function_tags.md.
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
||||||
Reference in New Issue
Block a user