diff --git a/kiosk/src/remote_debug.rs b/kiosk/src/remote_debug.rs index be25cee..08110fb 100644 --- a/kiosk/src/remote_debug.rs +++ b/kiosk/src/remote_debug.rs @@ -164,14 +164,12 @@ pub fn check_terminal_access() -> Result<(), String> { if is_locked() { return Err("locked".to_string()); } - // Check firmware channel — only dev allowed. The channel comes from - // the server-side kiosk config, delivered via heartbeat. Read from the - // cached bundle or the kiosk_app_version string (dev builds contain - // "-dev." in the version). No env var dependency. - let version = option_env!("BF_BUILD_VERSION") - .unwrap_or(env!("CARGO_PKG_VERSION")); - let is_dev = version.contains("-dev."); - if !is_dev { + // Check channel — terminal allowed when EITHER firmware or OS channel + // is "dev". Channels pushed from server via heartbeat response, cached + // in server.rs. No env var, no build-time check. + let fw = crate::server::cached_firmware_channel(); + let os = crate::server::cached_os_channel(); + if fw != "dev" && os != "dev" { return Err("terminal access requires dev channel".to_string()); } Ok(()) diff --git a/kiosk/src/server.rs b/kiosk/src/server.rs index 3e9cbdb..2d4e2fa 100644 --- a/kiosk/src/server.rs +++ b/kiosk/src/server.rs @@ -418,6 +418,31 @@ pub fn heartbeat( })) .timeout(Duration::from_secs(5)) .send() - .map(|r| r.status().is_success()) + .and_then(|r| { + if !r.status().is_success() { + return Ok(false); + } + // Parse channels from heartbeat response and cache for terminal access check. + if let Ok(body) = r.json::() { + if let Some(fc) = body.get("firmware_channel").and_then(|v| v.as_str()) { + CACHED_FIRMWARE_CHANNEL.lock().unwrap().replace(fc.to_string()); + } + if let Some(oc) = body.get("os_update_channel").and_then(|v| v.as_str()) { + CACHED_OS_CHANNEL.lock().unwrap().replace(oc.to_string()); + } + } + Ok(true) + }) .unwrap_or(false) } + +use std::sync::Mutex as StdMutex; +static CACHED_FIRMWARE_CHANNEL: StdMutex> = StdMutex::new(None); +static CACHED_OS_CHANNEL: StdMutex> = StdMutex::new(None); + +pub fn cached_firmware_channel() -> String { + CACHED_FIRMWARE_CHANNEL.lock().unwrap().clone().unwrap_or_else(|| "stable".to_string()) +} +pub fn cached_os_channel() -> String { + CACHED_OS_CHANNEL.lock().unwrap().clone().unwrap_or_else(|| "stable".to_string()) +} diff --git a/server/src/plugins/service-api-http/index.ts b/server/src/plugins/service-api-http/index.ts index 382b5d9..b77fab7 100644 --- a/server/src/plugins/service-api-http/index.ts +++ b/server/src/plugins/service-api-http/index.ts @@ -479,6 +479,8 @@ function registerKioskRoutes( return { ok: true, now: new Date().toISOString(), + firmware_channel: fresh?.firmware_channel ?? "stable", + os_update_channel: fresh?.os_update_channel ?? "stable", ...(pendingConfig ? { pending_config: pendingConfig } : {}), }; });