diff --git a/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py b/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py index b4ec709..3bcae63 100644 --- a/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py +++ b/apps/api/alembic/versions/2026_05_28_add_tool_definition_manifests.py @@ -232,14 +232,6 @@ def upgrade() -> None: "writable": True, "owner": "user", }, - { - "name": "ssh_keys", - "target": "/home/user/.ssh", - "source_type": "ssh_key", - "mode": "0700", - "file_mode": "0600", - "readonly": True, - }, { "name": "pi_state", "target": "/tmp/.pi/agents", diff --git a/apps/api/src/api/tool_instances.py b/apps/api/src/api/tool_instances.py index 0dc0874..05e9d54 100644 --- a/apps/api/src/api/tool_instances.py +++ b/apps/api/src/api/tool_instances.py @@ -1347,7 +1347,9 @@ async def start_instance( ToolDefinitionManifest, manifest_def.base_definition_id ) if base_def: - manifest = resolve_base(deep_merge(dict(base_def.manifest), manifest)) + manifest = resolve_base( + deep_merge(dict(base_def.manifest), manifest) + ) home_dir = get_manifest_home_dir(manifest) user_cfg = manifest.get("user") if user_cfg: diff --git a/apps/api/src/services/permission_fixer.py b/apps/api/src/services/permission_fixer.py index f3da86b..85afc3b 100644 --- a/apps/api/src/services/permission_fixer.py +++ b/apps/api/src/services/permission_fixer.py @@ -40,6 +40,17 @@ def apply_mount_permissions( "error": None, } + # Skip read-only mounts — their permissions cannot be changed + # post-start because the bind mount is locked. + if mount.get("readonly", False): + logger.debug( + "Skipping permission fix for read-only mount %s (target=%s)", + name, + target, + ) + results.append(result) + continue + # Skip if no permission policy defined if not owner and not mode and not file_mode: results.append(result) diff --git a/apps/api/tests/unit/test_permission_fixer.py b/apps/api/tests/unit/test_permission_fixer.py index 899cf8e..6bc672c 100644 --- a/apps/api/tests/unit/test_permission_fixer.py +++ b/apps/api/tests/unit/test_permission_fixer.py @@ -64,6 +64,24 @@ class TestApplyMountPermissions: "find /home/user/.ssh -type f -exec chmod 0600" in file_mode_call[0][1][2] ) + @patch("src.services.permission_fixer._run_in_container") + def test_skips_readonly_mount(self, mock_run) -> None: + mounts = [ + { + "name": "ssh_keys", + "target": "/home/user/.ssh", + "readonly": True, + "mode": "0700", + "file_mode": "0600", + }, + ] + results = apply_mount_permissions("abc123", mounts) + + assert len(results) == 1 + assert results[0]["mount_name"] == "ssh_keys" + assert results[0]["success"] is True + mock_run.assert_not_called() + @patch("src.services.permission_fixer._run_in_container") def test_skips_mount_with_no_policy(self, mock_run) -> None: mounts = [