{"id":48554561,"url":"https://github.com/scriptsmith/platter","last_synced_at":"2026-04-19T12:02:59.089Z","repository":{"id":346795428,"uuid":"1190688125","full_name":"ScriptSmith/platter","owner":"ScriptSmith","description":"Your computer served on a platter. Read, Write, Edit, Bash, Glob, and Grep as an MCP server.","archived":false,"fork":false,"pushed_at":"2026-04-17T02:06:39.000Z","size":262,"stargazers_count":0,"open_issues_count":1,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-17T03:35:13.254Z","etag":null,"topics":["ai","ai-agents","just-bash","mcp","mcp-server"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ScriptSmith.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-24T14:22:48.000Z","updated_at":"2026-04-17T02:06:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ScriptSmith/platter","commit_stats":null,"previous_names":["scriptsmith/platter"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/ScriptSmith/platter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScriptSmith%2Fplatter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScriptSmith%2Fplatter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScriptSmith%2Fplatter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScriptSmith%2Fplatter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ScriptSmith","download_url":"https://codeload.github.com/ScriptSmith/platter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ScriptSmith%2Fplatter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32005833,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","ai-agents","just-bash","mcp","mcp-server"],"created_at":"2026-04-08T10:02:01.167Z","updated_at":"2026-04-19T12:02:59.071Z","avatar_url":"https://github.com/ScriptSmith.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"linux/platter.svg\" width=\"128\" height=\"128\" alt=\"platter icon\"\u003e\n\u003c/p\u003e\n\n# platter\n\n*Your computer, served on a platter.*\n\nMCP server that exposes **Read**, **Write**, **Edit**, **Bash**, **Glob**, **Grep**, and **JS** tools over Stdio and StreamableHTTP transports. Built with [Bun](https://bun.sh), compiles to standalone executables.\n\nDesigned to be used by browser-based (or any MCP-compatible) agents, like [Hadrian](https://github.com/ScriptSmith/hadrian), to control a computer.\n\n## Tools\n\n| Tool | Description |\n|------|-------------|\n| **read** | Read file contents with pagination (offset/limit). Detects image files (JPEG, PNG, GIF, WebP) and returns metadata. Truncates text to 2000 lines or 50KB. |\n| **write** | Create or overwrite files. Auto-creates parent directories. |\n| **edit** | Find-and-replace with exact or fuzzy matching (normalizes smart quotes, dashes, and Unicode whitespace). Requires a unique match, or use `replace_all` for every occurrence (exact matches only). Returns a unified diff. |\n| **bash** | Execute shell commands with optional timeout. Output truncated to last 2000 lines or 50KB. |\n| **glob** | Fast file pattern matching. Returns up to 500 paths matching a glob pattern (e.g. `**/*.ts`). |\n| **grep** | Search file contents using [ripgrep](https://github.com/BurntSushi/ripgrep). Supports regex, file filtering, context lines, and multiple output modes. Requires `rg` to be installed. |\n| **js** | Evaluate JavaScript/TypeScript in a persistent Node.js `vm` context. State persists across calls within a session. Supports `await`, `console.log`, and loading packages from unpkg.com via `await load(\"package\")`. Auto-returns the last expression. **Not a security sandbox** — see [Security](#security) below. |\n\n## Quick start\n\n### From a release binary\n\nDownload the latest binary for your platform from [Releases](https://github.com/ScriptSmith/platter/releases), or grab it with `curl`:\n\n```bash\n# Download (replace the filename for your platform)\n# Available: platter-linux-x64, platter-linux-arm64, platter-darwin-x64, platter-darwin-arm64\ncurl -fsSL https://github.com/ScriptSmith/platter/releases/latest/download/platter-linux-x64 -o platter\nchmod +x platter\n\n./platter          # stdio mode\n./platter -t http  # HTTP mode on :3100\n```\n\n### Docker\n\n```bash\ndocker run --rm -i ghcr.io/scriptsmith/platter                                   # stdio mode\ndocker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter -t http --host 0.0.0.0  # HTTP mode\n```\n\nSee [Docker](#docker-1) below for mounting paths, networking, installing extra software, and building custom images.\n\n### From source\n\n```bash\nbun install\nbun run dev      # run directly from TypeScript\nbun run compile  # build standalone binary for current platform\n```\n\n## Usage\n\n```\nplatter v1.x.x\n\nYour computer, served on a platter.\n\nUsage: platter [options]\n\nOptions:\n  -t, --transport \u003cstdio|http\u003e   Transport mode (default: stdio)\n      --tray                     Run the HTTP server with a Linux system tray\n                                 (implies --transport=http, persists state\n                                 across restarts in ~/.config/platter)\n  -p, --port \u003cnumber\u003e            HTTP port (default: 3100)\n      --host \u003caddress\u003e           HTTP bind address (default: 127.0.0.1)\n      --cwd \u003cpath\u003e               Working directory for tools (default: current directory)\n      --cors-origin \u003corigin\u003e     Allowed CORS origin (default: *)\n      --auth \u003cmode\u003e              Auth mode: oauth, bearer, none (default: oauth)\n      --auth-token \u003ctoken\u003e       Bearer token for HTTP auth (auto-generated if omitted)\n      --tls-cert \u003cpath\u003e          TLS certificate file (PEM) — enables HTTPS\n      --tls-key \u003cpath\u003e           TLS private key file (PEM)\n\nProcess management:\n      --max-processes \u003cnumber\u003e   Max concurrent bash processes per session (default: 20)\n      --max-sessions \u003cnumber\u003e    Max concurrent HTTP sessions (default: unlimited)\n\nRestrictions:\n      --tools \u003clist\u003e             Comma-separated tools to enable (default: all)\n                                 Valid: read, write, edit, bash, glob, grep, js\n      --allow-path \u003cpath\u003e        Restrict read/write/edit/glob/grep to this path (repeatable)\n                                 Does not restrict bash or js\n      --allow-command \u003cregex\u003e    Allow bash commands matching this pattern (repeatable)\n                                 Pattern must match the entire command string\n                                 Applies to bash only; does not restrict js\n\nSandbox (applies to bash only; the js tool is never sandboxed):\n      --sandbox                  Use just-bash sandbox instead of native bash\n      --sandbox-fs \u003cmode\u003e        Filesystem backend: memory, overlay, readwrite (default: readwrite)\n      --sandbox-allow-url \u003curl\u003e  Allow network access to URL prefix (repeatable)\n\n  -h, --help                     Show this help message\n  -v, --version                  Show version number\n```\n\n### Restrictions\n\nYou can limit which tools are registered, which filesystem paths file tools can access, and which commands the bash tool can execute.\n\n#### Tool selection\n\nOnly register specific tools. Unregistered tools are completely hidden from MCP clients:\n\n```bash\nplatter --tools read,glob,grep    # read-only server\nplatter --tools read,write,edit   # no bash/search/js\n```\n\n#### Path restrictions\n\nRestrict file-accessing tools (read, write, edit, glob, grep) to one or more directory trees. Paths are resolved to absolute form and symlinks are resolved via `realpath` to prevent escaping:\n\n```bash\nplatter --allow-path /home/user/project\nplatter --allow-path /home/user/project --allow-path /tmp\n```\n\n#### Command restrictions\n\nOnly allow bash commands whose **entire** command string matches at least one regex pattern:\n\n```bash\nplatter --allow-command \"git( .*)?\"                             # git only\nplatter --allow-command \"git( .*)?\" --allow-command \"npm( .*)?\" # git or npm\nplatter --allow-command \"ls( .*)?\" --allow-command \"cat .*\"     # ls or cat\n```\n\nPatterns are anchored: `--allow-command \"git( .*)?\"` compiles to `^(?:git( .*)?)$`, so `git status` matches but `rm -rf / \u0026\u0026 git status` does not.\n\n#### Combined example\n\n```bash\n# Locked-down: read-only tools, scoped to one directory\nplatter --tools read,glob,grep --allow-path /home/user/project\n\n# Full tools, but bash restricted to git/npm, files restricted to project\nplatter --allow-path ./my-project --allow-command \"git( .*)?\" --allow-command \"npm( .*)?\"\n```\n\nActive restrictions are logged to stderr at startup.\n\n### Authentication\n\nControlled by `--auth \u003cmode\u003e`:\n\n| Mode | Description |\n|------|-------------|\n| `oauth` (default) | OAuth 2.1 Authorization Code + PKCE, with a static bearer token as fallback |\n| `bearer` | Static bearer token only |\n| `none` | No authentication |\n\n#### OAuth 2.1 (`--auth oauth`)\n\nMCP clients that support [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728) (Protected Resource Metadata) can discover platter's OAuth endpoints automatically and authenticate without manual token copying.\n\nThe flow:\n\n1. Client discovers `/.well-known/oauth-authorization-server` and `/.well-known/oauth-protected-resource/mcp`\n2. Client registers via `POST /register` ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591) dynamic client registration)\n3. Client initiates Authorization Code + PKCE flow via `/authorize`\n4. A **confirmation code** is displayed out-of-band (printed to stderr, or shown as a desktop notification in tray mode)\n5. User sees a consent page, enters the confirmation code, selects which tools to grant, and approves or denies the request\n6. Client exchanges the authorization code for tokens at `/token`\n7. Subsequent requests use `Authorization: Bearer \u003caccess_token\u003e`\n\nThe confirmation code proves that the person approving the request has access to the platter process — a remote attacker who can reach the consent page cannot approve without it. Codes are single-use and expire after 5 minutes, with a maximum of 5 attempts.\n\nAccess tokens expire after 1 hour and can be refreshed. Client registrations are persisted to `~/.config/platter/clients.json`. A static bearer token is also accepted as a fallback for clients that don't support OAuth.\n\n#### Bearer token (`--auth bearer`)\n\nA random bearer token is generated at startup and printed to stderr (or stored in the system keyring in tray mode). Every request must include `Authorization: Bearer \u003ctoken\u003e`. You can provide your own:\n\n```bash\nplatter -t http --auth bearer --auth-token my-secret-token\n```\n\n#### No authentication (`--auth none`)\n\nDisable authentication entirely (e.g. behind a reverse proxy that handles auth):\n\n```bash\nplatter -t http --auth none\n```\n\n### TLS (HTTPS)\n\nTo serve over HTTPS, provide a PEM-encoded certificate and private key:\n\n```bash\nplatter -t http --tls-cert cert.pem --tls-key key.pem\n```\n\nBoth `--tls-cert` and `--tls-key` are required together. When provided, the server listens over HTTPS instead of plain HTTP.\n\nTo generate a self-signed certificate for development or trusted internal use:\n\n```bash\nopenssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj '/CN=myserver'\nplatter -t http --tls-cert cert.pem --tls-key key.pem\n```\n\n### Stdio mode\n\nFor use with Claude Desktop, Cursor, and other MCP clients that spawn a subprocess:\n\n```json\n{\n  \"mcpServers\": {\n    \"platter\": {\n      \"command\": \"/path/to/platter\"\n    }\n  }\n}\n```\n\n### HTTP mode (StreamableHTTP)\n\nFor browser-based agents and remote connections:\n\n```bash\nplatter -t http -p 3100\n```\n\nThe server exposes a single endpoint at `/mcp` that handles:\n- `POST /mcp` - JSON-RPC messages (initialize, tool calls)\n- `GET /mcp` - SSE notification stream\n- `DELETE /mcp` - session teardown\n\nCORS is enabled for all origins by default (reflects the request `Origin`). To restrict to a specific origin:\n\n```bash\nplatter -t http --cors-origin https://myapp.example.com\n```\n\nSessions are managed via the `Mcp-Session-Id` header per the StreamableHTTP spec.\n\nThe server validates the `Host` header to prevent [DNS rebinding attacks](https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-w48q-cv73-mx4w). When `--cors-origin` is set, the `Origin` header is also validated server-side (not just via CORS response headers).\n\n### Tray mode (Linux)\n\nRun platter as a persistent background service with a system tray icon:\n\n```bash\nplatter --tray\n```\n\nThis implies `--transport=http` and adds:\n\n- **System tray icon** via DBus (StatusNotifierItem protocol), compatible with KDE, GNOME (with AppIndicator extension), and other desktop environments.\n- **Persistent configuration** in `~/.config/platter/config.json` — auth token, enabled tools, port, host, and working directory survive restarts.\n- **Dynamic tool toggling** — enable or disable individual tools at runtime from the tray menu. Changes are persisted and take effect immediately.\n- **Auth token management** — copy the server URL or auth token to clipboard, or regenerate the token. Tokens are stored in the system keyring when available, falling back to the config file.\n- **Server controls** — start, stop, and restart the HTTP server from the tray menu.\n\n#### Linux installer\n\nThe `linux/install.sh` script performs a user-level install (no `sudo` required):\n\n```bash\n./linux/install.sh                  # install\n./linux/install.sh --uninstall      # remove\n```\n\nThis installs:\n- `~/.local/bin/platter` — the binary\n- `~/.local/share/applications/platter.desktop` — desktop launcher entry\n- `~/.local/share/icons/hicolor/scalable/apps/platter.svg` — application icon\n- `~/.config/systemd/user/platter.service` — systemd user unit for autostart\n\n## Security\n\n### Network (HTTP mode)\n\n- **TLS (HTTPS)** - optional transport encryption via `--tls-cert` and `--tls-key`. Uses Node.js `https` module with PEM-encoded certificate and key files.\n- **OAuth 2.1 + PKCE** (`--auth oauth`, default) - MCP clients authenticate via Authorization Code flow with PKCE ([RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)). Supports dynamic client registration ([RFC 7591](https://datatracker.ietf.org/doc/html/rfc7591)) and token revocation ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)).\n- **Out-of-band consent confirmation** - the OAuth consent page requires a confirmation code that is only displayed to the platter operator (via stderr or desktop notification). This prevents CSRF and cross-origin attacks from auto-approving authorization requests. The consent page is also protected with `X-Frame-Options: DENY`, `Sec-Fetch-Site` validation, and `Origin` header checking.\n- **Bearer token authentication** - a static bearer token (RFC 6750) is available as a fallback in `oauth` mode and as the sole method in `bearer` mode. A random 256-bit token is generated at startup unless you provide `--auth-token` or set `--auth none`.\n- **Host header validation** - prevents [DNS rebinding attacks](https://github.com/modelcontextprotocol/typescript-sdk/security/advisories/GHSA-w48q-cv73-mx4w). Localhost binds accept only `127.0.0.1`, `localhost`, and `::1`; remote binds accept only the specified `--host`.\n- **Origin validation** - when `--cors-origin` is set to a specific origin, requests with a mismatched `Origin` header are actively rejected with 403 (not just filtered by CORS response headers).\n\n### Restrictions (best-effort)\n\n`--tools`, `--allow-path`, and `--allow-command` are **defense-in-depth** controls. They raise the bar significantly but are not a sandbox. The limitations below should be understood before relying on them in a threat model.\n\n#### What they do well\n\n- **Tool selection** is enforced at registration time. Disabled tools are never exposed via the MCP protocol. There is no way for a client to invoke or discover them.\n- **Path validation** resolves symlinks via `realpath()` on both the target and each allowed path before comparison, preventing traversal via `../` or symlinked directories. For write targets that don't exist yet, the nearest existing ancestor is resolved instead.\n- **Command validation** anchors regex patterns to match the full command string, preventing trivial bypasses like appending `\u0026\u0026 malicious-command`.\n\n#### Known limitations and bypasses\n\n- **Bash is inherently unrestricted.** When the bash tool is enabled, a sufficiently creative command can bypass `--allow-path` entirely (e.g. `cat /etc/passwd`). If you set `--allow-path` without also setting `--allow-command` or removing bash from `--tools`, a warning is printed at startup. For strong file-access control, either disable bash (`--tools read,write,edit,glob,grep`) or pair `--allow-path` with a tight `--allow-command` allowlist.\n- **The `js` tool is not a security sandbox.** It runs code in a Node.js `vm` context, which [Node's own documentation](https://nodejs.org/api/vm.html#vm-executing-javascript) explicitly states is *not* a security mechanism — untrusted code can escape the context via known techniques. Even without an escape, the runtime exposes `fetch` (arbitrary network access, including private/loopback addresses), `Buffer`, and a `load()` helper that downloads and executes arbitrary code from `unpkg.com` or any other URL. None of `--allow-path`, `--allow-command`, `--sandbox`, or `--sandbox-allow-url` apply to the `js` tool — these flags only affect the file and bash tools. If the `js` tool is enabled, treat the server as having roughly the same blast radius as unrestricted bash. To disable it: `--tools read,write,edit,bash,glob,grep`. For strong isolation, run platter inside a container or VM.\n- **Command regex operates on the raw string.** It does not parse shell syntax. Patterns like `--allow-command \"git( .*)?\"` block `rm \u0026\u0026 git status` (because the full string doesn't match), but a determined attacker could construct commands that the regex matches yet that execute unintended code, for example if an allowed pattern is too broad. Write patterns as narrowly as possible.\n- **Symlink TOCTOU.** Path validation resolves symlinks at check time. If a symlink target is changed between the check and the actual file operation, the validation can be bypassed. This is a fundamental limitation of userspace path checking.\n- **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.\n- **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.\n\nFor stronger isolation, use the just-bash sandbox, a Docker container, or both.\n\n### Sandbox mode (just-bash)\n\nOpt into [just-bash](https://github.com/vercel-labs/just-bash), a TypeScript reimplementation of bash with a virtual filesystem, for sandboxed command execution. No native processes are spawned; the shell runs entirely in the Bun runtime.\n\n```bash\nplatter --sandbox                                               # readwrite fs, no network\nplatter --sandbox --sandbox-fs memory                           # pure in-memory fs\nplatter --sandbox --sandbox-fs overlay                          # reads from disk, writes ephemeral\nplatter --sandbox --sandbox-allow-url \"https://api.example.com\" # allow network to prefix\n```\n\n#### Filesystem modes\n\n| Mode | Reads | Writes | Use case |\n|---|---|---|---|\n| `memory` | Virtual only | Virtual only | Maximum isolation, no disk access at all |\n| `readwrite` (default) | Real disk | Real disk | Sandboxed execution with real file access |\n| `overlay` | Real disk | In-memory (ephemeral) | Explore files without risk of modification |\n\n#### Network access\n\nNetwork access is **disabled by default**. Use `--sandbox-allow-url` (repeatable) to allow access to specific URL prefixes. Private/loopback IPs are always denied.\n\n```bash\nplatter --sandbox --sandbox-allow-url \"https://api.github.com\" --sandbox-allow-url \"https://registry.npmjs.org\"\n```\n\n#### Interaction with other restrictions\n\n- **`--allow-path`**: In `readwrite` and `overlay` modes, each allowed path is mounted into the sandbox. In `memory` mode, `--allow-path` is ignored.\n- **`--allow-command`**: Command regex validation still applies before the sandbox executes the command.\n- **`--sandbox` suppresses the bash + `--allow-path` warning**, since the sandbox enforces filesystem boundaries.\n\n#### Limitations\n\n- **Not full bash.** just-bash is a TypeScript reimplementation; some edge cases may behave differently from GNU bash.\n- **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.\n- **Beta software.** just-bash is under active development. Test your workflows before relying on it in production.\n\n### Container isolation (Docker)\n\nRunning 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.\n\n```bash\n# Minimal: no host filesystem, no network\ndocker run --rm -i --network none ghcr.io/scriptsmith/platter\n\n# Read-only project access, no bash\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/work:ro \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --tools read,glob,grep\n\n# Full tools, scoped to a mounted directory\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/work \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --allow-path /work\n```\n\n#### What the container enforces\n\n- **Filesystem boundary.** Only explicitly mounted paths (`-v`) are visible. Even with bash enabled, commands cannot read or write host paths that aren't mounted.\n- **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.\n- **Process isolation.** Processes inside the container cannot see or signal host processes.\n- **Resource limits.** Docker's `--memory`, `--cpus`, and `--pids-limit` flags can cap resource usage to prevent denial of service.\n\n#### Containers vs VMs\n\nContainers share the host kernel; isolation is enforced by kernel features (namespaces, cgroups, seccomp). A kernel vulnerability or a misconfigured container (e.g. `--privileged`) can break the boundary. VMs run a separate kernel on virtualised hardware, so a guest compromise does not directly expose the host. If your threat model includes untrusted code that may attempt kernel exploits, run platter inside a VM (or a VM-backed container runtime like [Kata Containers](https://katacontainers.io/) or [Firecracker](https://firecracker-microvm.github.io/)). For most use cases, like limiting blast radius from an AI agent, a properly configured container (non-root, capabilities dropped, `--network none`) is sufficient.\n\n#### Combining sandbox and container\n\nThe just-bash sandbox and Docker container address different layers. Used together, they provide defense in depth:\n\n| Layer | Protects against |\n|---|---|\n| **just-bash sandbox** | Arbitrary native process execution: no `git`, `curl`, `rm`, etc. Commands run in a TypeScript interpreter, not the OS shell. |\n| **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. |\n\n```bash\n# Maximum isolation: sandbox inside a container, overlay fs, no network\ndocker run --rm -i --network none \\\n  -v /home/user/project:/work:ro \\\n  ghcr.io/scriptsmith/platter --sandbox --sandbox-fs overlay\n\n# Sandbox with controlled network access inside a container\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/work \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 \\\n    --sandbox --sandbox-allow-url \"https://api.github.com\"\n```\n\nFor 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:\n\n```bash\ndocker run --rm -p 3100:3100 \\\n  --user 1000:1000 \\\n  --cap-drop ALL \\\n  --read-only --tmpfs /tmp \\\n  -v /home/user/project:/work \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --sandbox\n```\n\nSee [Docker](#docker-1) for full usage instructions including mounting paths, networking, and building custom images.\n\n## Docker\n\nThe 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.\n\n```bash\ndocker pull ghcr.io/scriptsmith/platter        # latest release\ndocker pull ghcr.io/scriptsmith/platter:1.0.0  # specific version\n```\n\n### Running in stdio mode\n\nPipe JSON-RPC messages via stdin/stdout:\n\n```bash\ndocker run --rm -i ghcr.io/scriptsmith/platter\n```\n\n### Running in HTTP mode\n\nBind to `0.0.0.0` inside the container so the port is reachable from the host:\n\n```bash\ndocker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter -t http --host 0.0.0.0\n```\n\n### Mounting paths\n\nMount host directories into the container and use `--cwd` or `--allow-path` to give platter access:\n\n```bash\n# Mount a project directory as the working directory\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/work \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0\n\n# Mount read-only\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/work:ro \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 --tools read,glob,grep\n\n# Mount multiple directories with path restrictions\ndocker run --rm -p 3100:3100 \\\n  -v /home/user/project:/project \\\n  -v /tmp/scratch:/scratch \\\n  ghcr.io/scriptsmith/platter -t http --host 0.0.0.0 \\\n    --cwd /project \\\n    --allow-path /project --allow-path /scratch\n```\n\n### Networking\n\nBy default containers have full outbound network access. You can restrict this with Docker's network options:\n\n```bash\n# No network access (file-only tools)\ndocker run --rm --network none -i ghcr.io/scriptsmith/platter\n\n# Access host services (e.g. a local database)\ndocker run --rm -p 3100:3100 --network host ghcr.io/scriptsmith/platter -t http --host 0.0.0.0\n```\n\n### Installing additional software at runtime\n\nThe 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).\n\n```bash\ndocker run --rm -p 3100:3100 ghcr.io/scriptsmith/platter \\\n  bash -c \"apt-get update \u0026\u0026 apt-get install -y git nodejs \u0026\u0026 exec platter -t http --host 0.0.0.0\"\n```\n\nOr interactively:\n\n```bash\ndocker run --rm -it --entrypoint bash ghcr.io/scriptsmith/platter\n# inside the container:\napt-get update \u0026\u0026 apt-get install -y git python3\nplatter -t http --host 0.0.0.0\n```\n\n### Building a custom image\n\nLayer additional tools on top of the platter image for a ready-to-use environment:\n\n```dockerfile\nFROM ghcr.io/scriptsmith/platter:latest\n\nRUN apt-get update \\\n \u0026\u0026 apt-get install -y --no-install-recommends \\\n      git \\\n      curl \\\n      python3 \\\n      nodejs \\\n      npm \\\n \u0026\u0026 rm -rf /var/lib/apt/lists/*\n```\n\nBuild and run:\n\n```bash\ndocker build -t my-platter .\ndocker run --rm -p 3100:3100 -v ~/project:/work my-platter -t http --host 0.0.0.0\n```\n\n### Building the image locally\n\n```bash\ndocker build -t platter .\ndocker run --rm -i platter\n```\n\n## Build\n\n```bash\nbun install\nbun run build        # bundle to dist/\nbun run compile      # standalone binary for current platform -\u003e ./platter\nbun run compile:all  # cross-compile for linux-x64, linux-arm64, darwin-x64, darwin-arm64\nbun run format       # format with Biome\nbun run format:check # check formatting\nbun run lint         # lint with Biome\nbun run lint:fix     # lint and auto-fix with Biome\nbun run typecheck    # typecheck with TypeScript\nbun run test         # run tests\n```\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptsmith%2Fplatter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscriptsmith%2Fplatter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptsmith%2Fplatter/lists"}