service_whiteboard.nu — hero_whiteboard server + UI lifecycle module #82

Closed
opened 2026-04-19 18:57:16 +00:00 by mahmoud · 3 comments
Owner

Child of #75.

Objective

Add tools/modules/services/service_whiteboard.nu implementing the standard install | start | stop | status lifecycle for the hero_whiteboard service (server + UI) so it can be driven the same way as service_os, service_books, service_browser.

Scope

  • Repo: ssh://git@forge.ourworld.tf/lhumina_code/hero_whiteboard.git
  • Binaries (per buildenv.sh): hero_whiteboard, hero_whiteboard_server, hero_whiteboard_ui
  • Runtime actions (register with hero_proc): hero_whiteboard_server, hero_whiteboard_ui
  • TOML reference: lhumina_code/hero_zero/services/hero_whiteboard.toml
  • Sockets: $HERO_SOCKET_DIR/hero_whiteboard/rpc.sock, $HERO_SOCKET_DIR/hero_whiteboard/ui.sock (confirm in spec)
  • Env: RUST_LOG=info for both server and UI — no placeholders to resolve.
  • Dependencies: none declared in TOML.
  • Workspace layout: virtual workspace (no root [package]) — plain cargo build --release should build all three binaries. No --workspace flag needed.
  • --root flag supported but optional; user-level is default.

Acceptance criteria

  • use services/mod.nu * makes service_whiteboard available.
  • service_whiteboard install [--root] [--update] clones lhumina_code/hero_whiteboard, builds all three binaries in release mode, places them in ~/hero/bin/ (or /root/hero/bin/ with --root).
  • service_whiteboard start [--reset] [--root] [--update] registers both runtime actions + the service, starts, prints both socket paths + UI URL in the summary. Idempotent without --reset.
  • service_whiteboard status [--root] reports state.
  • service_whiteboard stop [--root] cleanly unregisters.
  • Smoke-tested on Hetzner: install → start --reset → status shows running with 0 restarts → stop.

Template & references

  • Template: service_os.nu (PR #78, merged) — clean two-binary pattern, virtual workspace, no env placeholders. This should be nearly a copy-rename.
  • Reference: service_books.nu (PR #81, merged) for the hybrid-workspace + serve subcommand patterns, in case hero_whiteboard binaries turn out to require a subcommand (the spec should verify via hero_whiteboard_server --help).
  • Skills: claude/skills/nu_service/SKILL.md (build), claude/skills/nu_service_use/SKILL.md (use).
  • Shared helpers: tools/modules/services/lib.nu.

Expected simplifications vs. prior services

This should be the simplest cycle so far: virtual workspace, no data directory, no dependency preflight, no env placeholders. If it lands as a clean copy-rename of service_os.nu (minus the WASM asset preflight), that validates the template is reusable for the remaining Tier 1 services.

Child of #75. ## Objective Add `tools/modules/services/service_whiteboard.nu` implementing the standard `install | start | stop | status` lifecycle for the **hero_whiteboard** service (server + UI) so it can be driven the same way as `service_os`, `service_books`, `service_browser`. ## Scope - **Repo**: `ssh://git@forge.ourworld.tf/lhumina_code/hero_whiteboard.git` - **Binaries** (per `buildenv.sh`): `hero_whiteboard`, `hero_whiteboard_server`, `hero_whiteboard_ui` - **Runtime actions** (register with hero_proc): `hero_whiteboard_server`, `hero_whiteboard_ui` - **TOML reference**: `lhumina_code/hero_zero/services/hero_whiteboard.toml` - **Sockets**: `$HERO_SOCKET_DIR/hero_whiteboard/rpc.sock`, `$HERO_SOCKET_DIR/hero_whiteboard/ui.sock` (confirm in spec) - **Env**: `RUST_LOG=info` for both server and UI — no placeholders to resolve. - **Dependencies**: none declared in TOML. - **Workspace layout**: virtual workspace (no root `[package]`) — plain `cargo build --release` should build all three binaries. No `--workspace` flag needed. - `--root` flag supported but optional; user-level is default. ## Acceptance criteria - [ ] `use services/mod.nu *` makes `service_whiteboard` available. - [ ] `service_whiteboard install [--root] [--update]` clones `lhumina_code/hero_whiteboard`, builds all three binaries in release mode, places them in `~/hero/bin/` (or `/root/hero/bin/` with `--root`). - [ ] `service_whiteboard start [--reset] [--root] [--update]` registers both runtime actions + the service, starts, prints both socket paths + UI URL in the summary. Idempotent without `--reset`. - [ ] `service_whiteboard status [--root]` reports state. - [ ] `service_whiteboard stop [--root]` cleanly unregisters. - [ ] Smoke-tested on Hetzner: install → start --reset → status shows running with 0 restarts → stop. ## Template & references - Template: `service_os.nu` (PR #78, merged) — clean two-binary pattern, virtual workspace, no env placeholders. This should be nearly a copy-rename. - Reference: `service_books.nu` (PR #81, merged) for the hybrid-workspace + `serve` subcommand patterns, in case hero_whiteboard binaries turn out to require a subcommand (the spec should verify via `hero_whiteboard_server --help`). - Skills: `claude/skills/nu_service/SKILL.md` (build), `claude/skills/nu_service_use/SKILL.md` (use). - Shared helpers: `tools/modules/services/lib.nu`. ## Expected simplifications vs. prior services This should be the simplest cycle so far: virtual workspace, no data directory, no dependency preflight, no env placeholders. If it lands as a clean copy-rename of `service_os.nu` (minus the WASM asset preflight), that validates the template is reusable for the remaining Tier 1 services.
mahmoud self-assigned this 2026-04-19 19:00:46 +00:00
mahmoud added this to the ACTIVE project 2026-04-19 19:00:49 +00:00
mahmoud added this to the now milestone 2026-04-19 19:00:53 +00:00
Author
Owner

Implementation Spec for Issue #82

Objective

Add tools/modules/services/service_whiteboard.nu — a install | start | stop | status lifecycle module for the hero_whiteboard service (server + UI) registered with hero_proc, following the minimal two-binary virtual-workspace pattern established by service_os.nu.

Requirements

  • Expose install, start, stop, status as subcommands of service_whiteboard.
  • Register two hero_proc actions: hero_whiteboard_server and hero_whiteboard_ui, bundled into a hero_whiteboard service in context core.
  • Install three binaries into ~/hero/bin (or /root/hero/bin with --root): hero_whiteboard (CLI, not a hero_proc action), hero_whiteboard_server, hero_whiteboard_ui.
  • Both actions talk only over Unix domain sockets under $HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.
  • Both actions expose only RUST_LOG=info — no HERO_* env placeholders, no data dir computation.
  • No dependency preflight: hero_whiteboard.toml declares no depends_on; neither binary has hard-fail prerequisites.
  • Action scripts are plain binary paths (script: $bin) — neither binary has a serve subcommand; main calls serve() directly. Confirmed in hero_whiteboard_server/src/main.rs:21-24 and hero_whiteboard_ui/src/main.rs:85-90.
  • Virtual workspace (Cargo.toml:1-10, no root [package]), so plain svc_cargo_install (single cargo build --release) is sufficient — no --workspace pass needed.
  • --root must transparently target /root/hero/... via sudo, reusing lib.nu helpers.
  • Re-register is always clean: svx_drop_registration tears down any prior service + actions before re-adding.
  • Idempotent start: if already running and neither --reset nor --update given, print a hint and return.
  • stop is safe when nothing is registered and when hero_proc itself is down (prints a warning and returns).
  • status requires hero_proc up and delegates to proc service status.

Files to Modify/Create

  • tools/modules/services/service_whiteboard.nu — new, ~260 lines, cloned from service_os.nu with the WASM assets preflight removed.
  • tools/modules/services/mod.nu — add one line: export use service_whiteboard.nu after the existing export use service_books.nu on line 9.

Implementation Plan

Step 1: Copy service_os.nuservice_whiteboard.nu

Files: tools/modules/services/service_whiteboard.nu

  • Start from service_os.nu as the template (simpler than service_books.nu; no hybrid workspace, no env placeholders, no serve subcommand, no data dir).
  • Keep the same file layout: header comment → use imports → constants → action builders → service config → drop helper → installstartstopstatus.
    Dependencies: none.

Step 2: Rewrite the header comment for whiteboard

Files: tools/modules/services/service_whiteboard.nu

  • Mirror service_os.nu:1-41 but:
    • Change the service description to: "Whiteboard — collaborative drawing and diagramming" (from hero_whiteboard.toml:3).
    • List the two daemons: hero_whiteboard_server and hero_whiteboard_ui.
    • Document the two UDS paths: $HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.
    • Keep the hero_proc dependency paragraph (same wording as service_os.nu:13-15).
    • Drop the entire WASM assets paragraph (service_os.nu:17-22) — whiteboard has no external assets preflight.
    • Keep the import/usage block and the --root paragraph unchanged in spirit (service_os.nu:24-35).
      Dependencies: Step 1.

Step 3: Update constants

Files: tools/modules/services/service_whiteboard.nu

  • At service_os.nu:47-52 equivalent, set:
    • SVX_SERVICE_NAME = "hero_whiteboard"
    • SVX_FORGE_LOC = "lhumina_code/hero_whiteboard"
    • SVX_BINARIES = ["hero_whiteboard" "hero_whiteboard_server" "hero_whiteboard_ui"]
    • SVX_ACTIONS = ["hero_whiteboard_server" "hero_whiteboard_ui"]
  • Keep the comment noting that hero_whiteboard (CLI) is shipped but not registered as an action — same reasoning as in service_os.nu:50-51.
    Dependencies: Step 1.

Step 4: Rename and rewrite svx_server_action

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:58-97 verbatim, substituting hero_os_serverhero_whiteboard_server and hero_oshero_whiteboard in every string (action name, kill_other.socket[0], health_checks[0].action, health_checks[0].openrpc_socket).
  • Keep env: {RUST_LOG: "info"} identical (matches the TOML).
  • Keep script: $bin (no serve subcommand — confirmed above).
  • Keep the retry policy and health_check policy values byte-for-byte identical to service_os.nu:68-95.
    Dependencies: Steps 1, 3.

Step 5: Rename and rewrite svx_ui_action

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:99-138 verbatim, substituting hero_os_uihero_whiteboard_ui and hero_oshero_whiteboard in every string.
  • Keep script: $bin (no serve subcommand — confirmed in hero_whiteboard_ui/src/main.rs:85-90).
  • Keep retry/health policy values identical to service_os.nu:108-136.
    Dependencies: Steps 1, 3.

Step 6: Rewrite svx_service_config

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:140-152. Set description: "Hero Whiteboard — collaborative drawing and diagramming server and UI". Keep context_name: "core", class: "system", critical: false, status: "start" unchanged.
    Dependencies: Steps 1, 3.

Step 7: Keep svx_drop_registration unchanged in shape; drop svx_check_assets

Files: tools/modules/services/service_whiteboard.nu

  • svx_drop_registration is identical in structure to service_os.nu:155-161; only the constants it closes over change (handled in Step 3).
  • Delete the entire svx_check_assets helper from service_os.nu:164-182. Whiteboard has no external asset dir or embedder socket — nothing to preflight.
    Dependencies: Step 1.

Step 8: Trim install to the minimal form

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:191-198 verbatim. Body is exactly:
    • if $root { svc_require_sudo }
    • if $update { svc_update $SVX_FORGE_LOC }
    • svc_cargo_install $SVX_FORGE_LOC $SVX_BINARIES $root
  • Do NOT adopt the service_books.nu hybrid-workspace branch — whiteboard is a pure virtual workspace (verified at hero_whiteboard/Cargo.toml:1-10), so svc_cargo_install's internal cargo build --release already builds all three binaries.
    Dependencies: Steps 1, 3.

Step 9: Trim start — remove the assets preflight call

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:215-293 structure, substituting hero_oshero_whiteboard throughout strings.
  • Delete the svx_check_assets $root call (service_os.nu:252-253) and renumber the subsequent inline comments.
  • Keep every other numbered step identical: sudo gate, svc_require_proc, idempotent already-running check, install --root=$root --update=$update, post-install bin_ok sanity check, drop registration, register both actions + the service, start, settle sleep 1sec, final summary block.
  • Summary block at service_os.nu:273-292: copy verbatim with the usual hero_oshero_whiteboard rename.
    Dependencies: Steps 1-7.

Step 10: stop and status

Files: tools/modules/services/service_whiteboard.nu

  • Clone service_os.nu:306-321 for stop and service_os.nu:330-335 for status, renaming hero_oshero_whiteboard in all strings. No logic changes.
    Dependencies: Steps 1, 3, 7.

Step 11: Register in mod.nu

Files: tools/modules/services/mod.nu

  • Append export use service_whiteboard.nu as a new line after service_books.nu (the current line 9). Alphabetical grouping is not enforced in mod.nu; follow the existing order of merge (ship order).
    Dependencies: Steps 1-10.

Step 12: Smoke-test on Hetzner before PR

  • service_proc start --root (prereq).
  • service_whiteboard install --root → clean cargo build, three binaries in /root/hero/bin/.
  • service_whiteboard start --reset --root → both actions registered, both sockets live, state: running.
  • service_whiteboard status --root → record shows running, both actions healthy.
  • service_whiteboard start --root again → idempotent "already running".
  • curl --unix-socket /root/hero/var/sockets/hero_whiteboard/ui.sock http://localhost/ → expect HTTP response.
  • service_whiteboard stop --root → sockets gone, service unregistered.
  • Post-stop service_whiteboard status --root → RPC error service 'hero_whiteboard' not found.
    Dependencies: Steps 1-11.

Acceptance Criteria

  • tools/modules/services/service_whiteboard.nu exists and exports install, start, stop, status.
  • tools/modules/services/mod.nu re-exports service_whiteboard.nu.
  • install builds all three binaries via one cargo build --release and places them under the correct bin dir.
  • start registers hero_whiteboard_server, hero_whiteboard_ui, and the hero_whiteboard service in context core, then starts it; both UDS sockets appear at $HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.
  • start --reset tears down any prior registration and re-registers cleanly.
  • start is idempotent when nothing changed.
  • stop unregisters cleanly and is safe when nothing is registered or when hero_proc is down.
  • status requires hero_proc up and delegates to proc service status hero_whiteboard.
  • All commands accept --root (-r) and behave the same for root targets.
  • No emoji, no AI-attribution markers in the committed file.
  • Smoke-tested end-to-end on the Hetzner box.

Notes

  • Binary inventory (from hero_whiteboard/buildenv.sh:5):
    • hero_whiteboard — CLI, shipped but NOT registered as a hero_proc action (mirrors the hero_os CLI role).
    • hero_whiteboard_server — registered action; OpenRPC state server.
    • hero_whiteboard_ui — registered action; serves user whiteboard at / and admin dashboard at /admin/ over ui.sock.
    • Other workspace members (app, examples, sdk) have no [[bin]] targets and are correctly absent from SVX_BINARIES.
  • Workspace layout: pure virtual workspace (no root [package]). One cargo build --release at the workspace root builds all three bin targets. No --workspace flag needed; svc_cargo_install is sufficient. Matches hero_os, not hero_books.
  • Socket path confirmation (read from Rust source):
    • hero_whiteboard_server/src/main.rs:67-75$HERO_SOCKET_DIR/hero_whiteboard/rpc.sock (falls back to $HOME/hero/var/sockets/hero_whiteboard/rpc.sock).
    • hero_whiteboard_ui/src/main.rs:35-43$HERO_SOCKET_DIR/hero_whiteboard/ui.sock (same fallback).
    • Both match the svc_sock_base-derived path that service_os.nu's svc_*_action builders already compute — no changes to path derivation needed.
  • Subcommand finding: no serve subcommand. Both main.rs files invoke serve(...) directly from #[tokio::main] async fn main() with no clap parser. script: $bin applies. This is the first non-books service to confirm this — the serve-subcommand path is specific to books' dual-entrypoint CLI and is not the general pattern.
  • Shared-helper consideration: since neither hero_os nor hero_whiteboard takes serve, and only hero_books does, there is no "two-in-a-row" case to promote to a helper. Defer any refactor to lib.nu until a fourth service confirms which pattern is the outlier. Explicitly out of scope for this PR.
  • Root vs user-level: identical behaviour to service_os.nu. --root flips svc_home, svc_bin, and svc_sock_base to /root/hero/... via lib.nu; cargo builds always run in the invoking user's session; binaries are copied with sudo via svc_install_binary. svc_require_sudo enforces passwordless sudo up-front when --root is used.
  • No preflight, by design: hero_whiteboard.toml has no depends_on and both binaries self-heal (server creates its sqlite DB via db::open_db(), both remove stale sockets before bind, and the UI connects to the server lazily per-request). No warning helper is needed in this PR — this is the simplest cycle to date.
## Implementation Spec for Issue #82 ### Objective Add `tools/modules/services/service_whiteboard.nu` — a `install | start | stop | status` lifecycle module for the `hero_whiteboard` service (server + UI) registered with `hero_proc`, following the minimal two-binary virtual-workspace pattern established by `service_os.nu`. ### Requirements - Expose `install`, `start`, `stop`, `status` as subcommands of `service_whiteboard`. - Register two `hero_proc` actions: `hero_whiteboard_server` and `hero_whiteboard_ui`, bundled into a `hero_whiteboard` service in context `core`. - Install three binaries into `~/hero/bin` (or `/root/hero/bin` with `--root`): `hero_whiteboard` (CLI, not a `hero_proc` action), `hero_whiteboard_server`, `hero_whiteboard_ui`. - Both actions talk only over Unix domain sockets under `$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock`. - Both actions expose only `RUST_LOG=info` — no `HERO_*` env placeholders, no data dir computation. - No dependency preflight: `hero_whiteboard.toml` declares no `depends_on`; neither binary has hard-fail prerequisites. - Action scripts are plain binary paths (`script: $bin`) — neither binary has a `serve` subcommand; `main` calls `serve()` directly. Confirmed in `hero_whiteboard_server/src/main.rs:21-24` and `hero_whiteboard_ui/src/main.rs:85-90`. - Virtual workspace (`Cargo.toml:1-10`, no root `[package]`), so plain `svc_cargo_install` (single `cargo build --release`) is sufficient — no `--workspace` pass needed. - `--root` must transparently target `/root/hero/...` via sudo, reusing `lib.nu` helpers. - Re-register is always clean: `svx_drop_registration` tears down any prior service + actions before re-adding. - Idempotent `start`: if already running and neither `--reset` nor `--update` given, print a hint and return. - `stop` is safe when nothing is registered and when `hero_proc` itself is down (prints a warning and returns). - `status` requires `hero_proc` up and delegates to `proc service status`. ### Files to Modify/Create - `tools/modules/services/service_whiteboard.nu` — new, ~260 lines, cloned from `service_os.nu` with the WASM assets preflight removed. - `tools/modules/services/mod.nu` — add one line: `export use service_whiteboard.nu` after the existing `export use service_books.nu` on line 9. ### Implementation Plan #### Step 1: Copy `service_os.nu` → `service_whiteboard.nu` Files: `tools/modules/services/service_whiteboard.nu` - Start from `service_os.nu` as the template (simpler than `service_books.nu`; no hybrid workspace, no env placeholders, no `serve` subcommand, no data dir). - Keep the same file layout: header comment → `use` imports → constants → action builders → service config → drop helper → `install` → `start` → `stop` → `status`. Dependencies: none. #### Step 2: Rewrite the header comment for whiteboard Files: `tools/modules/services/service_whiteboard.nu` - Mirror `service_os.nu:1-41` but: - Change the service description to: "Whiteboard — collaborative drawing and diagramming" (from `hero_whiteboard.toml:3`). - List the two daemons: `hero_whiteboard_server` and `hero_whiteboard_ui`. - Document the two UDS paths: `$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock`. - Keep the `hero_proc` dependency paragraph (same wording as `service_os.nu:13-15`). - **Drop** the entire WASM assets paragraph (`service_os.nu:17-22`) — whiteboard has no external assets preflight. - Keep the import/usage block and the `--root` paragraph unchanged in spirit (`service_os.nu:24-35`). Dependencies: Step 1. #### Step 3: Update constants Files: `tools/modules/services/service_whiteboard.nu` - At `service_os.nu:47-52` equivalent, set: - `SVX_SERVICE_NAME = "hero_whiteboard"` - `SVX_FORGE_LOC = "lhumina_code/hero_whiteboard"` - `SVX_BINARIES = ["hero_whiteboard" "hero_whiteboard_server" "hero_whiteboard_ui"]` - `SVX_ACTIONS = ["hero_whiteboard_server" "hero_whiteboard_ui"]` - Keep the comment noting that `hero_whiteboard` (CLI) is shipped but not registered as an action — same reasoning as in `service_os.nu:50-51`. Dependencies: Step 1. #### Step 4: Rename and rewrite `svx_server_action` Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:58-97` verbatim, substituting `hero_os_server` → `hero_whiteboard_server` and `hero_os` → `hero_whiteboard` in every string (action name, `kill_other.socket[0]`, `health_checks[0].action`, `health_checks[0].openrpc_socket`). - Keep `env: {RUST_LOG: "info"}` identical (matches the TOML). - Keep `script: $bin` (no `serve` subcommand — confirmed above). - Keep the retry policy and health_check policy values byte-for-byte identical to `service_os.nu:68-95`. Dependencies: Steps 1, 3. #### Step 5: Rename and rewrite `svx_ui_action` Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:99-138` verbatim, substituting `hero_os_ui` → `hero_whiteboard_ui` and `hero_os` → `hero_whiteboard` in every string. - Keep `script: $bin` (no `serve` subcommand — confirmed in `hero_whiteboard_ui/src/main.rs:85-90`). - Keep retry/health policy values identical to `service_os.nu:108-136`. Dependencies: Steps 1, 3. #### Step 6: Rewrite `svx_service_config` Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:140-152`. Set `description: "Hero Whiteboard — collaborative drawing and diagramming server and UI"`. Keep `context_name: "core"`, `class: "system"`, `critical: false`, `status: "start"` unchanged. Dependencies: Steps 1, 3. #### Step 7: Keep `svx_drop_registration` unchanged in shape; drop `svx_check_assets` Files: `tools/modules/services/service_whiteboard.nu` - `svx_drop_registration` is identical in structure to `service_os.nu:155-161`; only the constants it closes over change (handled in Step 3). - **Delete** the entire `svx_check_assets` helper from `service_os.nu:164-182`. Whiteboard has no external asset dir or embedder socket — nothing to preflight. Dependencies: Step 1. #### Step 8: Trim `install` to the minimal form Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:191-198` verbatim. Body is exactly: - `if $root { svc_require_sudo }` - `if $update { svc_update $SVX_FORGE_LOC }` - `svc_cargo_install $SVX_FORGE_LOC $SVX_BINARIES $root` - Do NOT adopt the `service_books.nu` hybrid-workspace branch — whiteboard is a pure virtual workspace (verified at `hero_whiteboard/Cargo.toml:1-10`), so `svc_cargo_install`'s internal `cargo build --release` already builds all three binaries. Dependencies: Steps 1, 3. #### Step 9: Trim `start` — remove the assets preflight call Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:215-293` structure, substituting `hero_os` → `hero_whiteboard` throughout strings. - **Delete** the `svx_check_assets $root` call (`service_os.nu:252-253`) and renumber the subsequent inline comments. - Keep every other numbered step identical: sudo gate, `svc_require_proc`, idempotent already-running check, `install --root=$root --update=$update`, post-install `bin_ok` sanity check, drop registration, register both actions + the service, start, settle `sleep 1sec`, final summary block. - Summary block at `service_os.nu:273-292`: copy verbatim with the usual `hero_os` → `hero_whiteboard` rename. Dependencies: Steps 1-7. #### Step 10: `stop` and `status` Files: `tools/modules/services/service_whiteboard.nu` - Clone `service_os.nu:306-321` for `stop` and `service_os.nu:330-335` for `status`, renaming `hero_os` → `hero_whiteboard` in all strings. No logic changes. Dependencies: Steps 1, 3, 7. #### Step 11: Register in `mod.nu` Files: `tools/modules/services/mod.nu` - Append `export use service_whiteboard.nu` as a new line after `service_books.nu` (the current line 9). Alphabetical grouping is not enforced in `mod.nu`; follow the existing order of merge (ship order). Dependencies: Steps 1-10. #### Step 12: Smoke-test on Hetzner before PR - `service_proc start --root` (prereq). - `service_whiteboard install --root` → clean cargo build, three binaries in `/root/hero/bin/`. - `service_whiteboard start --reset --root` → both actions registered, both sockets live, `state: running`. - `service_whiteboard status --root` → record shows running, both actions healthy. - `service_whiteboard start --root` again → idempotent "already running". - `curl --unix-socket /root/hero/var/sockets/hero_whiteboard/ui.sock http://localhost/` → expect HTTP response. - `service_whiteboard stop --root` → sockets gone, service unregistered. - Post-stop `service_whiteboard status --root` → RPC error `service 'hero_whiteboard' not found`. Dependencies: Steps 1-11. ### Acceptance Criteria - [ ] `tools/modules/services/service_whiteboard.nu` exists and exports `install`, `start`, `stop`, `status`. - [ ] `tools/modules/services/mod.nu` re-exports `service_whiteboard.nu`. - [ ] `install` builds all three binaries via one `cargo build --release` and places them under the correct `bin` dir. - [ ] `start` registers `hero_whiteboard_server`, `hero_whiteboard_ui`, and the `hero_whiteboard` service in context `core`, then starts it; both UDS sockets appear at `$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock`. - [ ] `start --reset` tears down any prior registration and re-registers cleanly. - [ ] `start` is idempotent when nothing changed. - [ ] `stop` unregisters cleanly and is safe when nothing is registered or when `hero_proc` is down. - [ ] `status` requires `hero_proc` up and delegates to `proc service status hero_whiteboard`. - [ ] All commands accept `--root (-r)` and behave the same for root targets. - [ ] No emoji, no AI-attribution markers in the committed file. - [ ] Smoke-tested end-to-end on the Hetzner box. ### Notes - **Binary inventory** (from `hero_whiteboard/buildenv.sh:5`): - `hero_whiteboard` — CLI, shipped but NOT registered as a `hero_proc` action (mirrors the `hero_os` CLI role). - `hero_whiteboard_server` — registered action; OpenRPC state server. - `hero_whiteboard_ui` — registered action; serves user whiteboard at `/` and admin dashboard at `/admin/` over `ui.sock`. - Other workspace members (`app`, `examples`, `sdk`) have no `[[bin]]` targets and are correctly absent from `SVX_BINARIES`. - **Workspace layout**: pure virtual workspace (no root `[package]`). One `cargo build --release` at the workspace root builds all three bin targets. No `--workspace` flag needed; `svc_cargo_install` is sufficient. Matches `hero_os`, not `hero_books`. - **Socket path confirmation** (read from Rust source): - `hero_whiteboard_server/src/main.rs:67-75` — `$HERO_SOCKET_DIR/hero_whiteboard/rpc.sock` (falls back to `$HOME/hero/var/sockets/hero_whiteboard/rpc.sock`). - `hero_whiteboard_ui/src/main.rs:35-43` — `$HERO_SOCKET_DIR/hero_whiteboard/ui.sock` (same fallback). - Both match the `svc_sock_base`-derived path that `service_os.nu`'s `svc_*_action` builders already compute — no changes to path derivation needed. - **Subcommand finding**: **no `serve` subcommand.** Both `main.rs` files invoke `serve(...)` directly from `#[tokio::main] async fn main()` with no clap parser. `script: $bin` applies. This is the first non-books service to confirm this — the `serve`-subcommand path is specific to books' dual-entrypoint CLI and is not the general pattern. - **Shared-helper consideration**: since neither `hero_os` nor `hero_whiteboard` takes `serve`, and only `hero_books` does, there is no "two-in-a-row" case to promote to a helper. Defer any refactor to `lib.nu` until a fourth service confirms which pattern is the outlier. Explicitly out of scope for this PR. - **Root vs user-level**: identical behaviour to `service_os.nu`. `--root` flips `svc_home`, `svc_bin`, and `svc_sock_base` to `/root/hero/...` via `lib.nu`; cargo builds always run in the invoking user's session; binaries are copied with sudo via `svc_install_binary`. `svc_require_sudo` enforces passwordless sudo up-front when `--root` is used. - **No preflight, by design**: `hero_whiteboard.toml` has no `depends_on` and both binaries self-heal (server creates its sqlite DB via `db::open_db()`, both remove stale sockets before bind, and the UI connects to the server lazily per-request). No warning helper is needed in this PR — this is the simplest cycle to date.
Author
Owner

Implementation summary

Changes

  • Added tools/modules/services/service_whiteboard.nu — ~270 lines, near-identical copy of service_os.nu with WASM asset preflight removed and names substituted.
  • Updated tools/modules/services/mod.nu — added export use service_whiteboard.nu.

What the module does

  • service_whiteboard install [--root] [--update] — clones lhumina_code/hero_whiteboard, runs cargo build --release, copies all 3 binaries (hero_whiteboard, hero_whiteboard_server, hero_whiteboard_ui) into ~/hero/bin/.
  • service_whiteboard start [--reset] [--root] [--update] — drops any stale registration, registers both runtime actions + the service, starts, prints both Unix sockets and the http+unix://…/ui.sock/ URL.
  • service_whiteboard status [--root] — returns the hero_proc record.
  • service_whiteboard stop [--root] — cleanly unregisters; tolerant of hero_proc being down.

Simplest cycle yet

No workspace quirks, no serve subcommand, no env placeholders, no dependency preflight. This lands as a template for every remaining minimal Tier 1 service.

End-to-end smoke test on Hetzner

# Assertion Result
1a–1c hero_proc-down error paths for status / stop / start PASS
2a service_proc start --root healthy PASS
2b service_whiteboard install --root produced 3 binaries PASS
2c service_whiteboard start --reset --root registers + starts PASS
2d rpc.sock live unix socket PASS
2e ui.sock live unix socket PASS
2f curl --unix-socket rpc.sock accepts HTTP PASS
2g curl --unix-socket ui.sock accepts HTTP PASS
2h service_whiteboard status returns {name: hero_whiteboard, state: running, restarts: 0} PASS
2i Idempotent start (no --reset) prints "already running" PASS
2j 15 s observation — no new restarts, state held running PASS
2k service_whiteboard stop --root stops and unregisters PASS
2l Socket files physically removed after stop minor failure (see notes)
2m Post-stop status returns service 'hero_whiteboard' not found PASS

Notes on the 2l "failure"

After stop, hero_proc unregistered both actions and the service cleanly (confirmed by 2m: the follow-up status call returned the expected service 'hero_whiteboard' not found RPC error). However, the socket files (rpc.sock, ui.sock) were still on disk as zero-byte stale Unix-socket inodes — mtime matched the start time, so they were never re-touched after creation.

This is a hero_whiteboard binary behaviour: neither hero_whiteboard_server nor hero_whiteboard_ui unlink their Unix-socket path on SIGTERM (unlike hero_os_server / hero_books_server which do). The stale socket files are harmless — the kill_other.socket cleanup in the action spec unlinks them on the next start, so re-register / restart paths stay clean.

Not in scope for this PR. Worth filing a follow-up against lhumina_code/hero_whiteboard if we want the binaries to clean up after themselves on shutdown.

Acceptance criteria

  • Module loadable via use services/mod.nu * or use services/service_whiteboard.nu *.
  • install builds 3 binaries and places them in ~/hero/bin/ (or /root/hero/bin/ with --root).
  • start registers both actions + the service with hero_proc, starts, and surfaces both sockets in the summary.
  • status reports the hero_proc record.
  • stop cleanly unregisters (sockets persist as stale inodes — benign, see note above).
  • --root optional, user-level default.
  • Smoke-tested end-to-end on the Hetzner box.
## Implementation summary ### Changes - Added `tools/modules/services/service_whiteboard.nu` — ~270 lines, near-identical copy of `service_os.nu` with WASM asset preflight removed and names substituted. - Updated `tools/modules/services/mod.nu` — added `export use service_whiteboard.nu`. ### What the module does - `service_whiteboard install [--root] [--update]` — clones `lhumina_code/hero_whiteboard`, runs `cargo build --release`, copies all 3 binaries (`hero_whiteboard`, `hero_whiteboard_server`, `hero_whiteboard_ui`) into `~/hero/bin/`. - `service_whiteboard start [--reset] [--root] [--update]` — drops any stale registration, registers both runtime actions + the service, starts, prints both Unix sockets and the `http+unix://…/ui.sock/` URL. - `service_whiteboard status [--root]` — returns the hero_proc record. - `service_whiteboard stop [--root]` — cleanly unregisters; tolerant of hero_proc being down. ### Simplest cycle yet No workspace quirks, no `serve` subcommand, no env placeholders, no dependency preflight. This lands as a template for every remaining minimal Tier 1 service. ### End-to-end smoke test on Hetzner | # | Assertion | Result | |---|---|---| | 1a–1c | hero_proc-down error paths for status / stop / start | PASS | | 2a | `service_proc start --root` healthy | PASS | | 2b | `service_whiteboard install --root` produced 3 binaries | PASS | | 2c | `service_whiteboard start --reset --root` registers + starts | PASS | | 2d | `rpc.sock` live unix socket | PASS | | 2e | `ui.sock` live unix socket | PASS | | 2f | `curl --unix-socket rpc.sock` accepts HTTP | PASS | | 2g | `curl --unix-socket ui.sock` accepts HTTP | PASS | | 2h | `service_whiteboard status` returns `{name: hero_whiteboard, state: running, restarts: 0}` | PASS | | 2i | Idempotent start (no `--reset`) prints "already running" | PASS | | 2j | 15 s observation — no new restarts, state held running | PASS | | 2k | `service_whiteboard stop --root` stops and unregisters | PASS | | 2l | Socket files physically removed after stop | **minor failure** (see notes) | | 2m | Post-stop `status` returns `service 'hero_whiteboard' not found` | PASS | ### Notes on the 2l "failure" After `stop`, hero_proc unregistered both actions and the service cleanly (confirmed by 2m: the follow-up status call returned the expected `service 'hero_whiteboard' not found` RPC error). However, the socket files (`rpc.sock`, `ui.sock`) were still on disk as zero-byte stale Unix-socket inodes — mtime matched the start time, so they were never re-touched after creation. This is a hero_whiteboard binary behaviour: neither `hero_whiteboard_server` nor `hero_whiteboard_ui` unlink their Unix-socket path on SIGTERM (unlike hero_os_server / hero_books_server which do). The stale socket files are harmless — the `kill_other.socket` cleanup in the action spec unlinks them on the next `start`, so re-register / restart paths stay clean. Not in scope for this PR. Worth filing a follow-up against `lhumina_code/hero_whiteboard` if we want the binaries to clean up after themselves on shutdown. ### Acceptance criteria - [x] Module loadable via `use services/mod.nu *` or `use services/service_whiteboard.nu *`. - [x] `install` builds 3 binaries and places them in `~/hero/bin/` (or `/root/hero/bin/` with `--root`). - [x] `start` registers both actions + the service with hero_proc, starts, and surfaces both sockets in the summary. - [x] `status` reports the hero_proc record. - [x] `stop` cleanly unregisters (sockets persist as stale inodes — benign, see note above). - [x] `--root` optional, user-level default. - [x] Smoke-tested end-to-end on the Hetzner box.
Author
Owner

PR opened: #83

PR opened: https://forge.ourworld.tf/lhumina_code/hero_skills/pulls/83
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_skills#82
No description provided.