service_whiteboard.nu — hero_whiteboard server + UI lifecycle module #82
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_skills#82
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Child of #75.
Objective
Add
tools/modules/services/service_whiteboard.nuimplementing the standardinstall | start | stop | statuslifecycle for the hero_whiteboard service (server + UI) so it can be driven the same way asservice_os,service_books,service_browser.Scope
ssh://git@forge.ourworld.tf/lhumina_code/hero_whiteboard.gitbuildenv.sh):hero_whiteboard,hero_whiteboard_server,hero_whiteboard_uihero_whiteboard_server,hero_whiteboard_uilhumina_code/hero_zero/services/hero_whiteboard.toml$HERO_SOCKET_DIR/hero_whiteboard/rpc.sock,$HERO_SOCKET_DIR/hero_whiteboard/ui.sock(confirm in spec)RUST_LOG=infofor both server and UI — no placeholders to resolve.[package]) — plaincargo build --releaseshould build all three binaries. No--workspaceflag needed.--rootflag supported but optional; user-level is default.Acceptance criteria
use services/mod.nu *makesservice_whiteboardavailable.service_whiteboard install [--root] [--update]cloneslhumina_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.Template & references
service_os.nu(PR #78, merged) — clean two-binary pattern, virtual workspace, no env placeholders. This should be nearly a copy-rename.service_books.nu(PR #81, merged) for the hybrid-workspace +servesubcommand patterns, in case hero_whiteboard binaries turn out to require a subcommand (the spec should verify viahero_whiteboard_server --help).claude/skills/nu_service/SKILL.md(build),claude/skills/nu_service_use/SKILL.md(use).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.Implementation Spec for Issue #82
Objective
Add
tools/modules/services/service_whiteboard.nu— ainstall | start | stop | statuslifecycle module for thehero_whiteboardservice (server + UI) registered withhero_proc, following the minimal two-binary virtual-workspace pattern established byservice_os.nu.Requirements
install,start,stop,statusas subcommands ofservice_whiteboard.hero_procactions:hero_whiteboard_serverandhero_whiteboard_ui, bundled into ahero_whiteboardservice in contextcore.~/hero/bin(or/root/hero/binwith--root):hero_whiteboard(CLI, not ahero_procaction),hero_whiteboard_server,hero_whiteboard_ui.$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.RUST_LOG=info— noHERO_*env placeholders, no data dir computation.hero_whiteboard.tomldeclares nodepends_on; neither binary has hard-fail prerequisites.script: $bin) — neither binary has aservesubcommand;maincallsserve()directly. Confirmed inhero_whiteboard_server/src/main.rs:21-24andhero_whiteboard_ui/src/main.rs:85-90.Cargo.toml:1-10, no root[package]), so plainsvc_cargo_install(singlecargo build --release) is sufficient — no--workspacepass needed.--rootmust transparently target/root/hero/...via sudo, reusinglib.nuhelpers.svx_drop_registrationtears down any prior service + actions before re-adding.start: if already running and neither--resetnor--updategiven, print a hint and return.stopis safe when nothing is registered and whenhero_procitself is down (prints a warning and returns).statusrequireshero_procup and delegates toproc service status.Files to Modify/Create
tools/modules/services/service_whiteboard.nu— new, ~260 lines, cloned fromservice_os.nuwith the WASM assets preflight removed.tools/modules/services/mod.nu— add one line:export use service_whiteboard.nuafter the existingexport use service_books.nuon line 9.Implementation Plan
Step 1: Copy
service_os.nu→service_whiteboard.nuFiles:
tools/modules/services/service_whiteboard.nuservice_os.nuas the template (simpler thanservice_books.nu; no hybrid workspace, no env placeholders, noservesubcommand, no data dir).useimports → 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.nuservice_os.nu:1-41but:hero_whiteboard.toml:3).hero_whiteboard_serverandhero_whiteboard_ui.$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.hero_procdependency paragraph (same wording asservice_os.nu:13-15).service_os.nu:17-22) — whiteboard has no external assets preflight.--rootparagraph unchanged in spirit (service_os.nu:24-35).Dependencies: Step 1.
Step 3: Update constants
Files:
tools/modules/services/service_whiteboard.nuservice_os.nu:47-52equivalent, 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"]hero_whiteboard(CLI) is shipped but not registered as an action — same reasoning as inservice_os.nu:50-51.Dependencies: Step 1.
Step 4: Rename and rewrite
svx_server_actionFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:58-97verbatim, substitutinghero_os_server→hero_whiteboard_serverandhero_os→hero_whiteboardin every string (action name,kill_other.socket[0],health_checks[0].action,health_checks[0].openrpc_socket).env: {RUST_LOG: "info"}identical (matches the TOML).script: $bin(noservesubcommand — confirmed above).service_os.nu:68-95.Dependencies: Steps 1, 3.
Step 5: Rename and rewrite
svx_ui_actionFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:99-138verbatim, substitutinghero_os_ui→hero_whiteboard_uiandhero_os→hero_whiteboardin every string.script: $bin(noservesubcommand — confirmed inhero_whiteboard_ui/src/main.rs:85-90).service_os.nu:108-136.Dependencies: Steps 1, 3.
Step 6: Rewrite
svx_service_configFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:140-152. Setdescription: "Hero Whiteboard — collaborative drawing and diagramming server and UI". Keepcontext_name: "core",class: "system",critical: false,status: "start"unchanged.Dependencies: Steps 1, 3.
Step 7: Keep
svx_drop_registrationunchanged in shape; dropsvx_check_assetsFiles:
tools/modules/services/service_whiteboard.nusvx_drop_registrationis identical in structure toservice_os.nu:155-161; only the constants it closes over change (handled in Step 3).svx_check_assetshelper fromservice_os.nu:164-182. Whiteboard has no external asset dir or embedder socket — nothing to preflight.Dependencies: Step 1.
Step 8: Trim
installto the minimal formFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:191-198verbatim. Body is exactly:if $root { svc_require_sudo }if $update { svc_update $SVX_FORGE_LOC }svc_cargo_install $SVX_FORGE_LOC $SVX_BINARIES $rootservice_books.nuhybrid-workspace branch — whiteboard is a pure virtual workspace (verified athero_whiteboard/Cargo.toml:1-10), sosvc_cargo_install's internalcargo build --releasealready builds all three binaries.Dependencies: Steps 1, 3.
Step 9: Trim
start— remove the assets preflight callFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:215-293structure, substitutinghero_os→hero_whiteboardthroughout strings.svx_check_assets $rootcall (service_os.nu:252-253) and renumber the subsequent inline comments.svc_require_proc, idempotent already-running check,install --root=$root --update=$update, post-installbin_oksanity check, drop registration, register both actions + the service, start, settlesleep 1sec, final summary block.service_os.nu:273-292: copy verbatim with the usualhero_os→hero_whiteboardrename.Dependencies: Steps 1-7.
Step 10:
stopandstatusFiles:
tools/modules/services/service_whiteboard.nuservice_os.nu:306-321forstopandservice_os.nu:330-335forstatus, renaminghero_os→hero_whiteboardin all strings. No logic changes.Dependencies: Steps 1, 3, 7.
Step 11: Register in
mod.nuFiles:
tools/modules/services/mod.nuexport use service_whiteboard.nuas a new line afterservice_books.nu(the current line 9). Alphabetical grouping is not enforced inmod.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 --rootagain → 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.service_whiteboard status --root→ RPC errorservice 'hero_whiteboard' not found.Dependencies: Steps 1-11.
Acceptance Criteria
tools/modules/services/service_whiteboard.nuexists and exportsinstall,start,stop,status.tools/modules/services/mod.nure-exportsservice_whiteboard.nu.installbuilds all three binaries via onecargo build --releaseand places them under the correctbindir.startregistershero_whiteboard_server,hero_whiteboard_ui, and thehero_whiteboardservice in contextcore, then starts it; both UDS sockets appear at$HERO_SOCKET_DIR/hero_whiteboard/{rpc,ui}.sock.start --resettears down any prior registration and re-registers cleanly.startis idempotent when nothing changed.stopunregisters cleanly and is safe when nothing is registered or whenhero_procis down.statusrequireshero_procup and delegates toproc service status hero_whiteboard.--root (-r)and behave the same for root targets.Notes
hero_whiteboard/buildenv.sh:5):hero_whiteboard— CLI, shipped but NOT registered as ahero_procaction (mirrors thehero_osCLI role).hero_whiteboard_server— registered action; OpenRPC state server.hero_whiteboard_ui— registered action; serves user whiteboard at/and admin dashboard at/admin/overui.sock.app,examples,sdk) have no[[bin]]targets and are correctly absent fromSVX_BINARIES.[package]). Onecargo build --releaseat the workspace root builds all three bin targets. No--workspaceflag needed;svc_cargo_installis sufficient. Matcheshero_os, nothero_books.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).svc_sock_base-derived path thatservice_os.nu'ssvc_*_actionbuilders already compute — no changes to path derivation needed.servesubcommand. Bothmain.rsfiles invokeserve(...)directly from#[tokio::main] async fn main()with no clap parser.script: $binapplies. This is the first non-books service to confirm this — theserve-subcommand path is specific to books' dual-entrypoint CLI and is not the general pattern.hero_osnorhero_whiteboardtakesserve, and onlyhero_booksdoes, there is no "two-in-a-row" case to promote to a helper. Defer any refactor tolib.nuuntil a fourth service confirms which pattern is the outlier. Explicitly out of scope for this PR.service_os.nu.--rootflipssvc_home,svc_bin, andsvc_sock_baseto/root/hero/...vialib.nu; cargo builds always run in the invoking user's session; binaries are copied with sudo viasvc_install_binary.svc_require_sudoenforces passwordless sudo up-front when--rootis used.hero_whiteboard.tomlhas nodepends_onand both binaries self-heal (server creates its sqlite DB viadb::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 summary
Changes
tools/modules/services/service_whiteboard.nu— ~270 lines, near-identical copy ofservice_os.nuwith WASM asset preflight removed and names substituted.tools/modules/services/mod.nu— addedexport use service_whiteboard.nu.What the module does
service_whiteboard install [--root] [--update]— cloneslhumina_code/hero_whiteboard, runscargo 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 thehttp+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
servesubcommand, 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
service_proc start --roothealthyservice_whiteboard install --rootproduced 3 binariesservice_whiteboard start --reset --rootregisters + startsrpc.socklive unix socketui.socklive unix socketcurl --unix-socket rpc.sockaccepts HTTPcurl --unix-socket ui.sockaccepts HTTPservice_whiteboard statusreturns{name: hero_whiteboard, state: running, restarts: 0}--reset) prints "already running"service_whiteboard stop --rootstops and unregistersstatusreturnsservice 'hero_whiteboard' not foundNotes 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 expectedservice 'hero_whiteboard' not foundRPC 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_servernorhero_whiteboard_uiunlink their Unix-socket path on SIGTERM (unlike hero_os_server / hero_books_server which do). The stale socket files are harmless — thekill_other.socketcleanup in the action spec unlinks them on the nextstart, so re-register / restart paths stay clean.Not in scope for this PR. Worth filing a follow-up against
lhumina_code/hero_whiteboardif we want the binaries to clean up after themselves on shutdown.Acceptance criteria
use services/mod.nu *oruse services/service_whiteboard.nu *.installbuilds 3 binaries and places them in~/hero/bin/(or/root/hero/bin/with--root).startregisters both actions + the service with hero_proc, starts, and surfaces both sockets in the summary.statusreports the hero_proc record.stopcleanly unregisters (sockets persist as stale inodes — benign, see note above).--rootoptional, user-level default.PR opened: #83