Give your coding agent a room of its own—not the keys to the house.
Cohotfs creates reproducible container workspaces for agentic AI. Your project is mounted where the agent can work on it, while access to host toolchains, browsers, credentials, and agent configuration is granted one integration at a time.
Think of it as a furnished sublet for code: the workspace gets a writable desk, borrowed tools come with receipts, and the valuables stay at home.
Important
Cohotfs is currently Docker-first and Linux-first. The implemented runtime is a local Docker Engine over a Unix socket, the workspace platform is linux/amd64, and the host application runs on Linux or WSL2. Native containerd and CRI/CRI-O adapters are planned, not currently shipped.
An isolated container is useful until an agent needs to compile with your local
toolchain, open Chrome, sign a Git operation, or reuse a small piece of its normal
configuration. The tempting fix is to mount $HOME, the Docker socket, browser
profiles, or credential files into the container. That works right up to the point
where it works much too well.
Cohotfs keeps the host as the control plane:
- host integration is off by default;
- the container never receives a Docker/runtime socket;
- host SSH private keys, browser profiles, and agent authentication databases are not mounted directly; an OMP credential database requires an explicit private snapshot grant;
- every host-side Cohotfs file and socket lives below a user-owned
~/.cohotfs; - requested capabilities are checked before resources are created;
- interrupted operations are journaled and recoverable; and
isolation: gvisorfails closed when the configured runtime is unavailable. It never quietly becomes an ordinary container.
The project directory is intentionally writable. Cohotfs protects the host around the workspace; it does not pretend that an agent cannot edit the source tree you asked it to edit.
For each operation, the host-side cohotfs process resolves strict configuration,
probes the selected runtime, and compiles a complete redacted workspace plan before
making changes. It then:
- resolves a compatible Cohotfs base image by immutable digest;
- creates a non-privileged container with the requested source, limits, and explicitly granted integrations;
- starts
cohotfs-agentas PID 1 and container root; - creates an
agentaccount whose numeric UID/GID match the invoking host user; - generates a per-workspace SSH host key and starts a restrictive
sshdon container loopback; - pins that host key under a workspace-specific alias; and
- enters the workspace using the host OpenSSH client.
SSH stays end-to-end between the host OpenSSH client and the workspace sshd.
Cohotfs is only the byte-clean ProxyCommand transport:
host ssh
│
▼
cohotfs ssh-proxy
│ validates workspace state, runtime nonce, socket owner/type/mode/inode
▼
~/.cohotfs/run/workspaces/<id>/ssh/ssh.sock
│
▼
cohotfs-agent relay ──► sshd on 127.0.0.1:2222
Optional integrations use short-lived, identity-checked leases from the per-user
cohotfs host serve process. Secrets move only when an authorized operation asks
for them; they are not copied into images or durable workspace state.
| Area | Support |
|---|---|
| Host | Linux and WSL2 |
| Runtime | Local Docker Engine through a pathname Unix socket |
| Workspace platform | linux/amd64 |
| Isolation | Standard OCI isolation; optional fail-closed Docker gVisor runtime |
| Images | Pull/resolve image.ref by digest, explicit BuildKit-backed cohotfs image build, local-only pullPolicy: never, Cohotfs base compatibility probe |
| Lifecycle | Create, start, stop, restart, inspect, list, remove, recover, and rotate SSH host keys |
| Interactive access | Host OpenSSH shell, command execution, and loopback-only local port forwarding through an identity-checked Unix socket |
| Setup | once, always, and manual repository setup; mapped host UID/GID; explicit retry/force; bounded output and timeout handling |
| Host toolchains | Native Linux Go/Rust discovery; read-only toolchain roots; COW or isolated managed caches |
| Browser | Fresh-profile Linux Chrome CDP; native Windows Chrome from WSL through the companion bridge |
| Credentials | Explicit SSH-agent forwarding and allowlisted, read-only Git HTTPS credential brokering |
| AI agents | Allowlisted non-secret settings for OMP, Codex, and Claude; one-use environment-secret leases |
| Operations | Durable state, idempotent mutation journals, identity-checked cleanup, quarantine, and recovery |
| Machine output | Stable JSON output for discovery, status, and list commands |
- No remote Docker endpoint over TCP, TLS, or SSH.
- No native containerd or CRI/CRI-O backend yet.
- No silent runtime-exec or routable TCP fallback for workspace SSH.
- No runtime socket, host home,
.sshdirectory, private key, normal browser profile, or directly shared agent credential database mounted into a workspace. - No arbitrary prebuilt image. The final image must retain the matching Cohotfs bootstrap contract and its OpenSSH runtime dependencies.
- Workspace creation does not build
spec.image.buildinline yet. Runcohotfs image build, then use its local tag asimage.refwithpullPolicy: never. - No generic runtime annotation/option escape hatch.
Docker socket access is itself highly privileged—root-equivalent for a typical rootful daemon. Cohotfs uses the invoking user's existing access and never changes socket ownership or group membership. Prefer rootless Docker where it fits.
- Linux or WSL2 on an
amd64host - a local Docker Engine reachable by the current user through a Unix socket
- the Docker CLI for source image builds
- the host OpenSSH client and
ssh-keygen - Go 1.26.5 when building from source
From the Cohotfs repository root, build the host binary and the matching local workspace image:
install -d "$HOME/.local/bin"
go build -o "$HOME/.local/bin/cohotfs" ./cmd/cohotfs
export PATH="$HOME/.local/bin:$PATH"
./images/workspace-base/build-local-image.shThe helper builds the static in-container agent, creates a temporary Docker build
context, and tags the result as
ghcr.io/gosuda/cohotfs/workspace-base:dev. A dev host binary writes that
exact reference with pullPolicy: never, so workspace creation resolves the
already-loaded Docker image without contacting GHCR. Pass one optional image tag
to the helper only when you set the same spec.image.ref and retain
pullPolicy: never. Release manifests default to pullPolicy: always.
Prepare the per-user state and inspect the machine:
cohotfs onboard
cohotfs doctoronboard records each unambiguous native toolchain's canonical root in the host
configuration and grants that exact path through permittedRoots. Rerun it after
moving or replacing a toolchain. An enabled host toolchain whose root is not
granted fails before container creation instead of leaving its command unavailable.
Initialize trusted policy for the current project:
cd ~/src/my-project
cohotfs init
cohotfs config project show
cohotfs config project editinit writes the complete project policy only to
~/.cohotfs/projects/<source-hash>/workspace.yaml; it does not create or modify
repository files. The terminal editor handles integration toggles. Use
cohotfs config project edit --edit with $VISUAL or $EDITOR to edit the full
strict YAML document, including setup argv and resource limits. The generated
policy uses manual no-op setup. If setup should run repository code, put it at a
path such as scripts/cohotfs-setup.sh and explicitly set
spec.setup.command; existing repository .cohotfs and .omp directories are
masked inside the workspace and cannot host setup scripts. Absent reserved paths
do not receive nested mounts, so normal workspace creation does not create them.
For existing paths, Cohotfs persists the directory identity and validates it
before and after runtime creation and start; replacement fails closed and cleans
up the runtime. All Cohotfs and imported OMP state is stored under the user's
home directory.
Bootstrap PID 1 remains container root, but setup commands, SSH shells, and
cohotfs exec run with the host-mapped agent UID/GID. Setup is still trusted
repository code: it can modify the writable project mount by design.
When host toolchains are enabled, setup inherits their validated managed paths
and caches; unrelated container or agent-secret variables remain excluded.
Then enter:
cohotfsThe bare command fixes the current directory at /workspace, creates or reuses
its workspace, starts it when needed, and opens an interactive OpenSSH session.
It refuses to mount your home directory by default. The exceptional
cohotfs --allow-home path still masks ~/.cohotfs inside the workspace.
Useful current-directory commands:
cohotfs workspace create
cohotfs workspace list
cohotfs workspace status
cohotfs shell
cohotfs exec -- go test ./...
cohotfs port-forward 3000
cohotfs setup run
cohotfs workspace stop
cohotfs workspace recover # preview identity-matched cleanup
cohotfs workspace recover --yes # apply it
cohotfs workspace remove --yesWorkspace-targeting commands select the workspace whose canonical source is the
current directory. Use --workspace <name-or-id> to target another workspace,
for example cohotfs shell --workspace api or
cohotfs workspace stop --workspace api.
port-forward listens on host 127.0.0.1 and forwards to the same
container-loopback port through the authenticated workspace SSH transport. Keep
the command running while using http://127.0.0.1:3000; stop it with Ctrl-C.
Use --local-port 8080 to map host port 8080 to container port 3000.
--host 0.0.0.0 explicitly exposes the host port on every IPv4 interface; use
it only when other machines must connect. The container application may stay
bound to 127.0.0.1; no Docker port is published. Workspaces created with an
older bootstrap API are rejected with a remove/recreate error instead of
attempting an unsupported forward.
Inspect runtime negotiation without guessing:
cohotfs runtime list --output json
cohotfs runtime capabilities --output json
cohotfs config show
cohotfs config project showGlobal host capabilities and defaults live in ~/.cohotfs/config.yaml. The
complete trusted policy for a project lives in
~/.cohotfs/projects/<source-hash>/workspace.yaml, bound to that project's
canonical source identity. Repository files never participate in policy
resolution, so a checkout cannot grant itself host integrations.
An initialized project's validated document is authoritative. In an
uninitialized directory, Cohotfs resolves host defaults over built-ins. Host
runtime endpoints, permitted external roots, browser executables, toolchain
sources, and credential environment mappings remain machine-local in
~/.cohotfs/config.yaml.
Both documents are strict YAML: unknown keys are errors. Run cohotfs init to
write the complete project schema, then use cohotfs config project edit to
enable only the integrations that project needs.
OMP is opt-in. When enabled, its selected binary, native modules, model catalog,
and non-secret configuration are cloned into workspace-owned writable snapshots;
container writes cannot modify the host copies. Set requireCow: true to require
reflink support instead of allowing a private copy-once fallback. Setting
agents.omp.import.oauthDB: true clones the complete OMP agent directory,
including its credential, session, history, configuration, and cache state, into
a private writable COW snapshot mounted at PI_CODING_AGENT_DIR. Host and
workspace changes do not sync. Stop OMP before workspace creation when a
point-in-time-consistent SQLite snapshot is required.
All host-side state stays under:
~/.cohotfs/
├── config.yaml
├── projects/ # source-bound trusted project policies
├── state/ # workspaces, plans, and operation journals
├── workspaces/ # private homes, system state, and toolchain views
├── ssh/ # Cohotfs client key and pinned workspace host keys
├── browser/ # isolated Chrome profiles
├── run/ # identity-checked sockets, leases, and locks
├── logs/ # redacted audit/setup logs
└── tmp/
make build
make test
make vet
make windows-bridge
./images/workspace-base/build-local-image.shThe live Docker suite creates real containers and images, exercises SSH/SCP/SFTP, setup modes, resource policies, host-key rejection, concurrent mutation, and cleanup:
go test -tags=integration ./integration/docker \
-run TestWorkspaceEndToEnd -v -count=1 -timeout=20mMIT. See LICENSE.