Skip to content

[ISSUE-199] Per-sandbox symlink layer for all mount sources - #200

Open
1996fanrui wants to merge 2 commits into
mainfrom
issue-199-per-sandbox-symlink-layer
Open

[ISSUE-199] Per-sandbox symlink layer for all mount sources#200
1996fanrui wants to merge 2 commits into
mainfrom
issue-199-per-sandbox-symlink-layer

Conversation

@1996fanrui

Copy link
Copy Markdown
Owner

Summary

Introduce a per-sandbox symlink directory for all mount sources (builtin tools and generic mounts). Instead of bind-mounting original host paths directly into containers, the daemon now creates a symlink in ~/.local/share/agents-sandbox/mounts/<sandbox_id>/ pointing to each original path, and uses the symlink as the bind-mount source. Docker follows symlinks during bind-mount, so this requires no Docker-side changes.

close #199

Changes

Core Implementation

  • Platform paths (internal/platform/dirs.go): Add SandboxDataRoot() following the ExecLogRoot() pattern
  • Config wiring (internal/control/service.go, cmd/agboxd/config.go): Add SandboxDataRoot field to ServiceConfig, initialize from platform default
  • Symlink helper (internal/control/docker_runtime_materialize.go): New createMountSymlink function with md5(abs_path)_<basename> naming, idempotent semantics
  • Runtime state (internal/control/docker_runtime.go): Extend sandboxRuntimeState with SymlinkDir, create symlink dir in CreateSandbox, clean up in deleteRuntimeArtifacts
  • Materialize functions: Update materializeBuiltinTools, materializeBuiltinToolPath, materializeGenericMounts to route through symlink layer
  • Validation (internal/control/service_validation.go): Allow symlink sources for mount kind (uses os.Stat), keep rejecting for copy kind (uses os.Lstat)
  • Restart recovery (internal/control/service_events.go): Rebuild SymlinkDir from SandboxDataRoot + sandboxID on restart, preserve runtimeState for all terminal states

Special Cases

  • Docker Desktop magic path (/run/host-services/ssh-auth.sock) bypasses symlink layer
  • When SandboxDataRoot is empty, all functions fall back to original behavior (no symlinks)

Documentation

  • docs/configuration_reference.md: Add Sandbox data to Fixed Platform Paths table
  • docs/daemon_state_management.md: Add SymlinkDir to Category C, symlink directory to Category D
  • docs/sandbox_container_lifecycle.md: Add symlink dir creation step in Create Path
  • docs/container_dependency_strategy.md: Update mount source rules for symlink layer

Tests

  • New docker_runtime_symlink_test.go (414 lines): symlink naming, idempotency, replacement, normalization, builtin tools, generic mounts (dir/file/socket), magic path bypass, cleanup, fallback behavior
  • New TestCreateSandboxCreatesSymlinkDir: verifies symlink dir created via Docker runtime
  • New TestRestorePersistedSandboxesSetsSymlinkDir: verifies restart recovery rebuilds SymlinkDir
  • Updated validation tests: split into mount-accepts-symlink and copy-rejects-symlink

@1996fanrui
1996fanrui force-pushed the issue-199-per-sandbox-symlink-layer branch from f7695ed to 2fd4675 Compare April 20, 2026 19:01
1996fanrui and others added 2 commits April 20, 2026 21:29
Introduce a per-sandbox staging directory under SandboxDataRoot for
all mount sources (builtin tools and generic mounts). Each mount source
gets a staging entry named {md5(abs_path)}_{basename} in a flat directory.
The current implementation uses symlinks — Docker bind-mount follows symlinks
natively, enabling per-sandbox mount source management with no Docker-side changes.

Key changes:
- Add SandboxDataRoot() platform path (~/.local/share/agents-sandbox/mounts)
- Add createMountSymlink() helper with MD5 naming and idempotent behavior
- Rename SymlinkDir to MountStagingDir in sandboxRuntimeState
- Update materializeBuiltinTools/materializeGenericMounts to stage mount sources
- Docker Desktop magic path (/run/host-services/ssh-auth.sock) bypasses staging
- Allow symlink mount sources in validation (mount only, copy still rejected)
- Set MountStagingDir in restart recovery, preserve runtimeState for cleanup
- Clean up staging directory in deleteRuntimeArtifacts

Closes: #199

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Update four design documents to reflect the new mount staging layer:
- configuration_reference.md: add SandboxDataRoot to Fixed Platform Paths
- daemon_state_management.md: add MountStagingDir to Category C, staging dir to Category D
- sandbox_container_lifecycle.md: add staging dir creation step in Create Path
- container_dependency_strategy.md: update mount/copy/builtin staging handling

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@1996fanrui
1996fanrui force-pushed the issue-199-per-sandbox-symlink-layer branch from 2fd4675 to bfb1e5a Compare April 20, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Per-sandbox mount staging layer for all mount sources

1 participant