forked from bots-garden/ori
| 🛟 Updated. | 1 | = SBX: Docker Sandbox Environment |
| 2 | :toc: left | |
| 3 | :toclevels: 3 | |
| 4 | :sectnums: | |
| 5 | :icons: font | |
| 6 | ||
| 7 | == Overview | |
| 8 | ||
| 9 | SBX is Docker's sandboxed environment system that provides isolated, persistent development containers with controlled network access, environment persistence, and Git integration. It enables secure development workflows with fine-grained policy control. | |
| 10 | ||
| 11 | == Core Features | |
| 12 | ||
| 13 | === Environment Persistence | |
| 14 | ||
| 15 | SBX provides a persistent environment file at `/etc/sandbox-persistent.sh` that is automatically sourced across all shell contexts: | |
| 16 | ||
| 17 | * **Non-interactive shells**: via `BASH_ENV=/etc/sandbox-persistent.sh` | |
| 18 | * **Login shells**: via `/etc/profile.d/sandbox-persistent.sh` | |
| 19 | * **Interactive shells**: via `/etc/bash.bashrc` and `~/.bashrc` | |
| 20 | ||
| 21 | .Adding Persistent Variables | |
| 22 | [source,bash] | |
| 23 | ---- | |
| 24 | echo "export VAR_NAME=value" >> /etc/sandbox-persistent.sh | |
| 25 | ---- | |
| 26 | ||
| 27 | [IMPORTANT] | |
| 28 | ==== | |
| 29 | **Never add shell completion scripts to `/etc/sandbox-persistent.sh`**. Completion scripts break the bash tool because the persistent file is sourced before every command execution, not just during initialization. Only add core initialization scripts (like `nvm.sh` or `sdkman-init.sh`), never completion scripts (like `bash_completion`). | |
| 30 | ==== | |
| 31 | ||
| 32 | === Network Access and Security | |
| 33 | ||
| 34 | SBX includes a firewall that restricts outbound network access with policy-based controls. | |
| 35 | ||
| 36 | ==== Blocked Request Handling | |
| 37 | ||
| 38 | Blocked HTTP/HTTPS requests return **HTTP 403** with structured explanations: | |
| 39 | ||
| 40 | [horizontal] | |
| 41 | Blocked by local rule:: A developer-added deny rule (global or per-sandbox) is blocking the host | |
| 42 | Blocked by org policy:: Centralized organization policy enforcement | |
| 43 | No matching allow rule:: Domain not on any allow list (default deny) | |
| 44 | ||
| 45 | .Inspecting Connection Policy | |
| 46 | [source,bash] | |
| 47 | ---- | |
| 48 | sbx policy log # Shows host, rule, reason, last-seen time | |
| 49 | sbx policy ls # Shows active rules and suppressed rules | |
| 50 | ---- | |
| 51 | ||
| 52 | .Allowing Network Access | |
| 53 | [source,bash] | |
| 54 | ---- | |
| 55 | sbx policy allow network <domain>[,<domain>…] # Allow specific domains | |
| 56 | sbx policy allow network "**" # Allow all (not on denylist) | |
| 57 | ---- | |
| 58 | ||
| 59 | .Removing Deny Rules | |
| 60 | [source,bash] | |
| 61 | ---- | |
| 62 | sbx policy rm network --resource <host> # Remove global deny | |
| 63 | sbx policy rm network --sandbox <sandbox> --resource <host> # Remove sandbox-scoped deny | |
| 64 | ---- | |
| 65 | ||
| 66 | ==== Port Publishing | |
| 67 | ||
| 68 | Services in the sandbox are not directly accessible from the host. To expose ports: | |
| 69 | ||
| 70 | [source,bash] | |
| 71 | ---- | |
| 72 | sbx ports <sandbox-name> --publish [[HOST_IP:]HOST_PORT:]SANDBOX_PORT[/PROTOCOL] | |
| 73 | ---- | |
| 74 | ||
| 75 | .Example: Publishing Web Server | |
| 76 | [source,bash] | |
| 77 | ---- | |
| 78 | sbx ports <sandbox-name> --publish 8080:8080/tcp | |
| 79 | sbx ports <sandbox-name> # List published ports | |
| 80 | sbx ports <sandbox-name> --unpublish 8080:8080/tcp # Unpublish | |
| 81 | ---- | |
| 82 | ||
| 83 | [NOTE] | |
| 84 | ==== | |
| 85 | Services must listen on the `eth0` interface (not just `127.0.0.1`). Bind to `0.0.0.0` (IPv4) or `::` (IPv6) to be reachable via port publishing. | |
| 86 | ==== | |
| 87 | ||
| 88 | ==== Accessing Host Services | |
| 89 | ||
| 90 | The sandbox has its own `localhost`. To reach services on the host machine: | |
| 91 | ||
| 92 | [source,bash] | |
| 93 | ---- | |
| 94 | curl http://host.docker.internal:3000 | |
| 95 | ---- | |
| 96 | ||
| 97 | ==== IP Stack Configuration | |
| 98 | ||
| 99 | If connectivity fails, the proxy may be using the wrong IP protocol version. Configure with: | |
| 100 | ||
| 101 | [source,bash] | |
| 102 | ---- | |
| 103 | DOCKER_SANDBOXES_IP_STACK=ipv4only # Only IPv4 | |
| 104 | DOCKER_SANDBOXES_IP_STACK=ipv6only # Only IPv6 | |
| 105 | DOCKER_SANDBOXES_IP_STACK=dual-stack # Try both (may be slow if one fails) | |
| 106 | ---- | |
| 107 | ||
| 108 | ==== .NET Aspire IPv6 Workaround | |
| 109 | ||
| 110 | .NET Aspire's DCP uses bracketed IPv6 loopback `http://[::1]:<port>`. Add to `NO_PROXY`: | |
| 111 | ||
| 112 | [source,bash] | |
| 113 | ---- | |
| 114 | cat >> /etc/sandbox-persistent.sh <<'EOF' | |
| 115 | if [ -z "${SBX_ASPIRE_NOPROXY_DONE:-}" ]; then | |
| 116 | export NO_PROXY="${NO_PROXY:+$NO_PROXY,}[::1]" | |
| 117 | export no_proxy="$NO_PROXY" | |
| 118 | export SBX_ASPIRE_NOPROXY_DONE=1 | |
| 119 | fi | |
| 120 | EOF | |
| 121 | ---- | |
| 122 | ||
| 123 | === Git Integration | |
| 124 | ||
| 125 | ==== Authentication | |
| 126 | ||
| 127 | The sandbox proxy handles GitHub authentication automatically by injecting credentials for HTTPS Git operations. No need to run `gh auth login` inside the sandbox. | |
| 128 | ||
| 129 | [IMPORTANT] | |
| 130 | ==== | |
| 131 | `gh auth status` will show "not logged in" inside the sandbox. This is expected and does not affect Git operations. | |
| 132 | ==== | |
| 133 | ||
| 134 | .Configuring GitHub Token | |
| 135 | [source,bash] | |
| 136 | ---- | |
| 137 | # For existing sandbox (immediate effect) | |
| 138 | sbx secret set github --sandbox <sandbox-name> -t "$(gh auth token)" | |
| 139 | ||
| 140 | # Globally for all future sandboxes (requires recreate) | |
| 141 | sbx secret set github -t "$(gh auth token)" | |
| 142 | ---- | |
| 143 | ||
| 144 | [TIP] | |
| 145 | ==== | |
| 146 | Find the sandbox name using `$SANDBOX_NAME`, `hostname`, or the deprecated `$SANDBOX_VM_ID` inside the sandbox. | |
| 147 | ==== | |
| 148 | ||
| 149 | ==== Git Workspace Modes | |
| 150 | ||
| 151 | SBX supports two workspace modes. Check your mode: | |
| 152 | ||
| 153 | [source,bash] | |
| 154 | ---- | |
| 155 | if [ -d /run/sandbox/source ]; then echo "clone mode"; else echo "direct mode"; fi | |
| 156 | ---- | |
| 157 | ||
| 158 | ===== Direct Mode (Default) | |
| 159 | ||
| 160 | The host working tree is mounted directly. Edits, commits, and branches appear on the host **immediately**. No separate copy to sync. | |
| 161 | ||
| 162 | ===== Clone Mode (`--clone`) | |
| 163 | ||
| 164 | A standalone Git clone made at sandbox creation time: | |
| 165 | ||
| 166 | * **HEAD** matches the host repo's state at creation | |
| 167 | * **Commits stay in sandbox** until fetched by the host | |
| 168 | * **Host fetches with**: `git fetch sandbox-<name>` | |
| 169 | * **Read-only host mount** at `/run/sandbox/source` | |
| 170 | ||
| 171 | .Syncing from Host in Clone Mode | |
| 172 | [source,bash] | |
| 173 | ---- | |
| 174 | git fetch /run/sandbox/source # Fetch host commits | |
| 175 | git log HEAD..FETCH_HEAD --oneline # See what's new on host | |
| 176 | git pull /run/sandbox/source <branch> # Merge host branch | |
| 177 | ---- | |
| 178 | ||
| 179 | .Host Retrieving Sandbox Commits | |
| 180 | [source,bash] | |
| 181 | ---- | |
| 182 | git fetch sandbox-<name> # On host machine | |
| 183 | ---- | |
| 184 | ||
| 185 | [WARNING] | |
| 186 | ==== | |
| 187 | Commits not pushed to a remote (like GitHub) are lost if the sandbox is removed, as the git-daemon only runs while the sandbox is active. | |
| 188 | ==== | |
| 189 | ||
| 190 | ==== Pushing and Pull Requests | |
| 191 | ||
| 192 | Push and open PRs **directly from inside the sandbox**: | |
| 193 | ||
| 194 | [source,bash] | |
| 195 | ---- | |
| 196 | git remote -v # Check available remotes | |
| 197 | git checkout -b <branch> # Create working branch | |
| 198 | git add -A && git commit -m "…" | |
| 199 | git push -u origin <branch> # Push to mirrored remote | |
| 200 | gh pr create --fill # Create PR | |
| 201 | ---- | |
| 202 | ||
| 203 | .Pushing to Fork | |
| 204 | [source,bash] | |
| 205 | ---- | |
| 206 | git push -u <fork-remote> <branch> | |
| 207 | gh pr create --repo <org>/<repo> --head <fork-user>:<branch> --fill | |
| 208 | ---- | |
| 209 | ||
| 210 | == Claude Code Integration | |
| 211 | ||
| 212 | === Environment File | |
| 213 | ||
| 214 | The `CLAUDE_ENV_FILE` variable is set to `/etc/sandbox-persistent.sh`, which is sourced before each Bash command execution, ensuring environment persistence across tool invocations. | |
| 215 | ||
| 216 | [CAUTION] | |
| 217 | ==== | |
| 218 | Apply the same shell completion restriction: never add completion scripts to this file, as it's sourced before every command, not just shell initialization. | |
| 219 | ==== | |
| 220 | ||
| 221 | === Using the Bash Tool | |
| 222 | ||
| 223 | When tools are not found in PATH after installation: | |
| 224 | ||
| 225 | [source,bash] | |
| 226 | ---- | |
| 227 | bash -l -c "your-command" # Use login shell | |
| 228 | bash -l -c "java -version" # Example: SDKMAN-installed Java | |
| 229 | bash -l -c "node --version" # Example: NVM-installed Node | |
| 230 | ---- | |
| 231 | ||
| 232 | [TIP] | |
| 233 | ==== | |
| 234 | Login shells always source the persistent environment file fresh, ensuring the latest configuration is honored even if shell snapshots contain cached state. | |
| 235 | ==== | |
| 236 | ||
| 237 | == Kits System | |
| 238 | ||
| 239 | SBX supports installable "kits" that provide specialized functionality. Kit documentation is available under `/Users/k33g/kDrive/Rickub/bots-garden/kits-agent-context/` and should be read only when relevant to the task. | |
| 240 | ||
| 241 | .Example: Ori Kit | |
| 242 | The `ori` kit provides an ACP web client for Claude Code, serving a browser-based interface with: | |
| 243 | ||
| 244 | * React SPA with Zed-style agent panel | |
| 245 | * Workspace panel with file tree and Monaco editor | |
| 246 | * Interactive terminal | |
| 247 | * Markdown and AsciiDoc rendering | |
| 248 | ||
| 249 | == Common Operations | |
| 250 | ||
| 251 | === Troubleshooting Connectivity | |
| 252 | ||
| 253 | .Check Host IP Addresses | |
| 254 | [source,bash] | |
| 255 | ---- | |
| 256 | # macOS | |
| 257 | ifconfig | grep 'inet ' # IPv4 | |
| 258 | ifconfig | grep 'inet6 ' # IPv6 (ignore fe80::) | |
| 259 | ||
| 260 | # Linux | |
| 261 | ip -4 addr show scope global | |
| 262 | ip -6 addr show scope global | |
| 263 | ---- | |
| 264 | ||
| 265 | === Managing Secrets | |
| 266 | ||
| 267 | [source,bash] | |
| 268 | ---- | |
| 269 | sbx secret set <name> --sandbox <sandbox-name> -t "value" # Per-sandbox | |
| 270 | sbx secret set <name> -t "value" # Global | |
| 271 | ---- | |
| 272 | ||
| 273 | === Docker Network Access | |
| 274 | ||
| 275 | Direct access to container ports requires adding the container's network to the `no_proxy` configuration, as Docker daemon access is provided but port routing needs explicit configuration. | |
| 276 | ||
| 277 | == Best Practices | |
| 278 | ||
| 279 | [arabic] | |
| 280 | . **Never add shell completions to persistent environment files** - only core initialization | |
| 281 | . **Use login shells (`bash -l -c`)** when tools aren't found after installation | |
| 282 | . **Bind services to `0.0.0.0` or `::`** not just `127.0.0.1` for port publishing | |
| 283 | . **Use `host.docker.internal`** to access host services, not `localhost` | |
| 284 | . **Push and create PRs from inside the sandbox** - it's the authorized workflow | |
| 285 | . **Set IP stack explicitly** if dual-stack causes slow connections | |
| 286 | . **Configure GitHub tokens as sandbox secrets** for Git push operations | |
| 287 | ||
| 288 | == References | |
| 289 | ||
| 290 | * Sandbox policy management: `sbx policy` | |
| 291 | * Port publishing: `sbx ports` | |
| 292 | * Secret management: `sbx secret` | |
| 293 | * Git authentication: Automatic via proxy credential injection | |
| 294 | * Environment persistence: `/etc/sandbox-persistent.sh` |