fix: chmod claude dir 0755 so hive-core can list session files

claude_has_session() calls read_dir() on the agent's claude/ dir to
detect a valid session. The dir was 0700 (create-only path), so
hive-core (a different unix user) got EACCES → read_dir returns Err
→ has_session returns false → every agent shows 'needs login' in the
dashboard even when working.

Fix: ensure_claude_dir now always sets 0755. The credential files
inside (e.g. .credentials.json) are 0600, so the secrets stay private
regardless of the containing directory's mode. Existing 0700 dirs are
corrected on the next spawn/rebuild cycle that calls ensure_claude_dir.
This commit is contained in:
atlas 2026-06-04 11:37:42 +02:00
commit bc0087830f

View file

@ -242,7 +242,13 @@ pub async fn spawn(name: &str, hive: &HiveEnv, paths: &AgentPaths) -> Result<()>
crate::meta::sync_agents(hive, &agents).await?;
let container = container_name(name);
priv_run("create", name).await?;
set_nspawn_flags(&container, &paths.agent_dir, &paths.claude_dir, &paths.notes_dir).await?;
set_nspawn_flags(
&container,
&paths.agent_dir,
&paths.claude_dir,
&paths.notes_dir,
)
.await?;
set_resource_limits(&container, &hive.agent_cpu_quota, &hive.agent_memory_max).await?;
systemd_daemon_reload().await?;
priv_run("start", name).await
@ -418,7 +424,13 @@ pub async fn rebuild_no_meta(
// Rebuild strategy: stop-before-update + pre-build.
// See `docs/coordinator.md::Container lifecycle`.
let was_running = is_running(name).await;
set_nspawn_flags(&container, &paths.agent_dir, &paths.claude_dir, &paths.notes_dir).await?;
set_nspawn_flags(
&container,
&paths.agent_dir,
&paths.claude_dir,
&paths.notes_dir,
)
.await?;
set_resource_limits(&container, &hive.agent_cpu_quota, &hive.agent_memory_max).await?;
systemd_daemon_reload().await?;
if was_running {
@ -490,7 +502,13 @@ pub async fn rebuild_no_meta(
// See `docs/coordinator.md::Spawn path`.
on_step("nixos-container create");
priv_run("create", name).await?;
set_nspawn_flags(&container, &paths.agent_dir, &paths.claude_dir, &paths.notes_dir).await?;
set_nspawn_flags(
&container,
&paths.agent_dir,
&paths.claude_dir,
&paths.notes_dir,
)
.await?;
set_resource_limits(&container, &hive.agent_cpu_quota, &hive.agent_memory_max).await?;
systemd_daemon_reload().await?;
on_step("nixos-container start");
@ -758,12 +776,16 @@ pub fn ensure_claude_dir(claude_dir: &Path) -> Result<()> {
if !claude_dir.exists() {
std::fs::create_dir_all(claude_dir)
.with_context(|| format!("create {}", claude_dir.display()))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(claude_dir, std::fs::Permissions::from_mode(0o700))
.with_context(|| format!("chmod {}", claude_dir.display()))?;
}
}
// 0755: hive-core (different user from the agent) needs read+execute to
// list the directory so `claude_has_session` can detect a valid session.
// The credential files inside (`.credentials.json` etc.) are 0600 so the
// secrets themselves stay private regardless of the directory mode.
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
std::fs::set_permissions(claude_dir, std::fs::Permissions::from_mode(0o755))
.with_context(|| format!("chmod 755 {}", claude_dir.display()))?;
}
Ok(())
}