Skip to content

Commit 02cd0ed

Browse files
committed
Add docker container
1 parent ccedc20 commit 02cd0ed

4 files changed

Lines changed: 254 additions & 1 deletion

File tree

.github/workflows/ci.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,25 @@ jobs:
4141
| ./platter 2>/dev/null \
4242
| head -1 \
4343
| jq -e '.result.serverInfo.name == "platter"'
44+
45+
docker:
46+
name: Docker Build
47+
runs-on: ubuntu-latest
48+
steps:
49+
- uses: actions/checkout@v4
50+
- uses: docker/setup-buildx-action@v3
51+
- name: Build
52+
uses: docker/build-push-action@v6
53+
with:
54+
context: .
55+
push: false
56+
load: true
57+
tags: platter:test
58+
cache-from: type=gha
59+
cache-to: type=gha,mode=max
60+
- name: Smoke test (stdio)
61+
run: |
62+
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"ci","version":"1.0"}}}' \
63+
| docker run --rm -i platter:test 2>/dev/null \
64+
| head -1 \
65+
| jq -e '.result.serverInfo.name == "platter"'

.github/workflows/release.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66

77
permissions:
88
contents: write
9+
packages: write
910

1011
jobs:
1112
build:
@@ -49,3 +50,37 @@ jobs:
4950
with:
5051
generate_release_notes: true
5152
files: artifacts/platter-*
53+
54+
docker:
55+
name: Push Docker Image
56+
needs: build
57+
runs-on: ubuntu-latest
58+
steps:
59+
- uses: actions/checkout@v4
60+
- uses: docker/setup-buildx-action@v3
61+
- name: Docker meta
62+
id: meta
63+
uses: docker/metadata-action@v5
64+
with:
65+
images: ghcr.io/${{ github.repository }}
66+
tags: |
67+
type=semver,pattern={{version}}
68+
type=semver,pattern={{major}}.{{minor}}
69+
type=semver,pattern={{major}}
70+
type=raw,value=latest
71+
- name: Log in to GHCR
72+
uses: docker/login-action@v3
73+
with:
74+
registry: ghcr.io
75+
username: ${{ github.actor }}
76+
password: ${{ secrets.GITHUB_TOKEN }}
77+
- name: Build and push
78+
uses: docker/build-push-action@v6
79+
with:
80+
context: .
81+
platforms: linux/amd64,linux/arm64
82+
push: true
83+
tags: ${{ steps.meta.outputs.tags }}
84+
labels: ${{ steps.meta.outputs.labels }}
85+
cache-from: type=gha
86+
cache-to: type=gha,mode=max

Dockerfile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
FROM oven/bun:latest AS build
2+
WORKDIR /app
3+
COPY package.json bun.lock ./
4+
RUN bun install --frozen-lockfile
5+
COPY src/ src/
6+
COPY tsconfig.json ./
7+
RUN bun build src/index.ts --compile --outfile=platter
8+
9+
FROM debian:bookworm-slim
10+
RUN apt-get update \
11+
&& apt-get install -y --no-install-recommends ripgrep ca-certificates \
12+
&& rm -rf /var/lib/apt/lists/*
13+
COPY --from=build /app/platter /usr/local/bin/platter
14+
WORKDIR /work
15+
EXPOSE 3100
16+
ENTRYPOINT ["platter"]

README.md

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ chmod +x platter-linux-x64
2929
./platter-linux-x64 -t http # HTTP mode on :3100
3030
```
3131

32+
### Docker
33+
34+
```bash
35+
docker run --rm -i ghcr.io/scriptsmith/platter # stdio mode
36+
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 # HTTP mode
37+
```
38+
39+
See [Docker](#docker-1) below for mounting paths, networking, installing extra software, and building custom images.
40+
3241
### From source
3342

3443
```bash
@@ -190,7 +199,7 @@ The server validates the `Host` header to prevent [DNS rebinding attacks](https:
190199
- **Glob/grep search scope.** `--allow-path` validates the search directory for glob and grep, but results within that directory tree may include symlinks pointing outside it. The content of those symlink targets could be returned in grep output or listed by glob.
191200
- **No process-level sandboxing.** All restrictions are enforced in application code within the platter process. They do not use OS-level mechanisms (seccomp, namespaces, pledge, etc.). A vulnerability in platter itself, Bun, or a dependency could bypass all restrictions.
192201

193-
For high-security deployments, combine these restrictions with OS-level isolation (containers, VMs, dedicated users with limited filesystem permissions).
202+
For stronger isolation, use the just-bash sandbox, a Docker container, or both.
194203

195204
### Sandbox mode (just-bash)
196205

@@ -231,6 +240,177 @@ platter --sandbox --sandbox-allow-url "https://api.github.com" --sandbox-allow-u
231240
- **No native binaries.** Commands like `git`, `node`, `docker`, `rg`, `python` are not available. Only bash builtins and just-bash's built-in command set work.
232241
- **Beta software.** just-bash is under active development. Test your workflows before relying on it in production.
233242

243+
### Container isolation (Docker)
244+
245+
Running platter inside a Docker container provides OS-level isolation via Linux namespaces and cgroups. The container boundary limits what the bash tool can access — even unrestricted commands can only reach the filesystems and network that the container exposes.
246+
247+
```bash
248+
# Minimal: no host filesystem, no network
249+
docker run --rm -i --network none ghcr.io/scriptsmith/platter
250+
251+
# Read-only project access, no bash
252+
docker run --rm -p 3100:3100 \
253+
-v /home/user/project:/work:ro \
254+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --tools read,glob,grep
255+
256+
# Full tools, scoped to a mounted directory
257+
docker run --rm -p 3100:3100 \
258+
-v /home/user/project:/work \
259+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --allow-path /work
260+
```
261+
262+
#### What the container enforces
263+
264+
- **Filesystem boundary.** Only explicitly mounted paths (`-v`) are visible. Even with bash enabled, commands cannot read or write host paths that aren't mounted.
265+
- **Network boundary.** `--network none` completely disables networking. Without it, the container has outbound access but no access to host-only services unless `--network host` is used.
266+
- **Process isolation.** Processes inside the container cannot see or signal host processes.
267+
- **Resource limits.** Docker's `--memory`, `--cpus`, and `--pids-limit` flags can cap resource usage to prevent denial of service.
268+
269+
#### Combining sandbox and container
270+
271+
The just-bash sandbox and Docker container address different layers. Used together, they provide defense in depth:
272+
273+
| Layer | Protects against |
274+
|---|---|
275+
| **just-bash sandbox** | Arbitrary native process execution — no `git`, `curl`, `rm`, etc. Commands run in a TypeScript interpreter, not the OS shell. |
276+
| **Docker container** | Host filesystem/network access — even if the sandbox has a bug or is bypassed, the container limits blast radius to mounted paths and allowed networks. |
277+
278+
```bash
279+
# Maximum isolation: sandbox inside a container, overlay fs, no network
280+
docker run --rm -i --network none \
281+
-v /home/user/project:/work:ro \
282+
ghcr.io/scriptsmith/platter --sandbox --sandbox-fs overlay
283+
284+
# Sandbox with controlled network access inside a container
285+
docker run --rm -p 3100:3100 \
286+
-v /home/user/project:/work \
287+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 \
288+
--sandbox --sandbox-allow-url "https://api.github.com"
289+
```
290+
291+
For the highest security posture, also run the container as a non-root user (`--user`), drop all capabilities (`--cap-drop ALL`), and set the filesystem read-only (`--read-only`) with a tmpdir for any needed writes:
292+
293+
```bash
294+
docker run --rm -p 3100:3100 \
295+
--user 1000:1000 \
296+
--cap-drop ALL \
297+
--read-only --tmpfs /tmp \
298+
-v /home/user/project:/work \
299+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --sandbox
300+
```
301+
302+
See [Docker](#docker-1) for full usage instructions including mounting paths, networking, and building custom images.
303+
304+
## Docker
305+
306+
The Docker image is based on Debian Bookworm (slim) and includes ripgrep. Multi-arch images (`linux/amd64`, `linux/arm64`) are published to GitHub Container Registry on every tagged release.
307+
308+
```bash
309+
docker pull ghcr.io/scriptsmith/platter # latest release
310+
docker pull ghcr.io/scriptsmith/platter:1.0.0 # specific version
311+
```
312+
313+
### Running in stdio mode
314+
315+
Pipe JSON-RPC messages via stdin/stdout:
316+
317+
```bash
318+
docker run --rm -i ghcr.io/scriptsmith/platter
319+
```
320+
321+
### Running in HTTP mode
322+
323+
Bind to `0.0.0.0` inside the container so the port is reachable from the host:
324+
325+
```bash
326+
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter -t http --host 0.0.0.0
327+
```
328+
329+
### Mounting paths
330+
331+
Mount host directories into the container and use `--cwd` or `--allow-path` to give platter access:
332+
333+
```bash
334+
# Mount a project directory as the working directory
335+
docker run --rm -p 3100:3100 \
336+
-v /home/user/project:/work \
337+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0
338+
339+
# Mount read-only
340+
docker run --rm -p 3100:3100 \
341+
-v /home/user/project:/work:ro \
342+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --tools read,glob,grep
343+
344+
# Mount multiple directories with path restrictions
345+
docker run --rm -p 3100:3100 \
346+
-v /home/user/project:/project \
347+
-v /tmp/scratch:/scratch \
348+
ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 \
349+
--cwd /project \
350+
--allow-path /project --allow-path /scratch
351+
```
352+
353+
### Networking
354+
355+
By default containers have full outbound network access. You can restrict this with Docker's network options:
356+
357+
```bash
358+
# No network access (file-only tools)
359+
docker run --rm --network none -i ghcr.io/scriptsmith/platter
360+
361+
# Access host services (e.g. a local database)
362+
docker run --rm -p 3100:3100 --network host ghcr.io/scriptsmith/platter -t http --host 0.0.0.0
363+
```
364+
365+
### Installing additional software at runtime
366+
367+
The image uses Debian, so you can install packages with `apt-get` at runtime. This is useful for quick experiments but adds startup latency — for production use, build a custom image instead (see below).
368+
369+
```bash
370+
docker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter \
371+
bash -c "apt-get update && apt-get install -y git nodejs && exec platter -t http --host 0.0.0.0"
372+
```
373+
374+
Or interactively:
375+
376+
```bash
377+
docker run --rm -it --entrypoint bash ghcr.io/scriptsmith/platter
378+
# inside the container:
379+
apt-get update && apt-get install -y git python3
380+
platter -t http --host 0.0.0.0
381+
```
382+
383+
### Building a custom image
384+
385+
Layer additional tools on top of the platter image for a ready-to-use environment:
386+
387+
```dockerfile
388+
FROM ghcr.io/scriptsmith/platter:latest
389+
390+
RUN apt-get update \
391+
&& apt-get install -y --no-install-recommends \
392+
git \
393+
curl \
394+
python3 \
395+
nodejs \
396+
npm \
397+
&& rm -rf /var/lib/apt/lists/*
398+
```
399+
400+
Build and run:
401+
402+
```bash
403+
docker build -t my-platter .
404+
docker run --rm -p 3100:3100 -v ~/project:/work my-platter -t http --host 0.0.0.0
405+
```
406+
407+
### Building the image locally
408+
409+
```bash
410+
docker build -t platter .
411+
docker run --rm -i platter
412+
```
413+
234414
## Build
235415

236416
```bash

0 commit comments

Comments
 (0)