Sandboxing
Run CLI apps in isolated Docker containers with zero trust
Run any CLI or TUI app inside a hardened Docker container. This is ideal for running untrusted demos, third-party packages, or student submissions with full isolation from your host system.
bunx @gridland/container @gridland/demo -- landingSource Types
@gridland/container accepts four kinds of sources:
| Source | Example | What happens |
|---|---|---|
| npm package | @gridland/demo | Installs and runs the package bin |
| GitHub shorthand | cjroth/my-tui | Clones the repo and runs it |
| Git URL | https://github.com/user/repo | Clones the repo and runs it |
| Local directory | ./my-app | Mounts the directory read-only |
Getting Started
Install Docker
@gridland/container requires Docker to be installed and running.
Install Docker Desktop and make sure it's running.
curl -fsSL https://get.docker.com | shRun a Package
Run any npm package that exposes a CLI binary. Use -- to forward
arguments to the package:
bunx @gridland/container @gridland/demo -- landingThis runs the Gridland landing page demo inside a sandboxed container.
Arguments after -- are passed to the container entry point. Without --,
the package runs with no arguments (which may just print its help text).
The first run pulls or builds the sandbox image. Subsequent runs use the cached image.
Run a GitHub Repo
Use owner/repo shorthand or a full URL:
bunx @gridland/container cjroth/my-tui-demoThe repo is cloned with --depth 1, dependencies are installed, and the
entry point from package.json is executed.
Run a Local Project
Mount a local directory into the container read-only:
bunx @gridland/container ./my-appThe local directory is mounted read-only and copied to a tmpfs inside the container. Your source files are never modified.
Options
bunx @gridland/container <source> [options] [-- <args...>]| Option | Default | Description |
|---|---|---|
--no-network | network on | Disable network access inside the container |
--memory <limit> | 512m | Set the container memory limit |
--build | pull first | Force a local Docker image build |
-- <args...> | Forward arguments to the container entry point |
Examples
# Run the Gridland landing page demo
bunx @gridland/container @gridland/demo -- landing
# Run any other Gridland demo (table, gradient, chat, etc.)
bunx @gridland/container @gridland/demo -- gradient
# Run a third-party npm package
bunx @gridland/container cowsay -- "Hello from a container"
# Run with no network access
bunx @gridland/container @gridland/demo --no-network -- landing
# Set a 1GB memory limit
bunx @gridland/container heavy-app --memory 1g
# Force rebuild the sandbox image
bunx @gridland/container my-app --buildSecurity
Every container runs with hardened defaults:
--cap-drop=ALL-all Linux capabilities dropped--security-opt=no-new-privileges-prevents privilege escalation--read-only-root filesystem is read-only--pids-limit=256-limits process count--memory=512m-default memory cap- Non-root user -runs as an unprivileged
runneruser - tmpfs
/tmp-writable scratch space limited to 256MB
Network access is enabled by default. Use --no-network when running
untrusted code that should not make outbound connections.
How It Works
- The CLI detects the source type (npm, git, or local)
- Docker pulls the pre-built sandbox image from
ghcr.io(or builds locally) - A container runs with the security flags above
- Inside the container, the entrypoint installs dependencies and runs the app
- TTY is forwarded so interactive TUI apps work normally
- The container is removed on exit (
--rm)