Files
sandcage/docs/superpowers/specs/2026-05-23-mount-tilde-expansion-design.md
T
2026-05-23 17:11:31 +02:00

2.9 KiB

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\<user> C:/Users/<user>/... Forward slashes required
MINGW / Git Bash C:\Users\<user> (via Rust) C:/Users/<user>/... Rust bypasses MSYS path mangling
WSL (Linux binary) /home/<user> /home/<user>/... Native Linux paths
Native Linux /home/<user> /home/<user>/... Straightforward
macOS /Users/<user> /Users/<user>/... 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