diff --git a/app/MindWork AI Studio/wwwroot/changelog/v26.6.1.md b/app/MindWork AI Studio/wwwroot/changelog/v26.6.1.md index 83665a06..9eb7e830 100644 --- a/app/MindWork AI Studio/wwwroot/changelog/v26.6.1.md +++ b/app/MindWork AI Studio/wwwroot/changelog/v26.6.1.md @@ -1,5 +1,6 @@ # v26.6.1, build 241 (2026-06-xx xx:xx UTC) - Added support for up to 100 thousand enterprise configuration slots, using fixed-width slot names such as `config_00000` while keeping the existing first ten slot names compatible. - Added support for managed custom root certificate bundles and host allowlists for external HTTPS requests, helping Flatpak deployments connect to organization-internal services with private root CAs while keeping built-in cloud provider endpoints on system trust. +- Added support for reading enterprise policy files from a Flatpak provisioning extension. - Added startup path and Linux package type details to the information page to make support easier. - Improved the enterprise configuration details on the information page by showing where each configuration comes from and which configuration slot was used. diff --git a/documentation/Enterprise IT.md b/documentation/Enterprise IT.md index 3b61a59f..3d7a9c1b 100644 --- a/documentation/Enterprise IT.md +++ b/documentation/Enterprise IT.md @@ -79,6 +79,28 @@ AI Studio checks each directory listed in `$XDG_CONFIG_DIRS` and looks for a `mi The directories from `$XDG_CONFIG_DIRS` are processed in order. +#### Flatpak policy directory + +When AI Studio runs as a Flatpak, it first checks this sandbox path before the regular Linux policy directories: + +`/app/etc/MindWorkAI/` + +This path is intended for a Flatpak provisioning extension like: + +```yaml +add-extensions: + org.MindWorkAI.AIStudio.provisioning: + directory: etc/MindWorkAI + no-autodownload: true +``` + +Policy files can then be provided on the host through the extension directories. For example: + +- System-wide, read-only: `/var/lib/flatpak/extension/org.MindWorkAI.AIStudio.provisioning/x86_64/stable/` +- User-specific: `$XDG_DATA_HOME/flatpak/extension/org.MindWorkAI.AIStudio.provisioning/x86_64/stable/` + +Files placed there are mounted into the sandbox at `/app/etc/MindWorkAI/`. Use the same policy file names and YAML format described below. + #### macOS policy directory `/Library/Application Support/MindWork/AI Studio/` diff --git a/runtime/src/environment.rs b/runtime/src/environment.rs index 202e1b06..400b2fa8 100644 --- a/runtime/src/environment.rs +++ b/runtime/src/environment.rs @@ -22,6 +22,9 @@ const ENTERPRISE_REGISTRY_KEY_PATH: &str = r"Software\github\MindWork AI Studio\ const ENTERPRISE_POLICY_SECRET_FILE_NAME: &str = "config_encryption_secret.yaml"; +#[cfg(any(target_os = "linux", test))] +const FLATPAK_ENTERPRISE_POLICY_DIRECTORY: &str = "/app/etc/MindWorkAI"; + const ENTERPRISE_ENV_CONFIG_ID_PREFIX: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_ID"; const ENTERPRISE_ENV_CONFIG_SERVER_URL_PREFIX: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIG_SERVER_URL"; const ENTERPRISE_ENV_CONFIGS: &str = "MINDWORK_AI_STUDIO_ENTERPRISE_CONFIGS"; @@ -547,7 +550,7 @@ fn enterprise_policy_directories() -> Vec { #[cfg(target_os = "linux")] fn enterprise_policy_directories() -> Vec { let xdg_config_dirs = env::var("XDG_CONFIG_DIRS").ok(); - linux_policy_directories_from_xdg(xdg_config_dirs.as_deref()) + linux_policy_directories_from_xdg(xdg_config_dirs.as_deref(), is_flatpak()) } #[cfg(target_os = "macos")] @@ -563,17 +566,23 @@ fn enterprise_policy_directories() -> Vec { } #[cfg(any(target_os = "linux", test))] -fn linux_policy_directories_from_xdg(xdg_config_dirs: Option<&str>) -> Vec { +fn linux_policy_directories_from_xdg(xdg_config_dirs: Option<&str>, include_flatpak_provisioning: bool) -> Vec { let mut directories = Vec::new(); + if include_flatpak_provisioning { + directories.push(PathBuf::from(FLATPAK_ENTERPRISE_POLICY_DIRECTORY)); + } + + let mut has_linux_policy_directory = false; if let Some(raw_directories) = xdg_config_dirs { for path in raw_directories.split(':') { if let Some(path) = normalize_enterprise_value(path) { directories.push(PathBuf::from(path).join("mindwork-ai-studio")); + has_linux_policy_directory = true; } } } - if directories.is_empty() { + if !has_linux_policy_directory { directories.push(PathBuf::from("/etc/xdg/mindwork-ai-studio")); } @@ -1313,7 +1322,7 @@ mod tests { #[test] fn linux_policy_directories_from_xdg_preserves_order_and_falls_back() { assert_eq!( - linux_policy_directories_from_xdg(Some(" /opt/company:/etc/xdg ")), + linux_policy_directories_from_xdg(Some(" /opt/company:/etc/xdg "), false), vec![ PathBuf::from("/opt/company/mindwork-ai-studio"), PathBuf::from("/etc/xdg/mindwork-ai-studio"), @@ -1321,15 +1330,35 @@ mod tests { ); assert_eq!( - linux_policy_directories_from_xdg(Some(" : ")), + linux_policy_directories_from_xdg(Some(" : "), false), vec![PathBuf::from("/etc/xdg/mindwork-ai-studio")] ); assert_eq!( - linux_policy_directories_from_xdg(None), + linux_policy_directories_from_xdg(None, false), vec![PathBuf::from("/etc/xdg/mindwork-ai-studio")] ); } + #[test] + fn linux_policy_directories_from_xdg_checks_flatpak_provisioning_first() { + assert_eq!( + linux_policy_directories_from_xdg(Some(" /opt/company:/etc/xdg "), true), + vec![ + PathBuf::from("/app/etc/MindWorkAI"), + PathBuf::from("/opt/company/mindwork-ai-studio"), + PathBuf::from("/etc/xdg/mindwork-ai-studio"), + ] + ); + + assert_eq!( + linux_policy_directories_from_xdg(None, true), + vec![ + PathBuf::from("/app/etc/MindWorkAI"), + PathBuf::from("/etc/xdg/mindwork-ai-studio"), + ] + ); + } + #[test] fn load_policy_values_from_directories_uses_first_directory_wins() { let directory_a = tempdir().unwrap();