Repository navigation
Conversation
|
The macOS launcher failure is consistent with the existing GNU-stat and Linux-process assumptions. This patch changes permission and ownership checks authorizing script execution as well as process/session tracking ( |
432abbc to
ec9d6f9
Compare
|
Thanks for the note on the execution-boundary checks. Their meaning is unchanged: a script or directory that is world-writable, not owned by the user, outside the home or a symlink is refused, and so is one whose mode or owner cannot be read. I pushed tests that pin the last part, each case isolating one check (mode unreadable, owner unreadable, both, another owner): Start and Stop refuse with exit 126 and the matching reason, and making an unreadable mode fail open, or dropping the owner check, makes the new test fail. Rebased on main ( |
|
The updated launcher suite passes all 24 tests on Linux, including new refusals for unreadable mode, unreadable owner and another owner ( |
The shell sparkDash runs over SSH for a model launcher read /proc, used GNU stat -c and started the script with setsid. None exist on macOS, so every Start and Stop ended at "refusing: <dir> is world-writable". Read process facts from /proc where it exists and from ps elsewhere, fall back to BSD stat -f, and use perl's POSIX::setsid when there is no setsid. ps start times are read with LC_ALL=C. Linux units run the same commands as before. launcher.test.js: 14 of 21 failed on macOS before; 23 of 23 pass on macOS and on Linux, including two new tests (recorded start time, log size cap).
ec9d6f9 to
ae677bb
Compare
|
Understood that this needs a maintainer's approval. To make that review quick I added the exact delta for the privilege-relevant parts to "Merge risk" in the description: authorization ( |
|
Re-checked the updated head ae677bb: typecheck passes and the full server run is 1115/1132 pass with one |
|
Now on main as 9a67b41 (cherry-picked with |
The drain change from #218 passes GNU-only --pid to tail, so on macOS the follower exited at once and a Start printed nothing and never emitted the exit marker, undoing the #264 fix. Probe for --pid and otherwise use the earlier timed shutdown. Regression test uses a tail that rejects --pid. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Summary
Start and Stop now work on macOS units. On a Mac unit every Start and Stop of a registered model ended at
[sparkdash] refusing: <dir> is world-writable, even for a 755 directory, so a launcher could not be used there at all. Linux units run the same commands as before, and the safety checks still fail closed.Why
The shell sparkDash runs over SSH (
server/llmlaunch/commands.js) reads/proc/<pid>/stat, calls GNUstat -cand starts the script withsetsid. macOS has none of the three. The failing check iswwrite, which treats a mode it cannot read as world-writable, so the unreadablestat -cresult blocked every launch. Mac units are advertised as supported, and the launcher tests could not pass on a Mac.How it works
/procwhere it exists and frompselsewhere.psruns withLC_ALL=C: its start time is a date printed in the session's language, and a pid file written under one locale has to match when read under another.stattries the GNU form first and falls back to BSDstat -ffor mode, owner and size.newsidstarts the script as session leader withsetsid, or with perl'sPOSIX::setsidwhere there is none (macOS ships perl).alive()testskill -0 $PIDinstead of[ -d /proc/$PID ], keeping the start-time check against pid reuse.Evidence
launcher.test.jsruns the generated programs with real bash. On a Mac, 14 of its 21 tests fail on current main; with this change all 24 pass on macOS (bash 3.2) and on Linux (bash 5, util-linuxsetsid). Three tests are new: the pid file records the real start time (also underLC_ALL=de_DE.UTF-8); a running script's log is cut back past its size cap (this exercises BSDstat -f %z); and Start and Stop refuse, with exit 126 and the right reason, whenstatcannot report the mode, cannot report the owner, can report neither, or reports another owner. Each case isolates one check: making an unreadable mode fail open, or removing the owner check, makes the new test fail. Also run:npm run typecheck,npm run build, vitest, and the server suite, where the remaining failures are the ones current main already has on a Mac.Driven end to end from a Linux host through the launcher API on two MacBooks: Start, status
running, Stop, statusstopped, port closed, with a launchd service and with a real MLX server lane.Merge risk
This touches the code that decides whether a script may be executed, so here is the exact delta for the privilege-relevant parts:
wwrite,check_script). Only how the mode and owner are read changes: GNUstat -c, then BSDstat -f(fmode,fowner). The decisions are the same: a script or directory that is world-writable, not owned by the user, outside the home or a symlink is refused, and so is one whose mode or owner cannot be read. Three of the four new refusal cases isolate one check each.pstart,ppidof,alive,newsid). These decide whether a second Start launches another copy and whether status reads running; they never grant execution.alive()now testskill -0 $PIDinstead of[ -d /proc/$PID ]: a pid that belongs to another user (reuse) answers EPERM and counts as not alive, which is the same outcome the start-time check gave before, and a legacy pid file without a start time is treated as before.newsidruns the same script as the same user with the same arguments; on macOS it execs throughperl -MPOSIXinstead ofsetsid, and a missing perl makes the start fail rather than run unmanaged.Linux behaviour is otherwise the same commands and the same results in the existing tests. macOS has no
flock, so the existing "one start at a time" lock stays a no-op there; the manager's one-job-per-unit rule still applies.Scope
In: the launcher shell and its tests, plus a CHANGELOG entry. Out: launchd lane scripts (a user script that runs
launchctl), andapi.test.js, which needs the local-Spark path and fails on macOS for an unrelated reason.