diff --git a/crates/desktop/artcraft/src/core/state/data_dir/app_credentials_dir.rs b/crates/desktop/artcraft/src/core/state/data_dir/app_credentials_dir.rs
--- a/crates/desktop/artcraft/src/core/state/data_dir/app_credentials_dir.rs
+++ b/crates/desktop/artcraft/src/core/state/data_dir/app_credentials_dir.rs
@@ -21,6 +21,19 @@ impl DataSubdir for AppCredentialsDir {
 }
 
 impl AppCredentialsDir {
+  /// Session cookies, bearer and refresh tokens and API keys are plain files in here, so on Unix
+  /// the directory is readable by its owner only (0700). Created with the default 0755, any other
+  /// account on the machine could read them (home folders are 0755 on macOS). Folders made by
+  /// earlier versions are tightened too.
+  pub fn restrict_to_owner(&self) -> std::io::Result<()> {
+    #[cfg(unix)]
+    {
+      use std::os::unix::fs::PermissionsExt;
+      std::fs::set_permissions(&self.path, std::fs::Permissions::from_mode(0o700))?;
+    }
+    Ok(())
+  }
+
   pub fn get_grok_state_path(&self) -> PathBuf {
     self.path.join("grok_state.json")
   }
@@ -82,3 +95,33 @@ impl AppCredentialsDir {
     self.path.join("worldlabs_refresh.txt")
   }
 }
+
+#[cfg(all(test, unix))]
+mod tests {
+  use crate::core::state::data_dir::app_data_root::AppDataRoot;
+  use crate::core::state::data_dir::trait_data_subdir::DataSubdir;
+  use std::os::unix::fs::PermissionsExt;
+
+  fn mode(path: &std::path::Path) -> u32 {
+    std::fs::metadata(path).unwrap().permissions().mode() & 0o777
+  }
+
+  #[test]
+  fn new_credentials_dir_is_private_to_its_owner() {
+    let dir = tempfile::tempdir().unwrap();
+    let root = AppDataRoot::create_existing(dir.path()).unwrap();
+    assert_eq!(mode(root.credentials_dir().path()), 0o700);
+  }
+
+  #[test]
+  fn existing_credentials_dir_is_made_private() {
+    // Folders made by earlier versions are 0755 under the usual umask: other local accounts could
+    // read the session cookies and API keys inside.
+    let dir = tempfile::tempdir().unwrap();
+    let credentials = dir.path().join("credentials");
+    std::fs::create_dir(&credentials).unwrap();
+    std::fs::set_permissions(&credentials, std::fs::Permissions::from_mode(0o755)).unwrap();
+    let root = AppDataRoot::create_existing(dir.path()).unwrap();
+    assert_eq!(mode(root.credentials_dir().path()), 0o700);
+  }
+}
diff --git a/crates/desktop/artcraft/src/core/state/data_dir/app_data_root.rs b/crates/desktop/artcraft/src/core/state/data_dir/app_data_root.rs
--- a/crates/desktop/artcraft/src/core/state/data_dir/app_data_root.rs
+++ b/crates/desktop/artcraft/src/core/state/data_dir/app_data_root.rs
@@ -65,6 +65,9 @@ impl AppDataRoot {
     
     let assets_dir = AppAssetsDir::get_or_create_in_root_dir(&dir)?;
     let credentials_dir = AppCredentialsDir::get_or_create_in_root_dir(&dir)?;
+    if let Err(err) = credentials_dir.restrict_to_owner() {
+      println!("Couldn't restrict {:?} to its owner: {}", credentials_dir.path(), err);
+    }
     let downloads_dir = AppDownloadsDir::get_or_create_in_root_dir(&dir)?;
     let settings_dir = AppSettingsDir::get_or_create_in_root_dir(&dir)?;
     let state_dir = AppStateDir::get_or_create_in_root_dir(&dir)?;
