- pid: When a container stops and leaves behind a PID file with PID 1 on a shared volume, the host's init process (PID 1) passes the isProcessRunning check, blocking new gateway starts. Treat recorded PID 1 as always stale in both WritePidFile and ReadPidFileWithCheck. Added unit tests covering the PID=1 container leftover scenario. - isolation: Fix govet shadow warning on platform_windows.go line 105 where := shadows the outer err variable. Changed to = assignment. - gitattributes: Enforce LF line endings for shell scripts to prevent CRLF issues when checking out on Windows (breaks Docker entrypoint). Co-authored-by: BeaconCat <BeaconCat@users.noreply.github.com>
pkg/isolation
pkg/isolation provides process-level isolation for child processes started by picoclaw.
It does not sandbox the main picoclaw process itself.
Scope
The current scope is the child-process startup path:
exectool- CLI providers such as
claude-cliandcodex-cli - process hooks
- MCP
stdioservers
One-Sentence Model
- The
picoclawmain process still runs in the host environment. - Every child process should enter the shared
pkg/isolationstartup path first. - The startup path applies platform-specific isolation according to config.
Architecture
The implementation has four layers:
- Configuration layer: reads
config.Config.Isolationand injects it throughisolation.Configure(cfg). - Instance layout layer: resolves
config.GetHome(), prepares instance directories, and builds the runtime user environment. - Platform backend layer: Linux uses
bwrap; Windows uses a restricted token, low integrity, and aJob Object; other platforms are not implemented. - Unified startup layer:
PrepareCommand(cmd),Start(cmd), andRun(cmd).
All integrations that spawn subprocesses should reuse these helpers instead of calling cmd.Start or cmd.Run directly.
Configuration
Isolation lives under:
{
"isolation": {
"enabled": false,
"expose_paths": []
}
}
Field meanings:
enabled: enables or disables subprocess isolation. Default:false.expose_paths: explicitly exposes host paths inside the isolated environment. It only matters whenenabled=true. This is currently supported on Linux only.
Example:
{
"isolation": {
"enabled": true,
"expose_paths": [
{
"source": "/opt/toolchains/go",
"target": "/opt/toolchains/go",
"mode": "ro"
},
{
"source": "/data/shared-assets",
"target": "/opt/picoclaw-instance-a/workspace/assets",
"mode": "rw"
}
]
}
}
Rules for expose_paths:
sourceis a host path.targetis the path inside the isolated environment.modemust beroorrw.- When
targetis empty, it defaults tosource. - Only one final rule may exist for the same
target. - Later-loaded config overrides earlier rules for the same
target.
Platform note:
- Linux uses a real
source -> targetmount view. - Windows does not currently support
expose_paths.
Instance Root And Directories
The instance root follows config.GetHome():
- If
PICOCLAW_HOMEis set, use it. - Otherwise use the default
.picoclawdirectory under the user home.
If config.GetHome() falls back to . while isolation is enabled, startup should fail.
Default instance directories include:
- instance root
skillslogscachestateruntime-user-env
workspace is derived from cfg.WorkspacePath() when configured, otherwise from the default workspace rule.
Windows also prepares:
runtime-user-env/AppData/Roamingruntime-user-env/AppData/Local
User Environment Redirect
When isolation is enabled, child processes receive a redirected per-instance user environment.
Linux variables:
HOMETMPDIRXDG_CONFIG_HOMEXDG_CACHE_HOMEXDG_STATE_HOME
Windows variables:
USERPROFILEHOMETEMPTMPAPPDATALOCALAPPDATA
These paths point into runtime-user-env under the instance root.
Platform Behavior
Linux
The Linux backend currently depends on bwrap (bubblewrap).
Capabilities:
- minimal filesystem view
ipcnamespace isolation- redirected child-process user environment
source -> targetread-only or read-write mounts
Default mounts include the instance root plus the minimum runtime system paths such as /usr, /bin, /lib, /lib64, and /etc/resolv.conf.
At runtime, PicoClaw also adds the executable path, its directory, the effective working directory, and absolute path arguments when needed.
There is no automatic fallback when bwrap is missing.
Install examples:
apt install bubblewrapdnf install bubblewrapyum install bubblewrappacman -S bubblewrapapk add bubblewrap
If isolation must be disabled temporarily:
{
"isolation": {
"enabled": false
}
}
Disabling isolation increases the risk that child processes can access or modify more host files.
Windows
Windows isolation currently supports process-level restrictions such as restricted tokens, low integrity, job objects, and redirected user-environment directories.
expose_paths is not currently supported on Windows. If it is configured, startup should fail instead of pretending the paths were exposed.
The Windows backend currently uses:
- a restricted primary token
- low integrity level
- a
Job Object - redirected child-process user environment
It does not currently implement true source -> target filesystem remapping.
macOS And Other Platforms
They are not implemented yet.
When isolation is explicitly enabled on an unsupported platform, the higher-level runtime should surface that as an unsupported configuration instead of pretending isolation succeeded.
Logging And Debugging
When isolation is enabled, PicoClaw logs the generated isolation plan.
Linux log name:
linux isolation mount plan
Windows log name:
windows isolation access rules
If you suspect isolation is ineffective, check whether unexpected host paths appear in those logs.
Relationship To restrict_to_workspace
restrict_to_workspacelimits the paths an agent is normally allowed to access.pkg/isolationlimits what a child process can see and where its user environment points.
They complement each other and do not replace each other.
Current Limits
- Linux isolation is implemented with
bwrap, not a custom in-process isolation runtime. - Linux does not currently enable a dedicated
pidnamespace by default. - Windows does not yet implement full host ACL enforcement for every allowed or denied path.
- macOS is not implemented.
- The current design isolates child processes, not the main
picoclawprocess.
Suggested Reading Order
If you are new to this code, read it in this order:
pkg/config/config.gopkg/isolation/runtime.gopkg/isolation/platform_linux.gopkg/isolation/platform_windows.go- Call sites:
pkg/tools/shell.gopkg/providers/*.gopkg/agent/hook_process.gopkg/mcp/manager.go
That path gives the fastest overview of the configuration model, runtime flow, and platform-specific limits.