# Mount Tilde Expansion **Date:** 2026-05-23 **Status:** Approved ## Problem Mount paths in `.sandcage.yml` use `~` for portability (e.g. `~/.ssh:/home/agent/.ssh:ro`). Sandcage passes these strings verbatim as `-v` flags to `docker compose run` via Rust's `Command::new()`, which does not invoke a shell. Since `~` is a shell expansion feature, Docker receives the literal string `~/.ssh` and either: - **Windows:** Errors with "invalid characters for a local volume name" - **Linux/macOS:** Creates a literal `~` directory relative to CWD ## Platform Matrix | Environment | `dirs::home_dir()` | Docker path format | Notes | |---|---|---|---| | Native Windows (PowerShell/cmd) | `C:\Users\` | `C:/Users//...` | Forward slashes required | | MINGW / Git Bash | `C:\Users\` (via Rust) | `C:/Users//...` | Rust bypasses MSYS path mangling | | WSL (Linux binary) | `/home/` | `/home//...` | Native Linux paths | | Native Linux | `/home/` | `/home//...` | Straightforward | | macOS | `/Users/` | `/Users//...` | Straightforward | Key insight: Rust's `Command::new()` spawns Docker directly — no shell involved — so the environment (PowerShell, Git Bash, etc.) is irrelevant. `dirs::home_dir()` returns the correct native path on every platform. ## Design ### Approach: Expand at mount-pass-through time Config files keep the portable `~` form. Expansion happens only when building Docker CLI args. ### Implementation Add `expand_mount_path(mount: &str) -> String` in `docker.rs`: 1. Split mount on `:` to extract host path, container path, and optional mode. 2. If host path starts with `~/` or equals `~`, replace the `~` prefix with `dirs::home_dir()`. 3. On Windows (`cfg!(windows)`), normalize host path separators to forward slashes. 4. Rejoin parts with `:` and return. Call site: `build_run_args()` in `docker.rs`, replacing the current `args.push(mount.clone())` with `args.push(expand_mount_path(mount))`. ### Edge Cases | Input | Output (Linux) | Output (Windows) | |---|---|---| | `~/.ssh:/home/agent/.ssh:ro` | `/home/user/.ssh:/home/agent/.ssh:ro` | `C:/Users/user/.ssh:/home/agent/.ssh:ro` | | `~:/container` | `/home/user:/container` | `C:/Users/user:/container` | | `/absolute:/container:ro` | `/absolute:/container:ro` (unchanged) | `/absolute:/container:ro` (unchanged) | | `./relative:/container` | `./relative:/container` (unchanged) | `./relative:/container` (unchanged) | ### Not in Scope - `$HOME` / `${HOME}` expansion - `~otheruser/` expansion - Environment variable substitution in mount paths ### Testing - Unit tests for `expand_mount_path` covering all edge cases above. - Update existing `build_run_args` tests that assert literal `~/.ssh` to assert the expanded absolute path. ### Files Changed - `crates/sandcage/src/docker.rs` — add `expand_mount_path`, call it in `build_run_args` - Existing tests in `docker.rs` — update mount assertions