eve systemd units (user) #
Long-running processes are supervised by systemd user units, not nohup.
| Unit | Role |
|---|---|
eve-prep.service |
oneshot: OpenBao keys, freeq session, annas-mcp, eve build → ~/.config/eve/runtime.env |
eve.service |
agent HTTP :8000 (+ /.well-known/atproto-did for handle eve.boxd.sh) |
eve-irc-bridge.service |
freeq IRC ↔ eve (restart always) |
eve-freeq-session-refresh.timer |
every 5m: rook login → sync freeq SASL session → restart IRC bridge |
eve-freeq-session-refresh.service |
oneshot body of the timer |
eve-av-bridge.service |
optional MoQ / radio media plane |
rookery.service |
single-user PDS (wrangler dev on :8787, public pds.eve.boxd.sh) |
eve.target |
groups prep + agent + IRC bridge |
Identity: handle eve.boxd.sh, PDS https://pds.eve.boxd.sh, DID from ~/.config/rook/identity.json.
Install (boxd or local) #
# from the agent checkout
bash scripts/install-systemd.sh
# optional radio/AV:
bash scripts/install-systemd.sh --with-av
# optional PDS (installs when ~/rookery exists, or force with --with-rookery):
bash scripts/install-systemd.sh --with-rookery
# OpenBao creds for prep (boxd)
cp ~/.config/eve/openbao.env.example ~/.config/eve/openbao.env
chmod 600 ~/.config/eve/openbao.env
# edit OPENBAO_TOKEN=…
# survive logout / reboot (once per machine)
loginctl enable-linger "$USER"
systemctl --user start eve.target
npm run boxd:start / scripts/start.sh will start/restart the units when they are installed; use --legacy for the old foreground path, or --install to install then start.
Ops #
systemctl --user status eve.target
systemctl --user restart eve-irc-bridge.service # after freeq session sync
systemctl --user restart eve.service
systemctl --user stop eve.target # stop agent + bridge
journalctl --user -u eve.service -u eve-irc-bridge.service -f
After SASL / Guest nick fix #
npx --yes @solpbc/rook login
node scripts/sync-freeq-session.mjs
# prefer soft reload (no process restart — preserves AV / watch):
curl -sS -X POST http://127.0.0.1:8791/session/reload \
-H 'content-type: application/json' \
-d '{"reason":"operator-sasl-fix"}'
# only if the bridge process itself is wedged:
# systemctl --user restart eve-irc-bridge.service
freeq SASL session refresh (systemd timer) #
Access tokens are short-lived. The timer keeps freeq session files fresh without restarting the IRC bridge on every rotation:
systemctl --user status eve-freeq-session-refresh.timer
systemctl --user start eve-freeq-session-refresh.service # run once now
systemctl --user list-timers eve-freeq-session-refresh.timer
tail -f ~/logs/freeq-session-refresh.log
scripts/refresh-freeq-session.sh runs rook login --json, writes freeq
session files via sync-freeq-session.mjs, then POSTs
http://127.0.0.1:8791/session/reload. The bridge reloads the session from
disk and only soft-reconnects if IRC is unhealthy (SASL fail / not
joined). A healthy #channel + AV binding is left alone — SASL only runs at
connect, and connect() always re-reads the session file.
If refresh fails with an expired rook session, re-auth once:
npx --yes @solpbc/rook login
systemctl --user start eve-freeq-session-refresh.service
Config files (~/.config/eve/) #
| File | Purpose |
|---|---|
openbao.env |
OPENBAO_ADDR, OPENBAO_TOKEN (prep only; mode 0600) |
config.env |
optional non-secret overrides (IRC_*, OPENCODE_MODEL, …) |
runtime.env |
written by prep — API keys + defaults for services |
Templates live in systemd/user/; install substitutes @ROOT@, @NODE@, @NPX@.
Layout #
systemd/user/*.service # templates (not live units)
scripts/install-systemd.sh
scripts/prep.sh
scripts/run-av-bridge-service.sh
scripts/start.sh # systemctl if installed, else legacy
stream.place MoQ plane (additional) #
Second eve-av-bridge on 127.0.0.1:8792 rebroadcasts a stream.place live
stream into freeq AV (default #test). Auto-starts when STREAMPLACE_AUTO=1
in ~/.config/eve/config.env.
One MoQ plane per freeq session: the IRC bridge only keeps a single media
plane attached at a time. Starting radio or stream.place disconnects the other
plane and av-leaves prior eve~instance roster rows so freeq never sees two
eve publishers in the same call.
| Unit / endpoint | Role |
|---|---|
eve-av-bridge-streamplace.service |
MoQ media plane :8792 |
POST /streamplace/play (irc-bridge :8791) |
pick top-viewers stream (or body.streamer) + join AV |
POST /streamplace/stop |
stop rebroadcast + disconnect plane |
GET /streamplace/status |
plane health + top live |
systemctl --user status eve-av-bridge-streamplace.service
curl -sS http://127.0.0.1:8791/streamplace/status | jq .
curl -sS -X POST http://127.0.0.1:8791/streamplace/play -H 'content-type: application/json' -d '{}'
curl -sS -X POST http://127.0.0.1:8791/streamplace/stop
Picks place.stream.live.getLiveUsers sorted by viewerCount, plays via
place.stream.playback.whep?streamer=<did> (WHEP → demux → MoQ), or a
YouTube URL (yt-watch-demux.py via yt-dlp + ffmpeg).
stream.place publish plane (inverse / call rebroadcast) #
Egress only: freeq radio / a media URL → RTMP into stream.place (not a freeq MoQ attach). Managed by irc-bridge via ffmpeg; can run while a freeq plane is live.
| Endpoint | Role |
|---|---|
POST /streamplace/publish |
default call mix (freeq room → RTMP); or `mode:audio |
POST /streamplace/publish/stop |
stop ffmpeg publish |
GET /streamplace/publish/status |
publishing?, pid, source |
Config in ~/.config/eve/config.env (mode 0600):
STREAMPLACE_STREAM_KEY=… # Live Dashboard → Generate Stream Key
STREAMPLACE_RTMP_URL=rtmps://stream.place:1935/live # default
STREAMPLACE_PUBLISH_HANDLE=eve.boxd.sh # optional notice URL
# after freeq radio is playing:
curl -sS -X POST http://127.0.0.1:8791/streamplace/publish \
-H 'content-type: application/json' -d '{}'
curl -sS http://127.0.0.1:8791/streamplace/publish/status | jq .
Channel: eve: go live / eve: stop live. Tool: publish_stream.