Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .facts
Original file line number Diff line number Diff line change
Expand Up @@ -2355,6 +2355,7 @@
- Windows durable bootstrap failures retain only a closed set of launch-stage names from diagnostic stderr; raw remote diagnostics and credentials are never reproduced @spec @mvp @implemented
- Windows durable SSH bootstrap transfers its token through the existing private pipe, commits desktop task ownership after the daemon ready frame within the resolved native-plus-provider readiness budget, and survives the SSH launcher exiting; foreground bootstrap still stops its child when the controller disconnects @spec @mvp
- an interrupted Windows daemon-start claim can recover only when stale exact evidence and two process snapshots prove that its fenced wrapper, scheduled bootstrap tasks, and daemon launchers are absent, together with the daemon, service, and listener; unknown process metadata keeps the claim fenced, and recovery preserves the original claim and mailbox in an archive without fabricating a terminal result @spec @mvp @implemented
- POSIX bootstrap recovery detects Host commands from full process arguments and macOS probes the canonical dev.microck.satelle.host launchd service before and after quarantining a claim; an absent historical label is not evidence that the current service is inactive @spec @mvp @implemented
- satelle supports direct transport for hosts where SSH bootstrap is unavailable but a Host Daemon is already installed and reachable @spec @mvp @implemented

# research
Expand Down
43 changes: 36 additions & 7 deletions crates/satelle/src/bootstrap-lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,8 +1069,8 @@ for competitor in "$lock_root"/*; do
fi
fi
process_probe=null
if process_output="$(ps -eo pid=,comm=,args= 2>/dev/null)"; then
if printf '%s\n' "$process_output" | awk -v self="$$" -v parent="$PPID" '$1 != self && $1 != parent && ($2 == "satelle" || $2 == "satelle.exe") && $0 ~ /host start/ {{ found=1 }} END {{ exit !found }}'; then
if process_output="$(ps -ww -eo pid=,args= 2>/dev/null)"; then
if printf '%s\n' "$process_output" | awk -v self="$$" -v parent="$PPID" '$1 != self && $1 != parent && $0 ~ /(^|[[:space:]\/])satelle(\.exe)?[[:space:]]+host[[:space:]]+start([[:space:]]|$)/ {{ found=1 }} END {{ exit !found }}'; then
process_probe=true
else
probe_status=$?
Expand All @@ -1090,7 +1090,7 @@ for competitor in "$lock_root"/*; do
case "$probe_status" in 3|4) service_probe=false;; esac
fi
elif command -v launchctl >/dev/null 2>&1; then
if launchctl print "gui/$(id -u)/satelle-host" >/dev/null 2>&1; then
if launchctl print "gui/$(id -u)/dev.microck.satelle.host" >/dev/null 2>&1; then
service_probe=true
else
probe_status=$?
Expand Down Expand Up @@ -1254,8 +1254,8 @@ for competitor in "$lock_root"/*; do
fi
if [ "$failed_daemon_start" = true ] || [ "$released_state_owner" = true ]; then
post_process_probe=null
if process_output="$(ps -eo pid=,comm=,args= 2>/dev/null)"; then
if printf '%s\n' "$process_output" | awk -v self="$$" -v parent="$PPID" '$1 != self && $1 != parent && ($2 == "satelle" || $2 == "satelle.exe") && $0 ~ /host start/ {{ found=1 }} END {{ exit !found }}'; then
if process_output="$(ps -ww -eo pid=,args= 2>/dev/null)"; then
if printf '%s\n' "$process_output" | awk -v self="$$" -v parent="$PPID" '$1 != self && $1 != parent && $0 ~ /(^|[[:space:]\/])satelle(\.exe)?[[:space:]]+host[[:space:]]+start([[:space:]]|$)/ {{ found=1 }} END {{ exit !found }}'; then
post_process_probe=true
else
probe_status=$?
Expand All @@ -1269,7 +1269,7 @@ for competitor in "$lock_root"/*; do
case "$probe_status" in 3|4) post_service_probe=false;; esac
fi
elif command -v launchctl >/dev/null 2>&1; then
if launchctl print "gui/$(id -u)/satelle-host" >/dev/null 2>&1; then post_service_probe=true; else
if launchctl print "gui/$(id -u)/dev.microck.satelle.host" >/dev/null 2>&1; then post_service_probe=true; else
probe_status=$?
[ "$probe_status" -eq 113 ] && post_service_probe=false
fi
Expand Down Expand Up @@ -1523,7 +1523,7 @@ mod tests {
"acquired_at",
"heartbeat_at",
HEARTBEAT,
"ps -eo pid=,comm=,args=",
"ps -ww -eo pid=,args=",
"binary_present",
"systemctl --user is-active",
"launchctl print",
Expand Down Expand Up @@ -2637,6 +2637,35 @@ foreach ($body in @('satelle.exe host status', 'Write-Output unrelated')) {{
}
}

#[cfg(unix)]
#[test]
fn running_host_paths_keep_interrupted_release_fenced() {
let attempt = "0123456789abcdef0123456789abcdef";
for command in [
"satelle host start --foreground",
"/Users/microck/Library/Caches/Satelle/host/v0.1.32/darwin-arm64/satelle host start --foreground --launchd-service",
"/home/user with spaces/.cache/satelle host start --foreground",
] {
let home = tempfile::tempdir().expect("temporary process probe home");
let lock = home.path().join("satelle/bootstrap.lock");
let claim = write_stale_mutation_claim(&lock, "state_owner_release", attempt);
fs::create_dir(claim.join(format!("execution_started.{attempt}"))).unwrap();
let path = path_with_inactive_daemon_probe(home.path());
fs::write(
home.path().join("probe-bin/ps"),
format!("#!/bin/sh\nprintf '%s\\n' '999999 {command}'\n"),
)
.unwrap();
let replacement =
Request::new("operation-2", OperationKind::MissingDaemonRepair, None).unwrap();
let mut contender =
RunningProtocol::start_with_path(&replacement, home.path(), Some(&path));
assert_eq!(contender.read_line(), BUSY, "{command}");
assert_eq!(contender.close().code(), Some(75));
assert!(claim.join(format!("execution_started.{attempt}")).is_dir());
}
}

#[cfg(unix)]
#[test]
fn retirement_sentinel_reconciles_every_valid_crash_point() {
Expand Down
Loading