feat: instance-level SSH key selection for container mounting

- Revert mistaken ssh_key_id from ConfigProfile (model, API, resolver, frontend)
- Add ssh_key_ids JSON column to tool_instances via migration
- Update create_instance to accept and store ssh_key_ids
- Update start_instance to mount selected SSH keys to {home_dir}/.ssh
- Update list_instances to return ssh_key_ids
- Frontend CreateSessionForm: multi-select SSH key checkboxes
- Frontend instance-list: SSH key selector for start/restart actions
- Maintain separate SSH key dirs per key to avoid conflicts

Quality gates: pytest (231 passed, 6 pre-existing), tsc --noEmit clean
This commit is contained in:
Alex Blank
2026-05-29 13:30:53 +02:00
parent cbd3436ff7
commit e9364fa70f
14 changed files with 2049 additions and 1524 deletions
@@ -51,7 +51,6 @@ class ResolvedProfile:
mounts: dict[str, ResolvedMount] = field(default_factory=dict)
git_mounts: list[dict[str, Any]] = field(default_factory=list)
files: dict[str, str] = field(default_factory=dict)
ssh_key_id: uuid.UUID | None = None
env_overrides: dict[str, str] = field(default_factory=dict)
hint_overrides: dict[str, str] = field(default_factory=dict)
file_overrides: dict[str, str] = field(default_factory=dict)
@@ -319,9 +318,6 @@ async def _resolve_profile_recursive(
result.git_mounts = _merge_git_mounts(
result.git_mounts, included.git_mounts, included.profile_name
)
# Later included profile's SSH key wins
if included.ssh_key_id is not None:
result.ssh_key_id = included.ssh_key_id
# Apply the profile's own settings (selected profile overrides includes)
result.env_vars = _merge_env_vars(
@@ -353,9 +349,6 @@ async def _resolve_profile_recursive(
profile.git_mounts or [],
profile.name,
)
# Own SSH key overrides any inherited one
if profile.ssh_key_id is not None:
result.ssh_key_id = profile.ssh_key_id
return result
@@ -571,5 +564,4 @@ def resolved_profile_to_dict(resolved: ResolvedProfile) -> dict[str, Any]:
},
"git_mounts": resolved.git_mounts,
"included_profiles": resolved.included_profiles,
"ssh_key_id": str(resolved.ssh_key_id) if resolved.ssh_key_id else None,
}