Skip to content

Commit c92b459

Browse files
Make session shell isolation pluggable (just-bash default, Docker container opt-in)
- Add SessionEnvironment interface (environments/types.ts) implemented by justBashEnvironment.ts (default, reuses bash.ts unmodified) and containerEnvironment.ts (opt-in, per-session Docker container). - Select the strategy via a single line in environments/index.ts, with the alternative commented out alongside its import. - Restore container-mode support files from the prior containerize-tools branch: containerFs.ts, docker/image.ts, docker/sessionContainer.ts, session-image/Dockerfile. - Make chatSocket.ts isolation-agnostic: bang-commands and disposal now go through session.environment; track active environments so they are disposed of when sessions end or fail to start. - Add getSessionDir() to the storage provider interface (disk implementation returns a real path; in-memory throws, since container mode needs a real host directory to bind-mount). - Add commented-out docker.sock mount and HOST_SESSIONS_DIR to docker-compose.yml for running the app itself in compose with container mode enabled. - Document the switch in README.md. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d3f225d commit c92b459

17 files changed

Lines changed: 883 additions & 49 deletions

README.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,25 @@ To use a purely in-memory virtual filesystem instead, set an environment variabl
3535

3636
In realistic server deployments it would be better to use disk rather than in-memory storage, because disk space is more cheaply available. But if you're building a client-side application that creates large numbers of short-lived agent sessions, it might work well to use an in-memory VFS.
3737

38+
### Switching to container-based isolation
39+
40+
By default, each session's shell tool is powered by [just-bash](https://github.com/aspect-build/just-bash), an in-process virtual filesystem and shell simulator. This is the simplest option: it needs no extra setup at all.
41+
42+
As an alternative, this sample also includes a container-based isolation strategy: each session gets its own small, disposable Linux container (built from `app/session-image/Dockerfile`), and the `bash` tool runs commands for real inside it. The session's directory on disk is bind-mounted read-write into the container, so file tools and the `bash` tool both operate on the same files.
43+
44+
Both strategies implement the same `SessionEnvironment` interface (see `app/src/api/environments/types.ts`), so switching between them is a one-line change in `app/src/api/environments/index.ts`:
45+
46+
```ts
47+
export const createEnvironment: CreateEnvironment = createJustBashEnvironment;
48+
// export const createEnvironment: CreateEnvironment = createContainerEnvironment;
49+
```
50+
51+
To switch, comment out the first line and uncomment the second. This requires:
52+
53+
- Docker installed and running on the machine that runs the app server.
54+
- Disk-backed session storage (the default) rather than the in-memory VFS mode above, since containers need a real host directory to bind-mount.
55+
- If you're also running the app server itself via `docker-compose` (rather than `npm run dev` directly on the host), uncomment the Docker socket bind mount and `HOST_SESSIONS_DIR` lines in `docker-compose.yml`, so the app can talk to the host's Docker daemon to start sibling containers with correctly-resolved bind mount paths.
56+
3857
## Architecture
3958

4059
```mermaid
@@ -52,9 +71,8 @@ When a user connects, the app server creates an isolated session with:
5271

5372
- A **Copilot SDK session** (`CopilotSession`) that maintains conversation state, tool handlers, and event streaming.
5473
- We limit it to using tools that are intended to be safe in multi-user environments because they don't read/write files.
55-
- The session's only tool that can operate on disk is `bash`, but this is swapped out for a virtual version (see below).
56-
- A **virtual filesystem** (in-memory or disk-backed) scoped to that session. The Copilot agent reads and writes files within this filesystem only. It cannot see the server's disk or the state of other sessions.
57-
- A **virtual bash runtime** ([just-bash](https://github.com/aspect-build/just-bash)) attached to the virtual filesystem, exposed to the agent as a tool. Network access is restricted to a small allowlist.
74+
- The session's only tool that can operate on disk is `bash`, but this is swapped out for an isolated version (see below).
75+
- A **session environment** (see `app/src/api/environments/`) providing an isolated filesystem and a `bash` tool. Two implementations are included - the default uses [just-bash](https://github.com/aspect-build/just-bash), an in-process virtual filesystem and shell simulator; an alternative runs each session in its own small, disposable Linux container instead. See [Switching to container-based isolation](#switching-to-container-based-isolation).
5876

5977
Multiple browser tabs can observe the same session simultaneously — the server maintains a single `CopilotSession` per session ID and fans out events to all connected WebSockets. To see this, copy and paste your session URL into a second browser window or tab.
6078

@@ -76,7 +94,8 @@ The `rsync-filestore` container is a minimal Alpine image running an rsync daemo
7694
* `api`: server-side code that defines and manages agent sessions
7795
* `chatSocket.ts`: starts a WebSocket listener. As clients connect/disconnect, starts and stops `CopilotSession` instances and synchronizes storage to `rsync-filestore`
7896
* `storage/`: simple VFS implementations
79-
* `bash.ts`: swaps out Copilot SDK's built-in shell tool with one backed by [just-bash](https://github.com/vercel-labs/just-bash). This is simply an example - you could instead map it into a per-session container, any other isolated shell. Various other open source projects provide isolated shells.
97+
* `environments/`: pluggable session isolation strategies (see [Switching to container-based isolation](#switching-to-container-based-isolation)). `justBashEnvironment.ts` is the default; `containerEnvironment.ts` is the container-based alternative. Both implement the same `SessionEnvironment` interface (`types.ts`), selected by a single line in `index.ts`.
98+
* `bash.ts`: swaps out Copilot SDK's built-in shell tool with one backed by [just-bash](https://github.com/vercel-labs/just-bash), used by the default `justBashEnvironment`.
8099
* `web-ui`: an Express+React application providing the user interface
81100
* `hooks/useChat.ts`: opens the websocket connection to `api/chat` and uses a reducer pattern to convert the event stream into a UI
82101
* everything else: generic chat UI (a lot of code but nothing interesting)

0 commit comments

Comments
 (0)