Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
7b2725b
fix(client): stable macos hostname via scutil, log it at registration…
danylo-babenko-flamingo Jul 23, 2026
73e81b3
fix(client): prefer kernel hostname, fall back to scutil on junk valu…
danylo-babenko-flamingo Jul 23, 2026
4f6f840
feat(client): self-deactivate on gateway 410 after tenant deletion (#…
mikhailm-coder Jul 24, 2026
677b76b
feat: scheduled scripts
denys-gif Jul 24, 2026
00ce32c
add non-blocking doctor check for WebView2 runtime on Windows (#2144)
mikhailm-coder Jul 27, 2026
14b9ae4
fix: backfill persisted machine info when upgrading from older client…
mikhailm-coder Jul 27, 2026
1e15638
fix(client): back off launch-failure log spam in the tool run loop (#…
mikhailm-coder Jul 28, 2026
0fde660
fix(client): mesh self-heal heals a missing .msh instead of restart-l…
mikhailm-coder Jul 28, 2026
604812c
fix(client): detect and remediate tool processes that survive an upda…
mikhailm-coder Jul 28, 2026
6478bcd
fix(client): timebox all blocking SCM calls so a wedged service can't…
mikhailm-coder Jul 28, 2026
7816546
fix: bound tool uninstall command with a timeout and stream its outpu…
danylo-babenko-flamingo Jul 28, 2026
311e968
chore: move tests to a separate files
denys-gif Jul 30, 2026
05febb5
fix(client): re-publish tool agent ids on every start so re-keyed age…
mikhailm-coder Jul 31, 2026
9da2dcd
fix: update tool id
denys-gif Jul 31, 2026
c3462a4
chore(client): update service-manager 0.8 -> 0.11 (#2204)
mikhailm-coder Aug 3, 2026
ee86a0f
fix(client): re-resolve tool agent id on every republish tick, wake l…
mikhailm-coder Aug 4, 2026
9f8ad67
fix: stale chile script process
denys-gif Aug 6, 2026
215c353
feat: remove max script cap
denys-gif Aug 7, 2026
df8c756
fix(client): kill timed-out agentId command; mesh self-heal refreshes…
mikhailm-coder Aug 7, 2026
2810875
fix(clippy): re-apply lib-specific clippy fixes over freshly ported f…
mikhailm-coder Aug 11, 2026
cd0233b
docs: add technical-writer docs for services newer than this repo's d…
mikhailm-coder Aug 11, 2026
a28d1a6
Merge branch 'main' into hotfix/port-tenant-client-updates-3
mikhailm-coder Aug 11, 2026
79570df
Merge branch 'main' into hotfix/port-tenant-client-updates-3
mikhailm-coder Aug 11, 2026
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
103 changes: 21 additions & 82 deletions clients/openframe-client/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions clients/openframe-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,21 @@ tracing-appender = "0.2"
flate2 = "1.0"
indexmap = "2.0"
tar = "0.4"
service-manager = "0.8"
service-manager = "0.11"
plist = "1.7.1"
# System information collection dependencies
hostname = "0.3"
hostname = "0.4"
bytes = "1.10.1"
async-nats = { git = "https://github.com/flamingo-stack/nats.rs.git", rev = "3cfbf0f393a964be1255b382065079a82f316069", features = ["websockets"] }
rustls-pemfile = "1.0"
redb = "2"
regex = "1.11.1"

[target.'cfg(windows)'.dependencies]
winreg = "0.52"
winapi = { version = "0.3", features = ["winuser", "shellapi", "securitybaseapi", "errhandlingapi", "winerror", "wincon", "consoleapi", "processenv", "winbase"] }
is_elevated = "0.1"
windows-service = "0.6"
windows-service = "0.8"
windows = { version = "0.52", features = ["Win32_Foundation", "Win32_System_Threading", "Win32_System_RemoteDesktop", "Win32_System_JobObjects", "Win32_UI_WindowsAndMessaging", "Win32_System_RestartManager", "Win32_Security", "Win32_Security_Authorization", "Win32_System_Registry", "Win32_System_Environment"] }

[target.'cfg(unix)'.dependencies]
Expand Down
12 changes: 11 additions & 1 deletion clients/openframe-client/src/clients/auth_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@ use reqwest::{
Client,
};
use std::collections::HashMap;
use std::sync::Arc;

use crate::models::AgentTokenResponse;
use crate::services::deactivation_service::DeactivationService;

#[derive(Clone)]
pub struct AuthClient {
http_client: Client,
base_url: String,
deactivation: Arc<DeactivationService>,
}

impl AuthClient {
pub fn new(base_url: String, http_client: Client) -> Self {
pub fn new(
base_url: String,
http_client: Client,
deactivation: Arc<DeactivationService>,
) -> Self {
Self {
http_client,
base_url,
deactivation,
}
}

Expand Down Expand Up @@ -49,6 +57,7 @@ impl AuthClient {
.context("Failed to send token request")?;

let status = response.status();
self.deactivation.on_gateway_status(status).await;

if !status.is_success() {
return Err(anyhow::anyhow!(
Expand Down Expand Up @@ -92,6 +101,7 @@ impl AuthClient {
.context("Failed to send refresh token request")?;

let status = response.status();
self.deactivation.on_gateway_status(status).await;

if !status.is_success() {
return Err(anyhow::anyhow!(
Expand Down
37 changes: 2 additions & 35 deletions clients/openframe-client/src/clients/registration_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,38 +118,5 @@ fn is_client_secret_error(status: StatusCode, body: &str) -> bool {
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn detects_client_secret_invalid() {
let body = r#"{"code":"CLIENT_SECRET_INVALID","message":"Invalid client secret"}"#;
assert!(is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn detects_client_secret_empty() {
let body = r#"{"code":"CLIENT_SECRET_EMPTY","message":"Client secret is empty"}"#;
assert!(is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn ignores_other_401_error_codes() {
let body = r#"{"code":"INITIAL_KEY_INVALID","message":"..."}"#;
assert!(!is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn ignores_client_secret_error_on_non_401() {
let body = r#"{"code":"CLIENT_SECRET_INVALID"}"#;
assert!(!is_client_secret_error(StatusCode::BAD_REQUEST, body));
}

#[test]
fn handles_non_json_body() {
assert!(!is_client_secret_error(
StatusCode::UNAUTHORIZED,
"gateway timeout"
));
}
}
#[path = "registration_client_tests.rs"]
mod tests;
33 changes: 33 additions & 0 deletions clients/openframe-client/src/clients/registration_client_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use super::*;

#[test]
fn detects_client_secret_invalid() {
let body = r#"{"code":"CLIENT_SECRET_INVALID","message":"Invalid client secret"}"#;
assert!(is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn detects_client_secret_empty() {
let body = r#"{"code":"CLIENT_SECRET_EMPTY","message":"Client secret is empty"}"#;
assert!(is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn ignores_other_401_error_codes() {
let body = r#"{"code":"INITIAL_KEY_INVALID","message":"..."}"#;
assert!(!is_client_secret_error(StatusCode::UNAUTHORIZED, body));
}

#[test]
fn ignores_client_secret_error_on_non_401() {
let body = r#"{"code":"CLIENT_SECRET_INVALID"}"#;
assert!(!is_client_secret_error(StatusCode::BAD_REQUEST, body));
}

#[test]
fn handles_non_json_body() {
assert!(!is_client_secret_error(
StatusCode::UNAUTHORIZED,
"gateway timeout"
));
}
12 changes: 11 additions & 1 deletion clients/openframe-client/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,24 @@ pub mod service_stop {
pub const FORCE_KILL_TIMEOUT_SECS: u64 = 3;
/// Force-kill attempts per process.
pub const MAX_KILL_RETRIES: u32 = 3;
/// Polls awaiting a Windows service to report STOPPED (~10s).
/// Polls awaiting a Windows service state change (~10s; up to ~210s if SCM queries keep timing out non-consecutively).
pub const SERVICE_STOP_MAX_ATTEMPTS: u32 = 20;
/// Force-kill rounds for a stuck service (margin over Windows' 3 auto-restarts).
pub const SERVICE_FORCE_KILL_MAX_ATTEMPTS: u32 = 6;
/// Cap on a blocking SCM `stop()` before force-killing (else hangs ~4 min).
pub const SERVICE_STOP_CALL_TIMEOUT_SECS: u64 = 10;
/// Start attempts for a service before giving up.
pub const SERVICE_START_MAX_ATTEMPTS: u32 = 3;
/// Cap on any blocking SCM query (status/config/delete) so a wedged SCM can't hang a task.
pub const SCM_QUERY_TIMEOUT_SECS: u64 = 10;
/// Cap on a blocking SCM `start()` call (StartService can stall behind a busy service).
pub const SERVICE_START_CALL_TIMEOUT_SECS: u64 = 30;
/// Consecutive SCM query failures before a wait loop stops polling and escalates.
pub const SCM_QUERY_MAX_CONSECUTIVE_FAILURES: u32 = 2;
/// Overall cap on a guarded tool restart; guarantees the updating flag and tool lock release.
pub const TOOL_RESTART_TIMEOUT_SECS: u64 = 600;
/// In-flight SCM call permits; bounds blocking threads a wedged SCM can park.
pub const SCM_MAX_IN_FLIGHT: usize = 4;
}

// Legacy config structs, currently unused (candidates for removal).
Expand Down
Loading
Loading