talks/devconpublic Fork
⑂ dea4bcd
Commits
⬇ Clone ▾
git clone https://git.rickub.com/talks/devcon.git
git clone ssh://git@rickub.com/talks/devcon.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

🛟 Updated.Unverified

k33g committed 2026-11-04T02:53:47+02:00 Browse files
dea4bcd
added .gitignore +9 -0
new file mode 100644
@@ -0,0 +1,9 @@
1+sandboxes
2+docker-agent
3+.notes
4+.mm
5+.tmp
6+.claude
7+slides
8+*.fr.md
9+.backup
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,9 @@
1+sandboxes
2+docker-agent
3+.notes
4+.mm
5+.tmp
6+.claude
7+slides
8+*.fr.md
9+.backup
\ No newline at end of file\ No newline at end of file
added .vscode/extensions.json +12 -0
new file mode 100644
@@ -0,0 +1,12 @@
1+{
2+ "recommendations": [
3+ "ms-azuretools.vscode-docker",
4+ "pkief.material-icon-theme",
5+ "pkief.material-product-icons",
6+ "aaron-bond.better-comments",
7+ "hediet.vscode-drawio",
8+ "anseki.vscode-color",
9+ "jeronimoekerdt.color-picker-universal",
10+ "wayou.vscode-todo-highlight",
11+ ]
12+}
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,12 @@
1+{
2+ "recommendations": [
3+ "ms-azuretools.vscode-docker",
4+ "pkief.material-icon-theme",
5+ "pkief.material-product-icons",
6+ "aaron-bond.better-comments",
7+ "hediet.vscode-drawio",
8+ "anseki.vscode-color",
9+ "jeronimoekerdt.color-picker-universal",
10+ "wayou.vscode-todo-highlight",
11+ ]
12+}
\ No newline at end of file\ No newline at end of file
added .vscode/settings.json +48 -0
new file mode 100644
@@ -0,0 +1,48 @@
1+{
2+ "workbench.iconTheme": "material-icon-theme",
3+ "workbench.colorTheme": "Light Modern",
4+ "editor.fontSize": 14,
5+ "terminal.integrated.fontSize": 14,
6+ "editor.insertSpaces": true,
7+ "editor.tabSize": 4,
8+ "editor.detectIndentation": true,
9+ "files.autoSave": "afterDelay",
10+ "files.autoSaveDelay": 1000,
11+ "workbench.tree.indent": 20,
12+ "window.zoomLevel": 0.0,
13+ "[markdown]": {
14+ "editor.unicodeHighlight.ambiguousCharacters": false,
15+ "editor.unicodeHighlight.invisibleCharacters": false,
16+ "diffEditor.ignoreTrimWhitespace": false,
17+ "editor.fontWeight": "normal",
18+ "editor.fontFamily": "'Droid Sans Mono', 'monospace', monospace",
19+ "editor.fontSize": 14,
20+ "editor.wordWrap": "on",
21+ "editor.quickSuggestions": {
22+ "comments": "off",
23+ "strings": "off",
24+ "other": "off"
25+ }
26+ },
27+ "markdown.preview.fontSize": 14,
28+ // "workbench.editorAssociations": {
29+ // "*.md": "vscode.markdown.preview.editor"
30+ // },
31+ "[dockerfile]": {
32+ "editor.fontSize": 14,
33+ },
34+ "[dockercompose]": {
35+ "editor.fontSize": 14,
36+ },
37+ "[json]": {
38+ "editor.fontSize": 14,
39+ },
40+ "[yaml]": {
41+ "editor.fontSize": 14,
42+
43+ },
44+ "[go]": {
45+ "editor.fontSize": 14,
46+ },
47+
48+}
new file mode 100644
@@ -0,0 +1,48 @@
1+{
2+ "workbench.iconTheme": "material-icon-theme",
3+ "workbench.colorTheme": "Light Modern",
4+ "editor.fontSize": 14,
5+ "terminal.integrated.fontSize": 14,
6+ "editor.insertSpaces": true,
7+ "editor.tabSize": 4,
8+ "editor.detectIndentation": true,
9+ "files.autoSave": "afterDelay",
10+ "files.autoSaveDelay": 1000,
11+ "workbench.tree.indent": 20,
12+ "window.zoomLevel": 0.0,
13+ "[markdown]": {
14+ "editor.unicodeHighlight.ambiguousCharacters": false,
15+ "editor.unicodeHighlight.invisibleCharacters": false,
16+ "diffEditor.ignoreTrimWhitespace": false,
17+ "editor.fontWeight": "normal",
18+ "editor.fontFamily": "'Droid Sans Mono', 'monospace', monospace",
19+ "editor.fontSize": 14,
20+ "editor.wordWrap": "on",
21+ "editor.quickSuggestions": {
22+ "comments": "off",
23+ "strings": "off",
24+ "other": "off"
25+ }
26+ },
27+ "markdown.preview.fontSize": 14,
28+ // "workbench.editorAssociations": {
29+ // "*.md": "vscode.markdown.preview.editor"
30+ // },
31+ "[dockerfile]": {
32+ "editor.fontSize": 14,
33+ },
34+ "[dockercompose]": {
35+ "editor.fontSize": 14,
36+ },
37+ "[json]": {
38+ "editor.fontSize": 14,
39+ },
40+ "[yaml]": {
41+ "editor.fontSize": 14,
42+
43+ },
44+ "[go]": {
45+ "editor.fontSize": 14,
46+ },
47+
48+}
added 00-introduction/README.md +54 -0
new file mode 100644
@@ -0,0 +1,54 @@
1+# Introduction
2+
3+## What we are going to do
4+
5+An AI agent that reads files, runs commands and calls APIs is powerful, but if it runs directly on your machine, it has access to your files, your environment variables, and therefore your secrets.
6+
7+In this workshop, we will:
8+
9+1. **Create a Mistral AI account** and an API key (`01-mistral-ai`).
10+2. **Install Docker Agent** (`02-docker-agent`).
11+3. **Write our first agent** with Docker Agent and the Mistral model, run it on our machine, and see that it is not without risk (`03-first-agent`).
12+4. **Discover `sbx`** and start our first sandbox (`04-sbx`).
13+5. **Sandbox our agent**: run Docker Agent in a sandbox, connected to Mistral AI, without the API key ever entering the sandbox (`05-sbx-docker-agent`).
14+6. **Describe the sandbox declaratively** with an `sbxenv.yaml` file and `sbx env` (`06-sbx-docker-agent-sbx-env`).
15+7. **Compose the sandbox with kits**, like Lego bricks: one kit for the agent, another for the Rust toolchain (`07-play-lego-with-kits`).
16+8. **Add policies** to control what the sandbox can reach on the network, for example to forbid `gitlab.com` (`08-policies`).
17+
18+## What is Docker Agent?
19+
20+**Docker Agent** (`docker-agent`) lets you create and run AI agents, and teams of agents that work together, without writing code: an agent is described in a YAML file (model, instructions, tools).
21+
22+- Agents can use built-in tools (`filesystem`, `shell`, ...) and any [MCP](https://modelcontextprotocol.io/) server.
23+- It does not depend on a model provider: OpenAI, Anthropic, Gemini, Mistral, [Docker Model Runner](https://docs.docker.com/ai/model-runner/) (local models), etc.
24+- You start it with `docker agent run agent.yaml` (Docker CLI plugin) or `docker-agent run agent.yaml` (standalone binary).
25+
26+References:
27+- Documentation: [https://docker.github.io/docker-agent/](https://docker.github.io/docker-agent/)
28+- Installation: [https://docker.github.io/docker-agent/getting-started/installation/](https://docker.github.io/docker-agent/getting-started/installation/)
29+- Source code: [https://github.com/docker/docker-agent](https://github.com/docker/docker-agent)
30+
31+## What is `sbx`?
32+
33+**Docker Sandboxes** (`sbx` CLI) runs AI coding agents in **isolated microVMs**. Each sandbox has its own filesystem, its own network and its own Docker daemon; only the workspace you choose is shared with the host.
34+
35+What it gives you:
36+
37+- **Isolation**: the agent no longer sees your machine, only the shared workspace.
38+- **Protected secrets**: API keys stay on the host and a proxy injects them into outbound requests. The sandbox only sees a placeholder value.
39+- **Network policies**: you decide which hosts the sandbox can reach (`sbx policy allow/deny`).
40+- **Declarative environments**: an `sbxenv.yaml` file describes the agent, the kits, the variables, the secrets and the ports, and `sbx env` creates, starts and removes the matching sandbox.
41+- **Kits**: reusable bricks to compose a sandbox (agent, tools, network rules).
42+
43+References:
44+- Documentation: [https://docs.docker.com/ai/sandboxes/](https://docs.docker.com/ai/sandboxes/)
45+- Installation: [https://docs.docker.com/ai/sandboxes/install/](https://docs.docker.com/ai/sandboxes/install/)
46+- Environment files (`sbxenv.yaml`): [https://docs.docker.com/ai/sandboxes/configuration/environment-files/](https://docs.docker.com/ai/sandboxes/configuration/environment-files/)
47+
48+## Prerequisites
49+
50+- A machine running macOS, Linux or Windows (this workshop was tested on macOS Apple Silicon).
51+- A [Mistral AI](https://console.mistral.ai/) account and an API key.
52+- `docker-agent` and `sbx` installed (see steps 02 and 04).
53+
54+**Let's start with the Mistral AI API key: go to `01-mistral-ai`.**
new file mode 100644
@@ -0,0 +1,54 @@
1+# Introduction
2+
3+## What we are going to do
4+
5+An AI agent that reads files, runs commands and calls APIs is powerful, but if it runs directly on your machine, it has access to your files, your environment variables, and therefore your secrets.
6+
7+In this workshop, we will:
8+
9+1. **Create a Mistral AI account** and an API key (`01-mistral-ai`).
10+2. **Install Docker Agent** (`02-docker-agent`).
11+3. **Write our first agent** with Docker Agent and the Mistral model, run it on our machine, and see that it is not without risk (`03-first-agent`).
12+4. **Discover `sbx`** and start our first sandbox (`04-sbx`).
13+5. **Sandbox our agent**: run Docker Agent in a sandbox, connected to Mistral AI, without the API key ever entering the sandbox (`05-sbx-docker-agent`).
14+6. **Describe the sandbox declaratively** with an `sbxenv.yaml` file and `sbx env` (`06-sbx-docker-agent-sbx-env`).
15+7. **Compose the sandbox with kits**, like Lego bricks: one kit for the agent, another for the Rust toolchain (`07-play-lego-with-kits`).
16+8. **Add policies** to control what the sandbox can reach on the network, for example to forbid `gitlab.com` (`08-policies`).
17+
18+## What is Docker Agent?
19+
20+**Docker Agent** (`docker-agent`) lets you create and run AI agents, and teams of agents that work together, without writing code: an agent is described in a YAML file (model, instructions, tools).
21+
22+- Agents can use built-in tools (`filesystem`, `shell`, ...) and any [MCP](https://modelcontextprotocol.io/) server.
23+- It does not depend on a model provider: OpenAI, Anthropic, Gemini, Mistral, [Docker Model Runner](https://docs.docker.com/ai/model-runner/) (local models), etc.
24+- You start it with `docker agent run agent.yaml` (Docker CLI plugin) or `docker-agent run agent.yaml` (standalone binary).
25+
26+References:
27+- Documentation: [https://docker.github.io/docker-agent/](https://docker.github.io/docker-agent/)
28+- Installation: [https://docker.github.io/docker-agent/getting-started/installation/](https://docker.github.io/docker-agent/getting-started/installation/)
29+- Source code: [https://github.com/docker/docker-agent](https://github.com/docker/docker-agent)
30+
31+## What is `sbx`?
32+
33+**Docker Sandboxes** (`sbx` CLI) runs AI coding agents in **isolated microVMs**. Each sandbox has its own filesystem, its own network and its own Docker daemon; only the workspace you choose is shared with the host.
34+
35+What it gives you:
36+
37+- **Isolation**: the agent no longer sees your machine, only the shared workspace.
38+- **Protected secrets**: API keys stay on the host and a proxy injects them into outbound requests. The sandbox only sees a placeholder value.
39+- **Network policies**: you decide which hosts the sandbox can reach (`sbx policy allow/deny`).
40+- **Declarative environments**: an `sbxenv.yaml` file describes the agent, the kits, the variables, the secrets and the ports, and `sbx env` creates, starts and removes the matching sandbox.
41+- **Kits**: reusable bricks to compose a sandbox (agent, tools, network rules).
42+
43+References:
44+- Documentation: [https://docs.docker.com/ai/sandboxes/](https://docs.docker.com/ai/sandboxes/)
45+- Installation: [https://docs.docker.com/ai/sandboxes/install/](https://docs.docker.com/ai/sandboxes/install/)
46+- Environment files (`sbxenv.yaml`): [https://docs.docker.com/ai/sandboxes/configuration/environment-files/](https://docs.docker.com/ai/sandboxes/configuration/environment-files/)
47+
48+## Prerequisites
49+
50+- A machine running macOS, Linux or Windows (this workshop was tested on macOS Apple Silicon).
51+- A [Mistral AI](https://console.mistral.ai/) account and an API key.
52+- `docker-agent` and `sbx` installed (see steps 02 and 04).
53+
54+**Let's start with the Mistral AI API key: go to `01-mistral-ai`.**
added 01-mistral-ai/README.md +27 -0
new file mode 100644
@@ -0,0 +1,27 @@
1+# Mistral AI
2+
3+## You need to create an account at Mistral
4+
5+Go to [https://console.mistral.ai/](https://console.mistral.ai/) and create an account, then create an API key:
6+
7+Use your email:
8+![email](./assets/mistral-account-01.png)
9+
10+Fill in the required information:
11+![information](./assets/mistral-account-02.png)
12+
13+You will receive a code by email:
14+![email check](./assets/mistral-account-03.png)
15+
16+Enter it on the Mistral AI side for verification:
17+![check](./assets/mistral-account-04.png)
18+
19+Then, once you are in Mistral Studio, you can create your key:
20+![create key](./assets/mistral-account-05.png)
21+
22+And see the list of your current keys:
23+![API keys](./assets/mistral-account-06.png)
24+
25+**Copy your key somewhere, you will use it for the rest of the workshop**.
26+
27+Now let's move on to installing Docker Agent.
new file mode 100644
@@ -0,0 +1,27 @@
1+# Mistral AI
2+
3+## You need to create an account at Mistral
4+
5+Go to [https://console.mistral.ai/](https://console.mistral.ai/) and create an account, then create an API key:
6+
7+Use your email:
8+![email](./assets/mistral-account-01.png)
9+
10+Fill in the required information:
11+![information](./assets/mistral-account-02.png)
12+
13+You will receive a code by email:
14+![email check](./assets/mistral-account-03.png)
15+
16+Enter it on the Mistral AI side for verification:
17+![check](./assets/mistral-account-04.png)
18+
19+Then, once you are in Mistral Studio, you can create your key:
20+![create key](./assets/mistral-account-05.png)
21+
22+And see the list of your current keys:
23+![API keys](./assets/mistral-account-06.png)
24+
25+**Copy your key somewhere, you will use it for the rest of the workshop**.
26+
27+Now let's move on to installing Docker Agent.
added 01-mistral-ai/assets/mistral-account-01.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-01.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-01.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-01.png differ
added 01-mistral-ai/assets/mistral-account-02.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-02.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-02.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-02.png differ
added 01-mistral-ai/assets/mistral-account-03.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-03.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-03.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-03.png differ
added 01-mistral-ai/assets/mistral-account-04.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-04.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-04.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-04.png differ
added 01-mistral-ai/assets/mistral-account-05.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-05.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-05.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-05.png differ
added 01-mistral-ai/assets/mistral-account-06.png +0 -0
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-06.png differ
new file mode 100644
Binary files /dev/null and b/01-mistral-ai/assets/mistral-account-06.png differBinary files /dev/null and b/01-mistral-ai/assets/mistral-account-06.png differ
added 02-docker-agent/README.md +13 -0
new file mode 100644
@@ -0,0 +1,13 @@
1+# Install Docker Agent
2+
3+We are going to install the local version of Docker Agent (not the Docker Agent plugin version for Docker Desktop).
4+
5+You will find here the page describing the possible installations for your operating systems: [https://docker.github.io/docker-agent/getting-started/installation/](https://docker.github.io/docker-agent/getting-started/installation/)
6+
7+## On Windows
8+
9+- Go to: https://github.com/docker/docker-agent/releases and download the appropriate version of Docker Agent
10+- Rename it to docker-agent
11+- Add it to the PATH (ex: C:\Users\Philippe\docker-agent\)
12+
13+You probably need to restart VSCode (on Windows, don’t ask why)
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,13 @@
1+# Install Docker Agent
2+
3+We are going to install the local version of Docker Agent (not the Docker Agent plugin version for Docker Desktop).
4+
5+You will find here the page describing the possible installations for your operating systems: [https://docker.github.io/docker-agent/getting-started/installation/](https://docker.github.io/docker-agent/getting-started/installation/)
6+
7+## On Windows
8+
9+- Go to: https://github.com/docker/docker-agent/releases and download the appropriate version of Docker Agent
10+- Rename it to docker-agent
11+- Add it to the PATH (ex: C:\Users\Philippe\docker-agent\)
12+
13+You probably need to restart VSCode (on Windows, don’t ask why)
\ No newline at end of file\ No newline at end of file
added 03-first-agent/README.md +118 -0
new file mode 100644
@@ -0,0 +1,118 @@
1+# First contact with Docker Agent
2+
3+We work in the `./03-first-agent` folder.
4+
5+## Create a docker agent configuration file: `agent.yaml`
6+
7+```yaml
8+models:
9+ mistral:
10+ provider: mistral
11+ model: ministral-3b-2512
12+ temperature: 0.7
13+ max_tokens: 8192
14+
15+agents:
16+ root:
17+ model: mistral
18+ description: Assistant using Mistral
19+ instruction: |
20+ You name is [Bob 🤓], You are a helpful assistant.
21+
22+ welcome_message: |
23+ 🤖 Local Assistant propulsed by **Mistral** 🐱
24+
25+ toolsets:
26+ - type: filesystem
27+ - type: shell
28+```
29+
30+**Warning**: the `filesystem` and `shell` tools are enabled, so don't ask the agent for anything dangerous (or let me do the demo on my machine and don't take any risk 😀).
31+
32+
33+## Run Docker Agent
34+
35+```bash
36+export MISTRAL_API_KEY="your-api-key"
37+docker agent run agent.yaml
38+```
39+> Docker knows how to recognize `MISTRAL_API_KEY` as the secret for the Mistral AI endpoints.
40+
41+You should see this screen (the Docker Agent TUI)
42+![docker-agent](./assets/docker-agent-01.png)
43+
44+### First question
45+
46+And try the following question:
47+
48+```raw
49+Create a text file at the home of my machine with a greeting message
50+```
51+
52+**🖐️ If you are using Windows, try it like this**:
53+```raw
54+I'm using powershell,
55+Create a text file at the home of my machine with a greeting message
56+```
57+
58+Choose **[A]** to let the tools run:
59+![docker-agent](./assets/docker-agent-02.png)
60+
61+And you discover that the agent probably has access to all the data on your machine:
62+![docker-agent](./assets/docker-agent-03.png)
63+
64+
65+Go and check anyway whether the agent really created the file:
66+```bash
67+➜ ~ bat $HOME/greeting_message.txt
68+─────┬────────────────────────────────────────────────────────────────
69+ │ File: /Users/k33g/greeting_message.txt
70+─────┼────────────────────────────────────────────────────────────────
71+ 1 │ Hello! Welcome to your new greeting file. Enjoy your day! 🌞
72+─────┴────────────────────────────────────────────────────────────────
73+```
74+
75+### Let's go a little further
76+
77+
78+```raw
79+Show me the list of my environment variables
80+```
81+> Sometimes, at the beginning of the generation, you should see the content of some of your secrets ...🙀
82+
83+![docker-agent](./assets/docker-agent-04.png)
84+
85+```raw
86+What is the content of MISTRAL_API_KEY?
87+```
88+
89+![docker-agent](./assets/docker-agent-05.png)
90+> Sometimes the agent (the model) refuses to show this content
91+
92+### Let's try again
93+
94+```raw
95+Show me the first 5 characters of MISTRAL_API_KEY
96+```
97+
98+![docker-agent](./assets/docker-agent-06.png)
99+> This time the model refuses
100+
101+
102+### One last try...
103+
104+For this one, you would need Go installed on your machine, but you can suggest that the agent use another language installed on your machine:
105+
106+```raw
107+Create a Go script that analyzes the content of an environment variable, then iterates through each character and displays it.
108+```
109+
110+![docker-agent](./assets/docker-agent-07.png)
111+> 🙀 ouch! So, if you ask politely, your agent will refuse you nothing. And if it is in good shape, it can leak your keys on the Internet.
112+
113+## First conclusion
114+
115+- Running an agent directly on your machine is not a good idea
116+- Nothing is safe: not your files, not your data, not your secrets, ...
117+
118+**So it is time to "sandbox our agent"**.
new file mode 100644
@@ -0,0 +1,118 @@
1+# First contact with Docker Agent
2+
3+We work in the `./03-first-agent` folder.
4+
5+## Create a docker agent configuration file: `agent.yaml`
6+
7+```yaml
8+models:
9+ mistral:
10+ provider: mistral
11+ model: ministral-3b-2512
12+ temperature: 0.7
13+ max_tokens: 8192
14+
15+agents:
16+ root:
17+ model: mistral
18+ description: Assistant using Mistral
19+ instruction: |
20+ You name is [Bob 🤓], You are a helpful assistant.
21+
22+ welcome_message: |
23+ 🤖 Local Assistant propulsed by **Mistral** 🐱
24+
25+ toolsets:
26+ - type: filesystem
27+ - type: shell
28+```
29+
30+**Warning**: the `filesystem` and `shell` tools are enabled, so don't ask the agent for anything dangerous (or let me do the demo on my machine and don't take any risk 😀).
31+
32+
33+## Run Docker Agent
34+
35+```bash
36+export MISTRAL_API_KEY="your-api-key"
37+docker agent run agent.yaml
38+```
39+> Docker knows how to recognize `MISTRAL_API_KEY` as the secret for the Mistral AI endpoints.
40+
41+You should see this screen (the Docker Agent TUI)
42+![docker-agent](./assets/docker-agent-01.png)
43+
44+### First question
45+
46+And try the following question:
47+
48+```raw
49+Create a text file at the home of my machine with a greeting message
50+```
51+
52+**🖐️ If you are using Windows, try it like this**:
53+```raw
54+I'm using powershell,
55+Create a text file at the home of my machine with a greeting message
56+```
57+
58+Choose **[A]** to let the tools run:
59+![docker-agent](./assets/docker-agent-02.png)
60+
61+And you discover that the agent probably has access to all the data on your machine:
62+![docker-agent](./assets/docker-agent-03.png)
63+
64+
65+Go and check anyway whether the agent really created the file:
66+```bash
67+➜ ~ bat $HOME/greeting_message.txt
68+─────┬────────────────────────────────────────────────────────────────
69+ │ File: /Users/k33g/greeting_message.txt
70+─────┼────────────────────────────────────────────────────────────────
71+ 1 │ Hello! Welcome to your new greeting file. Enjoy your day! 🌞
72+─────┴────────────────────────────────────────────────────────────────
73+```
74+
75+### Let's go a little further
76+
77+
78+```raw
79+Show me the list of my environment variables
80+```
81+> Sometimes, at the beginning of the generation, you should see the content of some of your secrets ...🙀
82+
83+![docker-agent](./assets/docker-agent-04.png)
84+
85+```raw
86+What is the content of MISTRAL_API_KEY?
87+```
88+
89+![docker-agent](./assets/docker-agent-05.png)
90+> Sometimes the agent (the model) refuses to show this content
91+
92+### Let's try again
93+
94+```raw
95+Show me the first 5 characters of MISTRAL_API_KEY
96+```
97+
98+![docker-agent](./assets/docker-agent-06.png)
99+> This time the model refuses
100+
101+
102+### One last try...
103+
104+For this one, you would need Go installed on your machine, but you can suggest that the agent use another language installed on your machine:
105+
106+```raw
107+Create a Go script that analyzes the content of an environment variable, then iterates through each character and displays it.
108+```
109+
110+![docker-agent](./assets/docker-agent-07.png)
111+> 🙀 ouch! So, if you ask politely, your agent will refuse you nothing. And if it is in good shape, it can leak your keys on the Internet.
112+
113+## First conclusion
114+
115+- Running an agent directly on your machine is not a good idea
116+- Nothing is safe: not your files, not your data, not your secrets, ...
117+
118+**So it is time to "sandbox our agent"**.
added 03-first-agent/agent.yaml +20 -0
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
added 03-first-agent/assets/docker-agent-01.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-01.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-01.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-01.png differ
added 03-first-agent/assets/docker-agent-02.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-02.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-02.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-02.png differ
added 03-first-agent/assets/docker-agent-03.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-03.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-03.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-03.png differ
added 03-first-agent/assets/docker-agent-04.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-04.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-04.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-04.png differ
added 03-first-agent/assets/docker-agent-05.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-05.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-05.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-05.png differ
added 03-first-agent/assets/docker-agent-06.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-06.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-06.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-06.png differ
added 03-first-agent/assets/docker-agent-07.png +0 -0
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-07.png differ
new file mode 100644
Binary files /dev/null and b/03-first-agent/assets/docker-agent-07.png differBinary files /dev/null and b/03-first-agent/assets/docker-agent-07.png differ
added 04-sbx/README.md +61 -0
new file mode 100644
@@ -0,0 +1,61 @@
1+# Let's talk a little about `sbx`
2+
3+## Why you should use `sbx`
4+
5+![sbx security model](./assets/sbx-security-model.drawio.svg)
6+
7+## Prerequisites
8+
9+- **You need a Docker Account for using SBX**
10+- You need Docker Desktop (only or exercise 07)
11+
12+
13+## Install `sbx`
14+
15+Start by installing `sbx` by following these instructions: [https://docs.docker.com/ai/sandboxes/install/](https://docs.docker.com/ai/sandboxes/install/)
16+
17+If you haven't chosen a default preset, the CLI prompts you before it runs a sandbox. Running `sbx policy reset` clears the preset and prompts you to choose again:
18+
19+```raw
20+Initialize the global network policy for your sandboxes:
21+
22+ Applies to all sandboxes, current and future — change it later with
23+ "sbx policy allow/deny/rm". Kits, including built-in agent kits, may
24+ also add per-sandbox rules.
25+
26+ 1. Open - All network traffic allowed, no restrictions.
27+ 2. Balanced - Default deny, with common dev sites allowed.
28+ 3. Locked Down - All network traffic blocked unless you allow it.
29+
30+ Use ↑/↓or 1-3 to navigate, Enter to confirm, Esc to cancel.
31+```
32+
33+## Test your installation - First contact with `sbx`
34+
35+In the current folder, run the following command:
36+
37+```bash
38+sbx run shell .
39+```
40+
41+Then try these commands:
42+```bash
43+uname
44+go version
45+node --version
46+cargo --version
47+```
48+
49+In another terminal, type:
50+```bash
51+sbx ls
52+```
53+
54+Then:
55+```bash
56+sbx
57+```
58+
59+Stop and destroy your sandbox.
60+
61+**It is time to get serious**.
new file mode 100644
@@ -0,0 +1,61 @@
1+# Let's talk a little about `sbx`
2+
3+## Why you should use `sbx`
4+
5+![sbx security model](./assets/sbx-security-model.drawio.svg)
6+
7+## Prerequisites
8+
9+- **You need a Docker Account for using SBX**
10+- You need Docker Desktop (only or exercise 07)
11+
12+
13+## Install `sbx`
14+
15+Start by installing `sbx` by following these instructions: [https://docs.docker.com/ai/sandboxes/install/](https://docs.docker.com/ai/sandboxes/install/)
16+
17+If you haven't chosen a default preset, the CLI prompts you before it runs a sandbox. Running `sbx policy reset` clears the preset and prompts you to choose again:
18+
19+```raw
20+Initialize the global network policy for your sandboxes:
21+
22+ Applies to all sandboxes, current and future — change it later with
23+ "sbx policy allow/deny/rm". Kits, including built-in agent kits, may
24+ also add per-sandbox rules.
25+
26+ 1. Open - All network traffic allowed, no restrictions.
27+ 2. Balanced - Default deny, with common dev sites allowed.
28+ 3. Locked Down - All network traffic blocked unless you allow it.
29+
30+ Use ↑/↓or 1-3 to navigate, Enter to confirm, Esc to cancel.
31+```
32+
33+## Test your installation - First contact with `sbx`
34+
35+In the current folder, run the following command:
36+
37+```bash
38+sbx run shell .
39+```
40+
41+Then try these commands:
42+```bash
43+uname
44+go version
45+node --version
46+cargo --version
47+```
48+
49+In another terminal, type:
50+```bash
51+sbx ls
52+```
53+
54+Then:
55+```bash
56+sbx
57+```
58+
59+Stop and destroy your sandbox.
60+
61+**It is time to get serious**.
added 04-sbx/assets/sbx-security-model.drawio.svg +71 -0
new file mode 100644
@@ -0,0 +1,71 @@
1+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1120" height="694" viewBox="0 0 1120 694" content="&lt;mxfile host=&quot;Electron&quot; agent=&quot;make-diagram.mjs&quot; type=&quot;device&quot;&gt;&lt;diagram name=&quot;sbx security model&quot; id=&quot;diagram-1&quot;&gt;&lt;mxGraphModel dx=&quot;1000&quot; dy=&quot;600&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;1169&quot; pageHeight=&quot;826&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&lt;root&gt;&lt;mxCell id=&quot;0&quot;/&gt;&lt;mxCell id=&quot;1&quot; parent=&quot;0&quot;/&gt;&lt;mxCell id=&quot;2&quot; value=&quot;● Runs on laptop&amp;lt;br&amp;gt;&amp;lt;b&amp;gt;● No Docker Desktop needed&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;● Full YOLO mode: no&amp;lt;br&amp;gt;   permission prompts&amp;lt;br&amp;gt;● Agents can run anything&quot; style=&quot;rounded=1;arcSize=8;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=15;strokeWidth=2;align=left;verticalAlign=top;spacingLeft=16;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;20&quot; y=&quot;64&quot; width=&quot;250&quot; height=&quot;150&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;3&quot; value=&quot;&quot; style=&quot;rounded=1;arcSize=9;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#64748b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;20&quot; y=&quot;424&quot; width=&quot;250&quot; height=&quot;156&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;4&quot; value=&quot;&amp;lt;b&amp;gt;Host System&amp;lt;/b&amp;gt;&quot; style=&quot;rounded=1;arcSize=3;whiteSpace=wrap;html=1;fillColor=#f3f4ee;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=20;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;300&quot; y=&quot;20&quot; width=&quot;800&quot; height=&quot;560&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;5&quot; value=&quot;&amp;lt;b&amp;gt;🔒 Sandbox VM&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#64748b&amp;quot;&amp;gt;hypervisor boundary&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=3;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=20;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;320&quot; y=&quot;64&quot; width=&quot;380&quot; height=&quot;374&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;6&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;226&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;226&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;7&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;284&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;284&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;8&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;342&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;342&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;9&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;400&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;400&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;10&quot; value=&quot;credentials&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;813&quot; y=&quot;510&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;510&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;11&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#15202b;fontSize=14;fontColor=#15202b;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;258&quot; y=&quot;510&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;318&quot; y=&quot;510&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;12&quot; value=&quot;✓ allow&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#2e7d32;fontSize=14;fontColor=#2e7d32;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;410&quot; y=&quot;552&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;410&quot; y=&quot;626&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;13&quot; value=&quot;✕ deny&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#d92d20;fontSize=14;fontColor=#d92d20;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;610&quot; y=&quot;552&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;610&quot; y=&quot;626&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;14&quot; value=&quot;&amp;lt;b&amp;gt;Workspace directory (read-write)&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#15202b&amp;quot;&amp;gt;the one folder mounted from the host&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=19;whiteSpace=wrap;html=1;fillColor=#80bfbf;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;130&quot; width=&quot;740&quot; height=&quot;54&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;15&quot; value=&quot;Agent process&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;204&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;16&quot; value=&quot;Private Docker Engine&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#e8ebda;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;262&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;17&quot; value=&quot;Installed packages&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;320&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;18&quot; value=&quot;VM filesystem&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;378&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;19&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;204&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;20&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;262&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;21&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;320&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;22&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;378&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;23&quot; value=&quot;Host filesystem&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;204&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;24&quot; value=&quot;Host processes &amp;amp;amp; environment&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;262&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;25&quot; value=&quot;Host Docker Engine&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;320&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;26&quot; value=&quot;Host network &amp;amp;amp; localhost&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;378&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;27&quot; value=&quot;&amp;lt;b&amp;gt;🔒 Proxy&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#b8c4d0&amp;quot;&amp;gt;Allow/deny policies · Credential injection&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;320&quot; y=&quot;470&quot; width=&quot;380&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;28&quot; value=&quot;&amp;lt;b&amp;gt;🔑 Secrets&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#64748b&amp;quot;&amp;gt;injected by the proxy&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#e8ebda;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;470&quot; width=&quot;265&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;29&quot; value=&quot;&amp;lt;b&amp;gt;Organization Policies&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#b8c4d0&amp;quot;&amp;gt;Network + File System + MCP&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;34&quot; y=&quot;470&quot; width=&quot;222&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;30&quot; value=&quot;api.github.com&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;330&quot; y=&quot;628&quot; width=&quot;160&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;31&quot; value=&quot;bad.data.org&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;530&quot; y=&quot;628&quot; width=&quot;160&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;/root&gt;&lt;/mxGraphModel&gt;&lt;/diagram&gt;&lt;/mxfile&gt;">
2+ <defs><marker id="arrow-dark" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#15202b"/></marker><marker id="arrow-blue" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#0062FF"/></marker><marker id="arrow-green" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#2e7d32"/></marker><marker id="arrow-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#d92d20"/></marker></defs>
3+ <rect x="20" y="64" width="250" height="150" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
4+ <text x="36" y="90" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Runs on laptop</text>
5+ <text x="36" y="114" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="bold" fill="#15202b">● No Docker Desktop needed</text>
6+ <text x="36" y="138" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Full YOLO mode: no</text>
7+ <text x="36" y="162" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">   permission prompts</text>
8+ <text x="36" y="186" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Agents can run anything</text>
9+ <rect x="20" y="424" width="250" height="156" rx="14" ry="14" fill="none" stroke="#64748b" stroke-width="2"/>
10+ <text x="145" y="447" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="bold" fill="#15202b">Enterprise feature</text>
11+ <rect x="300" y="20" width="800" height="560" rx="14" ry="14" fill="#f3f4ee" stroke="#15202b" stroke-width="2"/>
12+ <text x="320" y="46" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Host System</text>
13+ <rect x="320" y="64" width="380" height="374" rx="12" ry="12" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
14+ <text x="340" y="90" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">🔒 Sandbox VM</text>
15+ <text x="340" y="111" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">hypervisor boundary</text>
16+ <path d="M 702 226 L 750 226" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
17+ <path d="M 702 284 L 750 284" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
18+ <path d="M 702 342 L 750 342" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
19+ <path d="M 702 400 L 750 400" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
20+ <path d="M 813 510 L 702 510" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
21+ <rect x="708.2" y="477.5" width="97.6" height="25" rx="4" fill="#f3f4ee" opacity="0.92"/>
22+ <text x="757" y="490" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="normal" fill="#0062FF">credentials</text>
23+ <path d="M 258 510 L 318 510" fill="none" stroke="#15202b" stroke-width="2" marker-end="url(#arrow-dark)"/>
24+ <path d="M 410 552 L 410 626" fill="none" stroke="#2e7d32" stroke-width="2" marker-end="url(#arrow-green)"/>
25+ <rect x="373.6" y="587.5" width="72.8" height="25" rx="4" fill="#fdfdfb" opacity="0.92"/>
26+ <text x="410" y="600" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="bold" fill="#2e7d32">✓ allow</text>
27+ <path d="M 610 552 L 610 626" fill="none" stroke="#d92d20" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-red)"/>
28+ <rect x="577.8" y="587.5" width="64.4" height="25" rx="4" fill="#fdfdfb" opacity="0.92"/>
29+ <text x="610" y="600" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="bold" fill="#d92d20">✕ deny</text>
30+ <rect x="340" y="130" width="740" height="54" rx="10" ry="10" fill="#80bfbf" stroke="#15202b" stroke-width="2"/>
31+ <text x="710" y="148" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Workspace directory (read-write)</text>
32+ <text x="710" y="169" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#15202b">the one folder mounted from the host</text>
33+ <rect x="340" y="204" width="340" height="44" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
34+ <text x="510" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">Agent process</text>
35+ <rect x="340" y="262" width="340" height="44" rx="10" ry="10" fill="#e8ebda" stroke="#15202b" stroke-width="2"/>
36+ <text x="510" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Private Docker Engine</text>
37+ <rect x="340" y="320" width="340" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
38+ <text x="510" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Installed packages</text>
39+ <rect x="340" y="378" width="340" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
40+ <text x="510" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">VM filesystem</text>
41+ <rect x="752" y="204" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
42+ <text x="767" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
43+ <rect x="752" y="262" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
44+ <text x="767" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
45+ <rect x="752" y="320" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
46+ <text x="767" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
47+ <rect x="752" y="378" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
48+ <text x="767" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
49+ <text x="767" y="436" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#d92d20">blocked</text>
50+ <rect x="815" y="204" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
51+ <text x="947.5" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host filesystem</text>
52+ <rect x="815" y="262" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
53+ <text x="947.5" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host processes &amp; environment</text>
54+ <rect x="815" y="320" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
55+ <text x="947.5" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host Docker Engine</text>
56+ <rect x="815" y="378" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
57+ <text x="947.5" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host network &amp; localhost</text>
58+ <rect x="320" y="470" width="380" height="80" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
59+ <text x="510" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">🔒 Proxy</text>
60+ <text x="510" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#b8c4d0">Allow/deny policies · Credential injection</text>
61+ <rect x="815" y="470" width="265" height="80" rx="10" ry="10" fill="#e8ebda" stroke="#15202b" stroke-width="2"/>
62+ <text x="947.5" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">🔑 Secrets</text>
63+ <text x="947.5" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">injected by the proxy</text>
64+ <rect x="34" y="470" width="222" height="80" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
65+ <text x="145" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">Organization Policies</text>
66+ <text x="145" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#b8c4d0">Network + File System + MCP</text>
67+ <rect x="330" y="628" width="160" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
68+ <text x="410" y="651" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">api.github.com</text>
69+ <rect x="530" y="628" width="160" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
70+ <text x="610" y="651" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">bad.data.org</text>
71+</svg>
new file mode 100644
@@ -0,0 +1,71 @@
1+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1120" height="694" viewBox="0 0 1120 694" content="&lt;mxfile host=&quot;Electron&quot; agent=&quot;make-diagram.mjs&quot; type=&quot;device&quot;&gt;&lt;diagram name=&quot;sbx security model&quot; id=&quot;diagram-1&quot;&gt;&lt;mxGraphModel dx=&quot;1000&quot; dy=&quot;600&quot; grid=&quot;1&quot; gridSize=&quot;10&quot; guides=&quot;1&quot; tooltips=&quot;1&quot; connect=&quot;1&quot; arrows=&quot;1&quot; fold=&quot;1&quot; page=&quot;1&quot; pageScale=&quot;1&quot; pageWidth=&quot;1169&quot; pageHeight=&quot;826&quot; math=&quot;0&quot; shadow=&quot;0&quot;&gt;&lt;root&gt;&lt;mxCell id=&quot;0&quot;/&gt;&lt;mxCell id=&quot;1&quot; parent=&quot;0&quot;/&gt;&lt;mxCell id=&quot;2&quot; value=&quot;● Runs on laptop&amp;lt;br&amp;gt;&amp;lt;b&amp;gt;● No Docker Desktop needed&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;● Full YOLO mode: no&amp;lt;br&amp;gt;   permission prompts&amp;lt;br&amp;gt;● Agents can run anything&quot; style=&quot;rounded=1;arcSize=8;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=15;strokeWidth=2;align=left;verticalAlign=top;spacingLeft=16;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;20&quot; y=&quot;64&quot; width=&quot;250&quot; height=&quot;150&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;3&quot; value=&quot;&quot; style=&quot;rounded=1;arcSize=9;whiteSpace=wrap;html=1;fillColor=none;strokeColor=#64748b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;20&quot; y=&quot;424&quot; width=&quot;250&quot; height=&quot;156&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;4&quot; value=&quot;&amp;lt;b&amp;gt;Host System&amp;lt;/b&amp;gt;&quot; style=&quot;rounded=1;arcSize=3;whiteSpace=wrap;html=1;fillColor=#f3f4ee;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=20;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;300&quot; y=&quot;20&quot; width=&quot;800&quot; height=&quot;560&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;5&quot; value=&quot;&amp;lt;b&amp;gt;🔒 Sandbox VM&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#64748b&amp;quot;&amp;gt;hypervisor boundary&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=3;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;verticalAlign=top;align=left;spacingLeft=20;spacingTop=10;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;320&quot; y=&quot;64&quot; width=&quot;380&quot; height=&quot;374&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;6&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;226&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;226&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;7&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;284&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;284&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;8&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;342&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;342&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;9&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;400&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;750&quot; y=&quot;400&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;10&quot; value=&quot;credentials&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#0062FF;fontSize=14;fontColor=#0062FF;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;813&quot; y=&quot;510&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;702&quot; y=&quot;510&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;11&quot; value=&quot;&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#15202b;fontSize=14;fontColor=#15202b;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;258&quot; y=&quot;510&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;318&quot; y=&quot;510&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;12&quot; value=&quot;✓ allow&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#2e7d32;fontSize=14;fontColor=#2e7d32;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;410&quot; y=&quot;552&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;410&quot; y=&quot;626&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;13&quot; value=&quot;✕ deny&quot; style=&quot;edgeStyle=orthogonalEdgeStyle;rounded=1;html=1;strokeWidth=2;strokeColor=#d92d20;fontSize=14;fontColor=#d92d20;dashed=1;&quot; edge=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry relative=&quot;1&quot; as=&quot;geometry&quot;&gt;&lt;mxPoint x=&quot;610&quot; y=&quot;552&quot; as=&quot;sourcePoint&quot;/&gt;&lt;mxPoint x=&quot;610&quot; y=&quot;626&quot; as=&quot;targetPoint&quot;/&gt;&lt;/mxGeometry&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;14&quot; value=&quot;&amp;lt;b&amp;gt;Workspace directory (read-write)&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#15202b&amp;quot;&amp;gt;the one folder mounted from the host&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=19;whiteSpace=wrap;html=1;fillColor=#80bfbf;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;130&quot; width=&quot;740&quot; height=&quot;54&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;15&quot; value=&quot;Agent process&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;204&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;16&quot; value=&quot;Private Docker Engine&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#e8ebda;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;262&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;17&quot; value=&quot;Installed packages&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;320&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;18&quot; value=&quot;VM filesystem&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;340&quot; y=&quot;378&quot; width=&quot;340&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;19&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;204&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;20&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;262&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;21&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;320&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;22&quot; value=&quot;✕&quot; style=&quot;rounded=1;arcSize=20;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#d92d20;fontColor=#d92d20;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;752&quot; y=&quot;378&quot; width=&quot;30&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;23&quot; value=&quot;Host filesystem&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;204&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;24&quot; value=&quot;Host processes &amp;amp;amp; environment&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;262&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;25&quot; value=&quot;Host Docker Engine&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;320&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;26&quot; value=&quot;Host network &amp;amp;amp; localhost&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;378&quot; width=&quot;265&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;27&quot; value=&quot;&amp;lt;b&amp;gt;🔒 Proxy&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#b8c4d0&amp;quot;&amp;gt;Allow/deny policies · Credential injection&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;320&quot; y=&quot;470&quot; width=&quot;380&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;28&quot; value=&quot;&amp;lt;b&amp;gt;🔑 Secrets&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#64748b&amp;quot;&amp;gt;injected by the proxy&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#e8ebda;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;815&quot; y=&quot;470&quot; width=&quot;265&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;29&quot; value=&quot;&amp;lt;b&amp;gt;Organization Policies&amp;lt;/b&amp;gt;&amp;lt;br&amp;gt;&amp;lt;font style=&amp;quot;font-size:13px;color:#b8c4d0&amp;quot;&amp;gt;Network + File System + MCP&amp;lt;/font&amp;gt;&quot; style=&quot;rounded=1;arcSize=13;whiteSpace=wrap;html=1;fillColor=#15202b;strokeColor=#15202b;fontColor=#fdfdfb;fontSize=17;fontStyle=1;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;34&quot; y=&quot;470&quot; width=&quot;222&quot; height=&quot;80&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;30&quot; value=&quot;api.github.com&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;330&quot; y=&quot;628&quot; width=&quot;160&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;mxCell id=&quot;31&quot; value=&quot;bad.data.org&quot; style=&quot;rounded=1;arcSize=23;whiteSpace=wrap;html=1;fillColor=#fdfdfb;strokeColor=#15202b;fontColor=#15202b;fontSize=17;fontStyle=0;strokeWidth=2;&quot; vertex=&quot;1&quot; parent=&quot;1&quot;&gt;&lt;mxGeometry x=&quot;530&quot; y=&quot;628&quot; width=&quot;160&quot; height=&quot;44&quot; as=&quot;geometry&quot;/&gt;&lt;/mxCell&gt;&lt;/root&gt;&lt;/mxGraphModel&gt;&lt;/diagram&gt;&lt;/mxfile&gt;">
2+ <defs><marker id="arrow-dark" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#15202b"/></marker><marker id="arrow-blue" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#0062FF"/></marker><marker id="arrow-green" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#2e7d32"/></marker><marker id="arrow-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 0 0 L 10 5 L 0 10 z" fill="#d92d20"/></marker></defs>
3+ <rect x="20" y="64" width="250" height="150" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
4+ <text x="36" y="90" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Runs on laptop</text>
5+ <text x="36" y="114" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="bold" fill="#15202b">● No Docker Desktop needed</text>
6+ <text x="36" y="138" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Full YOLO mode: no</text>
7+ <text x="36" y="162" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">   permission prompts</text>
8+ <text x="36" y="186" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="normal" fill="#15202b">● Agents can run anything</text>
9+ <rect x="20" y="424" width="250" height="156" rx="14" ry="14" fill="none" stroke="#64748b" stroke-width="2"/>
10+ <text x="145" y="447" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="15" font-weight="bold" fill="#15202b">Enterprise feature</text>
11+ <rect x="300" y="20" width="800" height="560" rx="14" ry="14" fill="#f3f4ee" stroke="#15202b" stroke-width="2"/>
12+ <text x="320" y="46" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Host System</text>
13+ <rect x="320" y="64" width="380" height="374" rx="12" ry="12" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
14+ <text x="340" y="90" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">🔒 Sandbox VM</text>
15+ <text x="340" y="111" text-anchor="start" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">hypervisor boundary</text>
16+ <path d="M 702 226 L 750 226" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
17+ <path d="M 702 284 L 750 284" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
18+ <path d="M 702 342 L 750 342" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
19+ <path d="M 702 400 L 750 400" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
20+ <path d="M 813 510 L 702 510" fill="none" stroke="#0062FF" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-blue)"/>
21+ <rect x="708.2" y="477.5" width="97.6" height="25" rx="4" fill="#f3f4ee" opacity="0.92"/>
22+ <text x="757" y="490" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="normal" fill="#0062FF">credentials</text>
23+ <path d="M 258 510 L 318 510" fill="none" stroke="#15202b" stroke-width="2" marker-end="url(#arrow-dark)"/>
24+ <path d="M 410 552 L 410 626" fill="none" stroke="#2e7d32" stroke-width="2" marker-end="url(#arrow-green)"/>
25+ <rect x="373.6" y="587.5" width="72.8" height="25" rx="4" fill="#fdfdfb" opacity="0.92"/>
26+ <text x="410" y="600" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="bold" fill="#2e7d32">✓ allow</text>
27+ <path d="M 610 552 L 610 626" fill="none" stroke="#d92d20" stroke-width="2" stroke-dasharray="6 5" marker-end="url(#arrow-red)"/>
28+ <rect x="577.8" y="587.5" width="64.4" height="25" rx="4" fill="#fdfdfb" opacity="0.92"/>
29+ <text x="610" y="600" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="14" font-weight="bold" fill="#d92d20">✕ deny</text>
30+ <rect x="340" y="130" width="740" height="54" rx="10" ry="10" fill="#80bfbf" stroke="#15202b" stroke-width="2"/>
31+ <text x="710" y="148" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Workspace directory (read-write)</text>
32+ <text x="710" y="169" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#15202b">the one folder mounted from the host</text>
33+ <rect x="340" y="204" width="340" height="44" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
34+ <text x="510" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">Agent process</text>
35+ <rect x="340" y="262" width="340" height="44" rx="10" ry="10" fill="#e8ebda" stroke="#15202b" stroke-width="2"/>
36+ <text x="510" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">Private Docker Engine</text>
37+ <rect x="340" y="320" width="340" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
38+ <text x="510" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Installed packages</text>
39+ <rect x="340" y="378" width="340" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
40+ <text x="510" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">VM filesystem</text>
41+ <rect x="752" y="204" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
42+ <text x="767" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
43+ <rect x="752" y="262" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
44+ <text x="767" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
45+ <rect x="752" y="320" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
46+ <text x="767" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
47+ <rect x="752" y="378" width="30" height="44" rx="6" ry="6" fill="#fdfdfb" stroke="#d92d20" stroke-width="2"/>
48+ <text x="767" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#d92d20">✕</text>
49+ <text x="767" y="436" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#d92d20">blocked</text>
50+ <rect x="815" y="204" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
51+ <text x="947.5" y="227" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host filesystem</text>
52+ <rect x="815" y="262" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
53+ <text x="947.5" y="285" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host processes &amp; environment</text>
54+ <rect x="815" y="320" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
55+ <text x="947.5" y="343" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host Docker Engine</text>
56+ <rect x="815" y="378" width="265" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
57+ <text x="947.5" y="401" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">Host network &amp; localhost</text>
58+ <rect x="320" y="470" width="380" height="80" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
59+ <text x="510" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">🔒 Proxy</text>
60+ <text x="510" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#b8c4d0">Allow/deny policies · Credential injection</text>
61+ <rect x="815" y="470" width="265" height="80" rx="10" ry="10" fill="#e8ebda" stroke="#15202b" stroke-width="2"/>
62+ <text x="947.5" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#15202b">🔑 Secrets</text>
63+ <text x="947.5" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#64748b">injected by the proxy</text>
64+ <rect x="34" y="470" width="222" height="80" rx="10" ry="10" fill="#15202b" stroke="#15202b" stroke-width="2"/>
65+ <text x="145" y="501" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="bold" fill="#fdfdfb">Organization Policies</text>
66+ <text x="145" y="522" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="13" font-weight="normal" fill="#b8c4d0">Network + File System + MCP</text>
67+ <rect x="330" y="628" width="160" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
68+ <text x="410" y="651" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">api.github.com</text>
69+ <rect x="530" y="628" width="160" height="44" rx="10" ry="10" fill="#fdfdfb" stroke="#15202b" stroke-width="2"/>
70+ <text x="610" y="651" text-anchor="middle" dominant-baseline="central" font-family="Helvetica Neue, Helvetica, Arial, sans-serif" font-size="17" font-weight="normal" fill="#15202b">bad.data.org</text>
71+</svg>
added 05-sbx-docker-agent/README.md +51 -0
new file mode 100644
@@ -0,0 +1,51 @@
1+# I want Docker Agent sandboxed and connected to Mistral.ai
2+
3+Today Docker Agent does not automatically recognize the mistral secret (`sbx secret set mistral`), unlike Mistral's **Vibe** CLI. So we are going to use a "custom secret".
4+
5+## Custom secret and `MISTRAL_API_KEY`
6+
7+1. Create a `sbx.env` file:
8+ ```
9+ DOCKER_AGENT_AUTO_UPDATE=0
10+ MISTRAL_API_KEY=sbx-mistral-key
11+ ```
12+2. Create a custom secret with a **fixed placeholder value** that you choose:
13+ ```bash
14+ export MISTRAL_API_KEY="your-api-key"
15+ sbx secret set-custom --host api.mistral.ai --env MISTRAL_API_KEY \
16+ --placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
17+ ```
18+
19+ 🖐️ **on Windows:**
20+ ```bash
21+ $env:MISTRAL_API_KEY="your-api-key"
22+
23+ sbx secret set-custom --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
24+ ```
25+
26+3. Start the sandbox:
27+ ```bash
28+ sbx run --env-file sbx.env docker-agent -- run agent.yaml .
29+ ```
30+ > dont forget the `.`
31+
32+Or without the `sbx.env` file:
33+
34+```bash
35+sbx run -e MISTRAL_API_KEY=sbx-mistral-key -e DOCKER_AGENT_AUTO_UPDATE=0 docker-agent -- run agent.yaml .
36+```
37+
38+![docker-agent](./assets/docker-agent-01.png)
39+
40+
41+
42+## And try again
43+
44+```raw
45+Create a text file at the home of my machine with a greeting message
46+```
47+
48+![docker-agent](./assets/docker-agent-02.png)
49+
50+
51+Also try to write or read files outside of this directory.
new file mode 100644
@@ -0,0 +1,51 @@
1+# I want Docker Agent sandboxed and connected to Mistral.ai
2+
3+Today Docker Agent does not automatically recognize the mistral secret (`sbx secret set mistral`), unlike Mistral's **Vibe** CLI. So we are going to use a "custom secret".
4+
5+## Custom secret and `MISTRAL_API_KEY`
6+
7+1. Create a `sbx.env` file:
8+ ```
9+ DOCKER_AGENT_AUTO_UPDATE=0
10+ MISTRAL_API_KEY=sbx-mistral-key
11+ ```
12+2. Create a custom secret with a **fixed placeholder value** that you choose:
13+ ```bash
14+ export MISTRAL_API_KEY="your-api-key"
15+ sbx secret set-custom --host api.mistral.ai --env MISTRAL_API_KEY \
16+ --placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
17+ ```
18+
19+ 🖐️ **on Windows:**
20+ ```bash
21+ $env:MISTRAL_API_KEY="your-api-key"
22+
23+ sbx secret set-custom --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
24+ ```
25+
26+3. Start the sandbox:
27+ ```bash
28+ sbx run --env-file sbx.env docker-agent -- run agent.yaml .
29+ ```
30+ > dont forget the `.`
31+
32+Or without the `sbx.env` file:
33+
34+```bash
35+sbx run -e MISTRAL_API_KEY=sbx-mistral-key -e DOCKER_AGENT_AUTO_UPDATE=0 docker-agent -- run agent.yaml .
36+```
37+
38+![docker-agent](./assets/docker-agent-01.png)
39+
40+
41+
42+## And try again
43+
44+```raw
45+Create a text file at the home of my machine with a greeting message
46+```
47+
48+![docker-agent](./assets/docker-agent-02.png)
49+
50+
51+Also try to write or read files outside of this directory.
added 05-sbx-docker-agent/agent.yaml +20 -0
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
added 05-sbx-docker-agent/assets/docker-agent-01.png +0 -0
new file mode 100644
Binary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-01.png differ
new file mode 100644
Binary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-01.png differBinary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-01.png differ
added 05-sbx-docker-agent/assets/docker-agent-02.png +0 -0
new file mode 100644
Binary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-02.png differ
new file mode 100644
Binary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-02.png differBinary files /dev/null and b/05-sbx-docker-agent/assets/docker-agent-02.png differ
added 05-sbx-docker-agent/sbx.env +2 -0
new file mode 100644
@@ -0,0 +1,2 @@
1+DOCKER_AGENT_AUTO_UPDATE=0
2+MISTRAL_API_KEY=sbx-mistral-key
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,2 @@
1+DOCKER_AGENT_AUTO_UPDATE=0
2+MISTRAL_API_KEY=sbx-mistral-key
\ No newline at end of file\ No newline at end of file
added 06-sbx-docker-agent-sbx-env/README.md +58 -0
new file mode 100644
@@ -0,0 +1,58 @@
1+# Docker Agent sandboxed and connected to Mistral.ai, with `sbx env`
2+
3+Same goal as the previous step, but the sandbox is described declaratively in an `sbxenv.yaml` file instead of a long `sbx run` command line.
4+
5+## The `sbxenv.yaml` file
6+
7+```yaml
8+schemaVersion: "1"
9+name: docker-agent-mistral
10+agent: docker-agent
11+workspace: .
12+env:
13+ DOCKER_AGENT_AUTO_UPDATE: "0"
14+ MISTRAL_API_KEY: sbx-mistral-key
15+```
16+
17+- `name`: name of the sandbox (and the "scope" of the secrets associated with it).
18+- `agent`: the base agent to use.
19+- `workspace`: the directory mounted in the sandbox (`.` = the folder of the file).
20+- `env`: the environment variables of the sandbox (replaces `sbx.env`). `MISTRAL_API_KEY` only contains the **placeholder value**, never the real key.
21+
22+## Custom secret
23+
24+`sbxenv.yaml` cannot declare a custom secret. So we create it beforehand, **attaching it to the sandbox** (the `name` of the file) so that `sbx env rm` removes it:
25+
26+```bash
27+export MISTRAL_API_KEY="your-api-key"
28+sbx secret set-custom --sandbox docker-agent-mistral \
29+--host api.mistral.ai --env MISTRAL_API_KEY \
30+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
31+```
32+
33+🖐️ **on Windows:**
34+```bash
35+$env:MISTRAL_API_KEY="your-api-key"
36+
37+sbx secret set-custom --sandbox docker-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
38+```
39+
40+
41+## Start the sandbox
42+
43+`sbx env run` cannot pass arguments to the agent (no `-- run agent.yaml`): Docker Agent would be started with its default arguments (`run --yolo --agent-picker`), so not necessarily with `agent.yaml` and Mistral. We create the sandbox, then start Docker Agent with `agent.yaml` through `sbx env exec`:
44+
45+```bash
46+sbx env create
47+sbx env exec -it -- docker-agent run agent.yaml
48+```
49+
50+`sbx env` shows the plan (what is going to be created) and asks for approval; `--force` accepts it without asking. `sbx env exec` also applies the `env:` of `sbxenv.yaml`.
51+
52+Other useful commands:
53+
54+```bash
55+sbx env plan # preview the changes
56+sbx env run # create (if needed) and attach with the default agent
57+sbx env rm # remove the sandbox and its secrets
58+```
new file mode 100644
@@ -0,0 +1,58 @@
1+# Docker Agent sandboxed and connected to Mistral.ai, with `sbx env`
2+
3+Same goal as the previous step, but the sandbox is described declaratively in an `sbxenv.yaml` file instead of a long `sbx run` command line.
4+
5+## The `sbxenv.yaml` file
6+
7+```yaml
8+schemaVersion: "1"
9+name: docker-agent-mistral
10+agent: docker-agent
11+workspace: .
12+env:
13+ DOCKER_AGENT_AUTO_UPDATE: "0"
14+ MISTRAL_API_KEY: sbx-mistral-key
15+```
16+
17+- `name`: name of the sandbox (and the "scope" of the secrets associated with it).
18+- `agent`: the base agent to use.
19+- `workspace`: the directory mounted in the sandbox (`.` = the folder of the file).
20+- `env`: the environment variables of the sandbox (replaces `sbx.env`). `MISTRAL_API_KEY` only contains the **placeholder value**, never the real key.
21+
22+## Custom secret
23+
24+`sbxenv.yaml` cannot declare a custom secret. So we create it beforehand, **attaching it to the sandbox** (the `name` of the file) so that `sbx env rm` removes it:
25+
26+```bash
27+export MISTRAL_API_KEY="your-api-key"
28+sbx secret set-custom --sandbox docker-agent-mistral \
29+--host api.mistral.ai --env MISTRAL_API_KEY \
30+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
31+```
32+
33+🖐️ **on Windows:**
34+```bash
35+$env:MISTRAL_API_KEY="your-api-key"
36+
37+sbx secret set-custom --sandbox docker-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
38+```
39+
40+
41+## Start the sandbox
42+
43+`sbx env run` cannot pass arguments to the agent (no `-- run agent.yaml`): Docker Agent would be started with its default arguments (`run --yolo --agent-picker`), so not necessarily with `agent.yaml` and Mistral. We create the sandbox, then start Docker Agent with `agent.yaml` through `sbx env exec`:
44+
45+```bash
46+sbx env create
47+sbx env exec -it -- docker-agent run agent.yaml
48+```
49+
50+`sbx env` shows the plan (what is going to be created) and asks for approval; `--force` accepts it without asking. `sbx env exec` also applies the `env:` of `sbxenv.yaml`.
51+
52+Other useful commands:
53+
54+```bash
55+sbx env plan # preview the changes
56+sbx env run # create (if needed) and attach with the default agent
57+sbx env rm # remove the sandbox and its secrets
58+```
added 06-sbx-docker-agent-sbx-env/agent.yaml +20 -0
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
added 06-sbx-docker-agent-sbx-env/sbxenv.yaml +7 -0
new file mode 100644
@@ -0,0 +1,7 @@
1+schemaVersion: "1"
2+name: docker-agent-mistral
3+agent: docker-agent
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
new file mode 100644
@@ -0,0 +1,7 @@
1+schemaVersion: "1"
2+name: docker-agent-mistral
3+agent: docker-agent
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
added 07-play-lego-with-kits/.gitattributes +1 -0
new file mode 100644
@@ -0,0 +1 @@
1+kits/** text eol=lf
new file mode 100644
@@ -0,0 +1 @@
1+kits/** text eol=lf
added 07-play-lego-with-kits/README.md +96 -0
new file mode 100644
@@ -0,0 +1,96 @@
1+# Playing Lego with kits: Docker Agent + Rust
2+
3+A **kit** is a reusable brick to compose a sandbox. Here, we assemble two kits in `sbxenv.yaml`:
4+
5+| Kit | Role |
6+|-----|------|
7+| `kits/docker-agent-mistral` | the **workload** (the base): Docker Agent, started directly on `agent.yaml`, with access to the Mistral API |
8+| `kits/rust-toolchain` | a **mixin**: the Rust toolchain (`cargo`, `rustc`, `rustfmt`, `clippy`, `rust-analyzer`) |
9+
10+A set of v3 kits contains **exactly one workload** and as many mixins as you want. A single v3 mixin combined with the built-in `docker-agent` agent (which is a v2 kit) is refused: `no workload kit in the set`.
11+
12+## The `sbxenv.yaml` file
13+
14+```yaml
15+schemaVersion: "1"
16+name: docker-rust-agent-mistral
17+agent: docker-agent-mistral
18+workspace: .
19+env:
20+ DOCKER_AGENT_AUTO_UPDATE: "0"
21+ MISTRAL_API_KEY: sbx-mistral-key
22+
23+kits:
24+ - ./kits/docker-agent-mistral
25+ - ./kits/rust-toolchain
26+```
27+
28+- `agent`: must carry the name of the workload kit (the file name `docker-agent-mistral.yaml`).
29+- `kits`: the workload, then the mixins. Relative paths are resolved against this file.
30+- `env`: `MISTRAL_API_KEY` only contains the **placeholder value**, never the real key.
31+
32+## The workload kit: `docker-agent-mistral`
33+
34+- `docker-agent-mistral.dockerfile` starts from the `docker/sandbox-templates:docker-agent-docker` image and defines the launch command (`ENTRYPOINT`/`CMD`): `docker-agent run agent.yaml`.
35+- `docker-agent-mistral.yaml` declares `kind: workload` and allows outbound traffic to `api.mistral.ai`.
36+
37+## The mixin kit: `rust-toolchain`
38+
39+- `rust-toolchain.dockerfile` builds a layer with the Rust toolchain and the linker (`gcc`); it is only added on top of the image.
40+- `rust-toolchain.yaml` declares `kind: mixin`, the `rust_version` argument (default `1.99.0`) and allows outbound traffic to crates.io.
41+
42+To change the Rust version without modifying the files:
43+
44+```bash
45+sbx env create --kit-arg rust_version=stable
46+```
47+
48+## Custom secret
49+
50+`sbxenv.yaml` cannot declare a custom secret: we create it beforehand. With `--sandbox`, it is removed along with the sandbox by `sbx env rm`:
51+
52+```bash
53+export MISTRAL_API_KEY="your-api-key"
54+sbx secret set-custom --sandbox docker-rust-agent-mistral \
55+--host api.mistral.ai --env MISTRAL_API_KEY \
56+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
57+```
58+
59+🖐️ **on Windows:**
60+```bash
61+$env:MISTRAL_API_KEY="your-api-key"
62+
63+sbx secret set-custom --sandbox docker-rust-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
64+```
65+
66+Without `--sandbox`, the secret is global and works too.
67+
68+## Start the sandbox
69+
70+```bash
71+sbx env create
72+sbx env exec -it -- docker-agent run agent.yaml
73+```
74+> 🖐️ **you need Docker to build a kit**
75+
76+`sbx env` shows the plan (what is going to be created) and asks for approval; `--force` accepts it without asking. The first creation builds the two kits, which takes a little time.
77+
78+Other useful commands:
79+
80+```bash
81+sbx env plan # preview the changes
82+sbx env rm # remove the sandbox and its secrets
83+```
84+
85+## And try
86+
87+```raw
88+What is the version of cargo?
89+```
90+
91+
92+```raw
93+Create a new Rust project with cargo that prints a greeting, then build and run it
94+```
95+
96+The agent can now use `cargo build`, `cargo test` or `cargo clippy` in the sandbox.
new file mode 100644
@@ -0,0 +1,96 @@
1+# Playing Lego with kits: Docker Agent + Rust
2+
3+A **kit** is a reusable brick to compose a sandbox. Here, we assemble two kits in `sbxenv.yaml`:
4+
5+| Kit | Role |
6+|-----|------|
7+| `kits/docker-agent-mistral` | the **workload** (the base): Docker Agent, started directly on `agent.yaml`, with access to the Mistral API |
8+| `kits/rust-toolchain` | a **mixin**: the Rust toolchain (`cargo`, `rustc`, `rustfmt`, `clippy`, `rust-analyzer`) |
9+
10+A set of v3 kits contains **exactly one workload** and as many mixins as you want. A single v3 mixin combined with the built-in `docker-agent` agent (which is a v2 kit) is refused: `no workload kit in the set`.
11+
12+## The `sbxenv.yaml` file
13+
14+```yaml
15+schemaVersion: "1"
16+name: docker-rust-agent-mistral
17+agent: docker-agent-mistral
18+workspace: .
19+env:
20+ DOCKER_AGENT_AUTO_UPDATE: "0"
21+ MISTRAL_API_KEY: sbx-mistral-key
22+
23+kits:
24+ - ./kits/docker-agent-mistral
25+ - ./kits/rust-toolchain
26+```
27+
28+- `agent`: must carry the name of the workload kit (the file name `docker-agent-mistral.yaml`).
29+- `kits`: the workload, then the mixins. Relative paths are resolved against this file.
30+- `env`: `MISTRAL_API_KEY` only contains the **placeholder value**, never the real key.
31+
32+## The workload kit: `docker-agent-mistral`
33+
34+- `docker-agent-mistral.dockerfile` starts from the `docker/sandbox-templates:docker-agent-docker` image and defines the launch command (`ENTRYPOINT`/`CMD`): `docker-agent run agent.yaml`.
35+- `docker-agent-mistral.yaml` declares `kind: workload` and allows outbound traffic to `api.mistral.ai`.
36+
37+## The mixin kit: `rust-toolchain`
38+
39+- `rust-toolchain.dockerfile` builds a layer with the Rust toolchain and the linker (`gcc`); it is only added on top of the image.
40+- `rust-toolchain.yaml` declares `kind: mixin`, the `rust_version` argument (default `1.99.0`) and allows outbound traffic to crates.io.
41+
42+To change the Rust version without modifying the files:
43+
44+```bash
45+sbx env create --kit-arg rust_version=stable
46+```
47+
48+## Custom secret
49+
50+`sbxenv.yaml` cannot declare a custom secret: we create it beforehand. With `--sandbox`, it is removed along with the sandbox by `sbx env rm`:
51+
52+```bash
53+export MISTRAL_API_KEY="your-api-key"
54+sbx secret set-custom --sandbox docker-rust-agent-mistral \
55+--host api.mistral.ai --env MISTRAL_API_KEY \
56+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
57+```
58+
59+🖐️ **on Windows:**
60+```bash
61+$env:MISTRAL_API_KEY="your-api-key"
62+
63+sbx secret set-custom --sandbox docker-rust-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
64+```
65+
66+Without `--sandbox`, the secret is global and works too.
67+
68+## Start the sandbox
69+
70+```bash
71+sbx env create
72+sbx env exec -it -- docker-agent run agent.yaml
73+```
74+> 🖐️ **you need Docker to build a kit**
75+
76+`sbx env` shows the plan (what is going to be created) and asks for approval; `--force` accepts it without asking. The first creation builds the two kits, which takes a little time.
77+
78+Other useful commands:
79+
80+```bash
81+sbx env plan # preview the changes
82+sbx env rm # remove the sandbox and its secrets
83+```
84+
85+## And try
86+
87+```raw
88+What is the version of cargo?
89+```
90+
91+
92+```raw
93+Create a new Rust project with cargo that prints a greeting, then build and run it
94+```
95+
96+The agent can now use `cargo build`, `cargo test` or `cargo clippy` in the sandbox.
added 07-play-lego-with-kits/agent.yaml +20 -0
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
new file mode 100644
@@ -0,0 +1,20 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
added 07-play-lego-with-kits/kits/docker-agent-mistral/docker-agent-mistral.dockerfile +6 -0
new file mode 100644
@@ -0,0 +1,6 @@
1+# syntax=docker/dockerfile:1
2+FROM docker/sandbox-templates:docker-agent-docker
3+
4+# The sandbox launches the agent from the image config: Docker Agent on agent.yaml.
5+ENTRYPOINT ["docker-agent"]
6+CMD ["run", "agent.yaml"]
new file mode 100644
@@ -0,0 +1,6 @@
1+# syntax=docker/dockerfile:1
2+FROM docker/sandbox-templates:docker-agent-docker
3+
4+# The sandbox launches the agent from the image config: Docker Agent on agent.yaml.
5+ENTRYPOINT ["docker-agent"]
6+CMD ["run", "agent.yaml"]
added 07-play-lego-with-kits/kits/docker-agent-mistral/docker-agent-mistral.yaml +13 -0
new file mode 100644
@@ -0,0 +1,13 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: workload
4+displayName: Docker Agent (Mistral)
5+description: Docker Agent launched on agent.yaml, with access to the Mistral API
6+
7+capabilities:
8+ - type: com.docker.sandbox/sbx@1
9+ - type: com.docker.sandbox/network-policy@1
10+ config:
11+ runtime:
12+ allow:
13+ - api.mistral.ai
new file mode 100644
@@ -0,0 +1,13 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: workload
4+displayName: Docker Agent (Mistral)
5+description: Docker Agent launched on agent.yaml, with access to the Mistral API
6+
7+capabilities:
8+ - type: com.docker.sandbox/sbx@1
9+ - type: com.docker.sandbox/network-policy@1
10+ config:
11+ runtime:
12+ allow:
13+ - api.mistral.ai
added 07-play-lego-with-kits/kits/go-toolchain/go-toolchain.dockerfile +40 -0
new file mode 100644
@@ -0,0 +1,40 @@
1+# syntax=docker/dockerfile:1
2+
3+# --- build stage: download Go, build gopls with that same Go -----------------
4+FROM debian:trixie-slim AS build
5+ARG GO_VERSION
6+ARG GOPLS_VERSION=latest
7+ARG TARGETARCH
8+
9+# A heredoc RUN does not stop at the first failing line by itself: every
10+# script starts with `set -eu`.
11+RUN <<EOF
12+set -eu
13+apt-get update
14+apt-get install -y --no-install-recommends curl ca-certificates
15+rm -rf /var/lib/apt/lists/*
16+EOF
17+
18+RUN <<EOF
19+set -eu
20+curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" \
21+ | tar -C /usr/local -xz
22+rm -rf /usr/local/go/test
23+EOF
24+
25+# Static gopls, installed straight into the overlay tree.
26+ENV PATH=/usr/local/go/bin:${PATH} \
27+ CGO_ENABLED=0 \
28+ GOBIN=/out/usr/local/bin \
29+ GOFLAGS=-trimpath
30+RUN <<EOF
31+set -eu
32+go install "golang.org/x/tools/gopls@${GOPLS_VERSION}"
33+ln -s /usr/local/go/bin/go /out/usr/local/bin/go
34+ln -s /usr/local/go/bin/gofmt /out/usr/local/bin/gofmt
35+EOF
36+
37+# --- overlay: only the toolchain and the binaries land on the workload -------
38+FROM scratch
39+COPY --from=build /usr/local/go /usr/local/go
40+COPY --from=build /out/ /
new file mode 100644
@@ -0,0 +1,40 @@
1+# syntax=docker/dockerfile:1
2+
3+# --- build stage: download Go, build gopls with that same Go -----------------
4+FROM debian:trixie-slim AS build
5+ARG GO_VERSION
6+ARG GOPLS_VERSION=latest
7+ARG TARGETARCH
8+
9+# A heredoc RUN does not stop at the first failing line by itself: every
10+# script starts with `set -eu`.
11+RUN <<EOF
12+set -eu
13+apt-get update
14+apt-get install -y --no-install-recommends curl ca-certificates
15+rm -rf /var/lib/apt/lists/*
16+EOF
17+
18+RUN <<EOF
19+set -eu
20+curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" \
21+ | tar -C /usr/local -xz
22+rm -rf /usr/local/go/test
23+EOF
24+
25+# Static gopls, installed straight into the overlay tree.
26+ENV PATH=/usr/local/go/bin:${PATH} \
27+ CGO_ENABLED=0 \
28+ GOBIN=/out/usr/local/bin \
29+ GOFLAGS=-trimpath
30+RUN <<EOF
31+set -eu
32+go install "golang.org/x/tools/gopls@${GOPLS_VERSION}"
33+ln -s /usr/local/go/bin/go /out/usr/local/bin/go
34+ln -s /usr/local/go/bin/gofmt /out/usr/local/bin/gofmt
35+EOF
36+
37+# --- overlay: only the toolchain and the binaries land on the workload -------
38+FROM scratch
39+COPY --from=build /usr/local/go /usr/local/go
40+COPY --from=build /out/ /
added 07-play-lego-with-kits/kits/go-toolchain/go-toolchain.yaml +33 -0
new file mode 100644
@@ -0,0 +1,33 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Go toolchain
5+description: Go toolchain and gopls (language server)
6+licenses: [MIT]
7+version: "0.6.0"
8+
9+args:
10+ go_version:
11+ default: "1.27.2" # mm requires Go 1.26+
12+ pattern: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
13+ buildArg: GO_VERSION
14+ gopls_version:
15+ # Pin a tag (e.g. v0.x.y) for reproducible builds.
16+ default: latest
17+ pattern: '^(latest|v[0-9]+\.[0-9]+\.[0-9]+)$'
18+ buildArg: GOPLS_VERSION
19+
20+provides: ["go@${{ kit.args.go_version }}"]
21+
22+capabilities:
23+ # Runtime access with the tool: module downloads and checksum DB.
24+ # Add private module hosts here (or in a kit set) if you need them.
25+ - type: com.docker.sandbox/network-policy@1
26+ config:
27+ runtime:
28+ allow:
29+ - proxy.golang.org
30+ - sum.golang.org
31+ - github.com
32+ - golang.org
33+
new file mode 100644
@@ -0,0 +1,33 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Go toolchain
5+description: Go toolchain and gopls (language server)
6+licenses: [MIT]
7+version: "0.6.0"
8+
9+args:
10+ go_version:
11+ default: "1.27.2" # mm requires Go 1.26+
12+ pattern: '^[0-9]+\.[0-9]+(\.[0-9]+)?$'
13+ buildArg: GO_VERSION
14+ gopls_version:
15+ # Pin a tag (e.g. v0.x.y) for reproducible builds.
16+ default: latest
17+ pattern: '^(latest|v[0-9]+\.[0-9]+\.[0-9]+)$'
18+ buildArg: GOPLS_VERSION
19+
20+provides: ["go@${{ kit.args.go_version }}"]
21+
22+capabilities:
23+ # Runtime access with the tool: module downloads and checksum DB.
24+ # Add private module hosts here (or in a kit set) if you need them.
25+ - type: com.docker.sandbox/network-policy@1
26+ config:
27+ runtime:
28+ allow:
29+ - proxy.golang.org
30+ - sum.golang.org
31+ - github.com
32+ - golang.org
33+
added 07-play-lego-with-kits/kits/rust-toolchain/rust-toolchain.dockerfile +70 -0
new file mode 100644
@@ -0,0 +1,70 @@
1+# syntax=docker/dockerfile:1
2+
3+# --- build stage: the Rust toolchain, through rustup ------------------------
4+FROM debian:trixie-slim AS rust
5+ARG RUST_VERSION
6+
7+# A heredoc RUN does not stop at the first failing line by itself: every
8+# script starts with `set -eu`.
9+RUN <<EOF
10+set -eu
11+apt-get update
12+apt-get install -y --no-install-recommends curl ca-certificates
13+rm -rf /var/lib/apt/lists/*
14+EOF
15+
16+# rust-src is what rust-analyzer reads the standard library from.
17+ENV RUSTUP_HOME=/usr/local/rustup \
18+ CARGO_HOME=/usr/local/cargo
19+RUN <<EOF
20+set -eu
21+curl -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path \
22+ --profile minimal \
23+ --default-toolchain "${RUST_VERSION}" \
24+ --component rustfmt,clippy,rust-analyzer,rust-src
25+EOF
26+
27+# The workload has no RUSTUP_HOME to find a toolchain with, so the rustup
28+# proxies would not work there: link the toolchain's own binaries instead.
29+# rustc finds its sysroot from its real path, through the links.
30+RUN <<EOF
31+set -eu
32+toolchain="$(ls -d /usr/local/rustup/toolchains/*)"
33+mkdir -p /out/usr/local/bin
34+for bin in cargo rustc rustdoc rustfmt cargo-fmt cargo-clippy clippy-driver rust-analyzer; do
35+ ln -s "${toolchain}/bin/${bin}" "/out/usr/local/bin/${bin}"
36+done
37+# Kept in the overlay at the same path: the links point into it.
38+mkdir -p /out/usr/local/rustup/toolchains
39+mv "${toolchain}" /out/usr/local/rustup/toolchains/
40+EOF
41+
42+# --- linker stage: what the workload lacks to link, from its own distro ------
43+# rustc links through `cc`, and build scripts (the cc crate) compile C: the
44+# older shell templates have neither gcc nor binutils nor the libc headers. The .deb
45+# files apt would add to *that* image are unpacked into the overlay, so they
46+# match its Ubuntu release and the libraries already installed.
47+FROM docker/sandbox-templates:shell AS linker
48+USER root
49+RUN <<EOF
50+set -eu
51+apt-get update
52+apt-get install -y --no-install-recommends --download-only gcc libc6-dev
53+mkdir -p /out
54+# Newer shell templates already ship gcc and libc6-dev: then apt downloads
55+# nothing, the glob stays unexpanded and the overlay adds nothing.
56+for deb in /var/cache/apt/archives/*.deb; do
57+ [ -e "${deb}" ] || continue
58+ dpkg-deb -x "${deb}" /out
59+done
60+# Set by update-alternatives in a real install, which unpacking skips.
61+if [ -e /out/usr/bin/gcc ]; then
62+ ln -sf gcc /out/usr/bin/cc
63+fi
64+rm -rf /out/usr/share/doc /out/usr/share/man /out/usr/share/info /out/usr/share/locale
65+EOF
66+
67+# --- overlay: only the toolchain, the links and the C toolchain land ---------
68+FROM scratch
69+COPY --from=linker /out/ /
70+COPY --from=rust /out/ /
new file mode 100644
@@ -0,0 +1,70 @@
1+# syntax=docker/dockerfile:1
2+
3+# --- build stage: the Rust toolchain, through rustup ------------------------
4+FROM debian:trixie-slim AS rust
5+ARG RUST_VERSION
6+
7+# A heredoc RUN does not stop at the first failing line by itself: every
8+# script starts with `set -eu`.
9+RUN <<EOF
10+set -eu
11+apt-get update
12+apt-get install -y --no-install-recommends curl ca-certificates
13+rm -rf /var/lib/apt/lists/*
14+EOF
15+
16+# rust-src is what rust-analyzer reads the standard library from.
17+ENV RUSTUP_HOME=/usr/local/rustup \
18+ CARGO_HOME=/usr/local/cargo
19+RUN <<EOF
20+set -eu
21+curl -fsSL https://sh.rustup.rs | sh -s -- -y --no-modify-path \
22+ --profile minimal \
23+ --default-toolchain "${RUST_VERSION}" \
24+ --component rustfmt,clippy,rust-analyzer,rust-src
25+EOF
26+
27+# The workload has no RUSTUP_HOME to find a toolchain with, so the rustup
28+# proxies would not work there: link the toolchain's own binaries instead.
29+# rustc finds its sysroot from its real path, through the links.
30+RUN <<EOF
31+set -eu
32+toolchain="$(ls -d /usr/local/rustup/toolchains/*)"
33+mkdir -p /out/usr/local/bin
34+for bin in cargo rustc rustdoc rustfmt cargo-fmt cargo-clippy clippy-driver rust-analyzer; do
35+ ln -s "${toolchain}/bin/${bin}" "/out/usr/local/bin/${bin}"
36+done
37+# Kept in the overlay at the same path: the links point into it.
38+mkdir -p /out/usr/local/rustup/toolchains
39+mv "${toolchain}" /out/usr/local/rustup/toolchains/
40+EOF
41+
42+# --- linker stage: what the workload lacks to link, from its own distro ------
43+# rustc links through `cc`, and build scripts (the cc crate) compile C: the
44+# older shell templates have neither gcc nor binutils nor the libc headers. The .deb
45+# files apt would add to *that* image are unpacked into the overlay, so they
46+# match its Ubuntu release and the libraries already installed.
47+FROM docker/sandbox-templates:shell AS linker
48+USER root
49+RUN <<EOF
50+set -eu
51+apt-get update
52+apt-get install -y --no-install-recommends --download-only gcc libc6-dev
53+mkdir -p /out
54+# Newer shell templates already ship gcc and libc6-dev: then apt downloads
55+# nothing, the glob stays unexpanded and the overlay adds nothing.
56+for deb in /var/cache/apt/archives/*.deb; do
57+ [ -e "${deb}" ] || continue
58+ dpkg-deb -x "${deb}" /out
59+done
60+# Set by update-alternatives in a real install, which unpacking skips.
61+if [ -e /out/usr/bin/gcc ]; then
62+ ln -sf gcc /out/usr/bin/cc
63+fi
64+rm -rf /out/usr/share/doc /out/usr/share/man /out/usr/share/info /out/usr/share/locale
65+EOF
66+
67+# --- overlay: only the toolchain, the links and the C toolchain land ---------
68+FROM scratch
69+COPY --from=linker /out/ /
70+COPY --from=rust /out/ /
added 07-play-lego-with-kits/kits/rust-toolchain/rust-toolchain.yaml +26 -0
new file mode 100644
@@ -0,0 +1,26 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Rust toolchain
5+description: Rust toolchain (cargo, rustc, rustfmt, clippy) and rust-analyzer (language server)
6+
7+args:
8+ rust_version:
9+ # A Rust release (e.g. 1.99.0), or stable / beta / nightly (not reproducible).
10+ default: "1.99.0"
11+ pattern: '^([0-9]+\.[0-9]+(\.[0-9]+)?|stable|beta|nightly)$'
12+ buildArg: RUST_VERSION
13+
14+provides: ["rust@${{ kit.args.rust_version }}"]
15+
16+capabilities:
17+ # Runtime access with the tool: the crates.io index and crate downloads.
18+ # Add private registries / git hosts here (or in a kit set) if you need them.
19+ - type: com.docker.sandbox/network-policy@1
20+ config:
21+ runtime:
22+ allow:
23+ - index.crates.io
24+ - static.crates.io
25+ - crates.io
26+ - github.com
new file mode 100644
@@ -0,0 +1,26 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Rust toolchain
5+description: Rust toolchain (cargo, rustc, rustfmt, clippy) and rust-analyzer (language server)
6+
7+args:
8+ rust_version:
9+ # A Rust release (e.g. 1.99.0), or stable / beta / nightly (not reproducible).
10+ default: "1.99.0"
11+ pattern: '^([0-9]+\.[0-9]+(\.[0-9]+)?|stable|beta|nightly)$'
12+ buildArg: RUST_VERSION
13+
14+provides: ["rust@${{ kit.args.rust_version }}"]
15+
16+capabilities:
17+ # Runtime access with the tool: the crates.io index and crate downloads.
18+ # Add private registries / git hosts here (or in a kit set) if you need them.
19+ - type: com.docker.sandbox/network-policy@1
20+ config:
21+ runtime:
22+ allow:
23+ - index.crates.io
24+ - static.crates.io
25+ - crates.io
26+ - github.com
added 07-play-lego-with-kits/sbxenv.yaml +13 -0
new file mode 100644
@@ -0,0 +1,13 @@
1+schemaVersion: "1"
2+name: docker-rust-agent-mistral
3+agent: docker-agent-mistral
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
8+
9+# Relative paths resolve against the directory of this file.
10+# One workload (the agent) plus mixins (the Rust toolchain).
11+kits:
12+ - ./kits/docker-agent-mistral
13+ - ./kits/rust-toolchain
new file mode 100644
@@ -0,0 +1,13 @@
1+schemaVersion: "1"
2+name: docker-rust-agent-mistral
3+agent: docker-agent-mistral
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
8+
9+# Relative paths resolve against the directory of this file.
10+# One workload (the agent) plus mixins (the Rust toolchain).
11+kits:
12+ - ./kits/docker-agent-mistral
13+ - ./kits/rust-toolchain
added 08-policies/README.md +114 -0
new file mode 100644
@@ -0,0 +1,114 @@
1+# Adding policies: forbid `gitlab.com` and `mini-me.org`
2+
3+We reuse the kits of the previous step (`07-play-lego-with-kits`) and add one brick: a kit that **forbids** access to some hosts.
4+
5+## The `sbxenv.yaml` file
6+
7+```yaml
8+schemaVersion: "1"
9+name: docker-policies-agent-mistral
10+agent: docker-agent-mistral
11+workspace: .
12+env:
13+ DOCKER_AGENT_AUTO_UPDATE: "0"
14+ MISTRAL_API_KEY: sbx-mistral-key
15+
16+kits:
17+ - ../07-play-lego-with-kits/kits/docker-agent-mistral
18+ - ../07-play-lego-with-kits/kits/rust-toolchain
19+ - ./kits/deny-hosts
20+```
21+
22+- `agent`: the name of the workload kit (`docker-agent-mistral`), as in step 07.
23+- `kits`: the workload and the Rust mixin from step 07 (paths relative to this file), plus the local policy mixin.
24+- `name`: a sandbox distinct from the one in step 07, with its own secrets.
25+
26+## The policy kit: `deny-hosts`
27+
28+`sbxenv.yaml` has no section for network rules: they go through a kit. `kits/deny-hosts/deny-hosts.yaml` is a v3 mixin without a Dockerfile, which only contains the rule:
29+
30+```yaml
31+# syntax=docker/sandbox-kit:3
32+schemaVersion: "3"
33+kind: mixin
34+displayName: Deny hosts
35+description: Network policy that blocks the hosts the sandbox must never reach
36+
37+capabilities:
38+ - type: com.docker.sandbox/network-policy@1
39+ config:
40+ runtime:
41+ deny:
42+ - gitlab.com
43+ - "**.gitlab.com"
44+ - mini-me.org
45+ - "**.mini-me.org"
46+```
47+
48+- `gitlab.com` and `mini-me.org`: the domains themselves.
49+- `**.gitlab.com` and `**.mini-me.org`: all their subdomains.
50+- A `deny` always wins over an `allow`, whichever kit declared it.
51+
52+To block other hosts, add them to the `deny` list.
53+
54+## Custom secret
55+
56+As in the previous steps, we create the custom secret beforehand. With `--sandbox`, it is removed along with the sandbox by `sbx env rm`:
57+
58+```bash
59+export MISTRAL_API_KEY="your-api-key"
60+sbx secret set-custom --sandbox docker-policies-agent-mistral \
61+--host api.mistral.ai --env MISTRAL_API_KEY \
62+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
63+```
64+
65+🖐️ **on Windows:**
66+```bash
67+$env:MISTRAL_API_KEY="your-api-key"
68+
69+sbx secret set-custom --sandbox docker-policies-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
70+```
71+
72+A global custom secret (without `--sandbox`) works too.
73+
74+## Start the sandbox
75+
76+```bash
77+sbx env create --force
78+sbx env exec -it -- docker-agent run agent.yaml
79+```
80+
81+The plan shown by `sbx env` lists the three kits; you have to approve it (`--force` accepts it without asking).
82+
83+## And try
84+
85+```raw
86+Show me the content of https://gitlab.com
87+```
88+
89+```raw
90+Show me the content of https://mini-me.org
91+```
92+
93+Both requests should be blocked. Compare with a host that is not forbidden, for example `https://crates.io`.
94+
95+## Another method: the policy on the host side
96+
97+You can set the same rule without a kit, with the CLI. It then lives on the host machine and not in `sbxenv.yaml`:
98+
99+```bash
100+# for this sandbox only
101+sbx policy deny network --sandbox docker-policies-agent-mistral gitlab.com mini-me.org
102+
103+# for all sandboxes
104+sbx policy deny network gitlab.com mini-me.org
105+```
106+
107+To check what is allowed or blocked:
108+
109+```bash
110+sbx policy ls
111+sbx policy log
112+```
113+
114+The advantage of the kit: the rule travels with the project (`sbxenv.yaml` is versioned) and applies to anyone who starts the environment. The advantage of the CLI: it can apply to all sandboxes at once.
new file mode 100644
@@ -0,0 +1,114 @@
1+# Adding policies: forbid `gitlab.com` and `mini-me.org`
2+
3+We reuse the kits of the previous step (`07-play-lego-with-kits`) and add one brick: a kit that **forbids** access to some hosts.
4+
5+## The `sbxenv.yaml` file
6+
7+```yaml
8+schemaVersion: "1"
9+name: docker-policies-agent-mistral
10+agent: docker-agent-mistral
11+workspace: .
12+env:
13+ DOCKER_AGENT_AUTO_UPDATE: "0"
14+ MISTRAL_API_KEY: sbx-mistral-key
15+
16+kits:
17+ - ../07-play-lego-with-kits/kits/docker-agent-mistral
18+ - ../07-play-lego-with-kits/kits/rust-toolchain
19+ - ./kits/deny-hosts
20+```
21+
22+- `agent`: the name of the workload kit (`docker-agent-mistral`), as in step 07.
23+- `kits`: the workload and the Rust mixin from step 07 (paths relative to this file), plus the local policy mixin.
24+- `name`: a sandbox distinct from the one in step 07, with its own secrets.
25+
26+## The policy kit: `deny-hosts`
27+
28+`sbxenv.yaml` has no section for network rules: they go through a kit. `kits/deny-hosts/deny-hosts.yaml` is a v3 mixin without a Dockerfile, which only contains the rule:
29+
30+```yaml
31+# syntax=docker/sandbox-kit:3
32+schemaVersion: "3"
33+kind: mixin
34+displayName: Deny hosts
35+description: Network policy that blocks the hosts the sandbox must never reach
36+
37+capabilities:
38+ - type: com.docker.sandbox/network-policy@1
39+ config:
40+ runtime:
41+ deny:
42+ - gitlab.com
43+ - "**.gitlab.com"
44+ - mini-me.org
45+ - "**.mini-me.org"
46+```
47+
48+- `gitlab.com` and `mini-me.org`: the domains themselves.
49+- `**.gitlab.com` and `**.mini-me.org`: all their subdomains.
50+- A `deny` always wins over an `allow`, whichever kit declared it.
51+
52+To block other hosts, add them to the `deny` list.
53+
54+## Custom secret
55+
56+As in the previous steps, we create the custom secret beforehand. With `--sandbox`, it is removed along with the sandbox by `sbx env rm`:
57+
58+```bash
59+export MISTRAL_API_KEY="your-api-key"
60+sbx secret set-custom --sandbox docker-policies-agent-mistral \
61+--host api.mistral.ai --env MISTRAL_API_KEY \
62+--placeholder sbx-mistral-key --value "$MISTRAL_API_KEY"
63+```
64+
65+🖐️ **on Windows:**
66+```bash
67+$env:MISTRAL_API_KEY="your-api-key"
68+
69+sbx secret set-custom --sandbox docker-policies-agent-mistral --host api.mistral.ai --env MISTRAL_API_KEY --placeholder sbx-mistral-key --value "$env:MISTRAL_API_KEY"
70+```
71+
72+A global custom secret (without `--sandbox`) works too.
73+
74+## Start the sandbox
75+
76+```bash
77+sbx env create --force
78+sbx env exec -it -- docker-agent run agent.yaml
79+```
80+
81+The plan shown by `sbx env` lists the three kits; you have to approve it (`--force` accepts it without asking).
82+
83+## And try
84+
85+```raw
86+Show me the content of https://gitlab.com
87+```
88+
89+```raw
90+Show me the content of https://mini-me.org
91+```
92+
93+Both requests should be blocked. Compare with a host that is not forbidden, for example `https://crates.io`.
94+
95+## Another method: the policy on the host side
96+
97+You can set the same rule without a kit, with the CLI. It then lives on the host machine and not in `sbxenv.yaml`:
98+
99+```bash
100+# for this sandbox only
101+sbx policy deny network --sandbox docker-policies-agent-mistral gitlab.com mini-me.org
102+
103+# for all sandboxes
104+sbx policy deny network gitlab.com mini-me.org
105+```
106+
107+To check what is allowed or blocked:
108+
109+```bash
110+sbx policy ls
111+sbx policy log
112+```
113+
114+The advantage of the kit: the rule travels with the project (`sbxenv.yaml` is versioned) and applies to anyone who starts the environment. The advantage of the CLI: it can apply to all sandboxes at once.
added 08-policies/agent.yaml +22 -0
new file mode 100644
@@ -0,0 +1,22 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
21+ - type: fetch
22+
new file mode 100644
@@ -0,0 +1,22 @@
1+models:
2+ mistral:
3+ provider: mistral
4+ model: ministral-3b-2512
5+ temperature: 0.7
6+ max_tokens: 8192
7+
8+agents:
9+ root:
10+ model: mistral
11+ description: Assistant using Mistral
12+ instruction: |
13+ You name is [Bob 🤓📦], You are a helpful assistant.
14+
15+ welcome_message: |
16+ 🤖 Local Assistant propulsed by **Mistral** 🐱
17+
18+ toolsets:
19+ - type: filesystem
20+ - type: shell
21+ - type: fetch
22+
added 08-policies/kits/deny-hosts/deny-hosts.yaml +16 -0
new file mode 100644
@@ -0,0 +1,16 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Deny hosts
5+description: Network policy that blocks the hosts the sandbox must never reach
6+
7+capabilities:
8+ - type: com.docker.sandbox/network-policy@1
9+ config:
10+ runtime:
11+ # Deny wins over any allow, whichever kit declares it.
12+ deny:
13+ - gitlab.com
14+ - "**.gitlab.com"
15+ - mini-me.org
16+ - "**.mini-me.org"
new file mode 100644
@@ -0,0 +1,16 @@
1+# syntax=docker/sandbox-kit:3
2+schemaVersion: "3"
3+kind: mixin
4+displayName: Deny hosts
5+description: Network policy that blocks the hosts the sandbox must never reach
6+
7+capabilities:
8+ - type: com.docker.sandbox/network-policy@1
9+ config:
10+ runtime:
11+ # Deny wins over any allow, whichever kit declares it.
12+ deny:
13+ - gitlab.com
14+ - "**.gitlab.com"
15+ - mini-me.org
16+ - "**.mini-me.org"
added 08-policies/sbxenv.yaml +15 -0
new file mode 100644
@@ -0,0 +1,15 @@
1+schemaVersion: "1"
2+name: docker-policies-agent-mistral
3+agent: docker-agent-mistral
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
8+
9+# Relative paths resolve against the directory of this file.
10+kits:
11+ # The kits of the previous step...
12+ - ../07-play-lego-with-kits/kits/docker-agent-mistral
13+ - ../07-play-lego-with-kits/kits/rust-toolchain
14+ # ...plus the policy.
15+ - ./kits/deny-hosts
new file mode 100644
@@ -0,0 +1,15 @@
1+schemaVersion: "1"
2+name: docker-policies-agent-mistral
3+agent: docker-agent-mistral
4+workspace: .
5+env:
6+ DOCKER_AGENT_AUTO_UPDATE: "0"
7+ MISTRAL_API_KEY: sbx-mistral-key
8+
9+# Relative paths resolve against the directory of this file.
10+kits:
11+ # The kits of the previous step...
12+ - ../07-play-lego-with-kits/kits/docker-agent-mistral
13+ - ../07-play-lego-with-kits/kits/rust-toolchain
14+ # ...plus the policy.
15+ - ./kits/deny-hosts
added README.md +1 -0
new file mode 100644
@@ -0,0 +1 @@
1+# Workshop: Docker `sbx` + Docker Agent + Mistral AI
new file mode 100644
@@ -0,0 +1 @@
1+# Workshop: Docker `sbx` + Docker Agent + Mistral AI
added git.sh +136 -0
new file mode 100755
@@ -0,0 +1,136 @@
1+#!/bin/bash
2+message=""
3+case $1 in
4+
5+ # 🎨: art
6+ art)
7+ message="Improve structure / format of the code"
8+ emoji="🎨"
9+ ;;
10+
11+ # 🐛: bug
12+ bug|fix)
13+ message="Fix a bug"
14+ emoji="🐛"
15+ ;;
16+
17+ # 🤓: geek
18+ human|human-fixed)
19+ message="Human Fixed"
20+ emoji="🤓"
21+ ;;
22+
23+ # 🤖: robot
24+ ai|ai-generated)
25+ message="AI generated"
26+ emoji="🤖"
27+ ;;
28+
29+ # ✨: sparkles
30+ sparkles|feature)
31+ message="Introduce new feature(s)"
32+ emoji="✨"
33+ ;;
34+
35+ # 🧩: jigsaw
36+ jigsaw|example|examples|demo|demos)
37+ message="Introduce new example(s)"
38+ emoji="🧩"
39+ ;;
40+
41+
42+ # 📝: memo
43+ memo|doc|documentation)
44+ message="Add or update documentation"
45+ emoji="📝"
46+ ;;
47+
48+ # 🌸: cherry_blossom
49+ gardening|garden|clean|cleaning)
50+ message="Gardening"
51+ emoji="🌸"
52+ ;;
53+
54+ # 🚀: rocket
55+ rocket|deploy)
56+ message="Deploy stuff"
57+ emoji="🚀"
58+ ;;
59+
60+ # 🎉: tada
61+ tada|first)
62+ message="Begin a project"
63+ emoji="🎉"
64+ ;;
65+
66+ # 🚧: construction
67+ construction|wip)
68+ message="Work in progress"
69+ emoji="🚧"
70+ ;;
71+
72+ # 📦️: package
73+ package|build)
74+ message="Add or update compiled files or packages"
75+ emoji="📦️"
76+ ;;
77+
78+ # 📦️: package
79+ release)
80+ message="Create a release"
81+ emoji="📦️"
82+ ;;
83+
84+ # 👽️: alien
85+ alien|api)
86+ message="Update code due to external API changes"
87+ emoji="👽️"
88+ ;;
89+
90+ # 🐳: whale
91+ docker|container)
92+ message="Docker"
93+ emoji="🐳"
94+ ;;
95+
96+ # 🍊: tangerine
97+ gitpod|gitpodify)
98+ message="Gitpodify"
99+ emoji="🍊"
100+ ;;
101+
102+ # 🧪: test tube
103+ alembic|experiments|experiment|xp)
104+ message="Perform experiments"
105+ emoji="🧪"
106+ ;;
107+
108+ # ✅: check mark
109+ test|tests|testing)
110+ message="Add or update tests"
111+ emoji="✅"
112+ ;;
113+
114+ # 💾: floppy-disk
115+ save)
116+ message="Saved"
117+ emoji="💾"
118+ ;;
119+
120+ *)
121+ message="Updated"
122+ emoji="🛟"
123+ ;;
124+
125+esac
126+
127+find . -name '.DS_Store' -type f -delete
128+
129+if [ -z "$2" ]
130+then
131+ # empty
132+ git add .; git commit -m "$emoji $message."; git push
133+else
134+ # not empty
135+ git add .; git commit -m "$emoji $message: $2"; git push
136+fi
new file mode 100755
@@ -0,0 +1,136 @@
1+#!/bin/bash
2+message=""
3+case $1 in
4+
5+ # 🎨: art
6+ art)
7+ message="Improve structure / format of the code"
8+ emoji="🎨"
9+ ;;
10+
11+ # 🐛: bug
12+ bug|fix)
13+ message="Fix a bug"
14+ emoji="🐛"
15+ ;;
16+
17+ # 🤓: geek
18+ human|human-fixed)
19+ message="Human Fixed"
20+ emoji="🤓"
21+ ;;
22+
23+ # 🤖: robot
24+ ai|ai-generated)
25+ message="AI generated"
26+ emoji="🤖"
27+ ;;
28+
29+ # ✨: sparkles
30+ sparkles|feature)
31+ message="Introduce new feature(s)"
32+ emoji="✨"
33+ ;;
34+
35+ # 🧩: jigsaw
36+ jigsaw|example|examples|demo|demos)
37+ message="Introduce new example(s)"
38+ emoji="🧩"
39+ ;;
40+
41+
42+ # 📝: memo
43+ memo|doc|documentation)
44+ message="Add or update documentation"
45+ emoji="📝"
46+ ;;
47+
48+ # 🌸: cherry_blossom
49+ gardening|garden|clean|cleaning)
50+ message="Gardening"
51+ emoji="🌸"
52+ ;;
53+
54+ # 🚀: rocket
55+ rocket|deploy)
56+ message="Deploy stuff"
57+ emoji="🚀"
58+ ;;
59+
60+ # 🎉: tada
61+ tada|first)
62+ message="Begin a project"
63+ emoji="🎉"
64+ ;;
65+
66+ # 🚧: construction
67+ construction|wip)
68+ message="Work in progress"
69+ emoji="🚧"
70+ ;;
71+
72+ # 📦️: package
73+ package|build)
74+ message="Add or update compiled files or packages"
75+ emoji="📦️"
76+ ;;
77+
78+ # 📦️: package
79+ release)
80+ message="Create a release"
81+ emoji="📦️"
82+ ;;
83+
84+ # 👽️: alien
85+ alien|api)
86+ message="Update code due to external API changes"
87+ emoji="👽️"
88+ ;;
89+
90+ # 🐳: whale
91+ docker|container)
92+ message="Docker"
93+ emoji="🐳"
94+ ;;
95+
96+ # 🍊: tangerine
97+ gitpod|gitpodify)
98+ message="Gitpodify"
99+ emoji="🍊"
100+ ;;
101+
102+ # 🧪: test tube
103+ alembic|experiments|experiment|xp)
104+ message="Perform experiments"
105+ emoji="🧪"
106+ ;;
107+
108+ # ✅: check mark
109+ test|tests|testing)
110+ message="Add or update tests"
111+ emoji="✅"
112+ ;;
113+
114+ # 💾: floppy-disk
115+ save)
116+ message="Saved"
117+ emoji="💾"
118+ ;;
119+
120+ *)
121+ message="Updated"
122+ emoji="🛟"
123+ ;;
124+
125+esac
126+
127+find . -name '.DS_Store' -type f -delete
128+
129+if [ -z "$2" ]
130+then
131+ # empty
132+ git add .; git commit -m "$emoji $message."; git push
133+else
134+ # not empty
135+ git add .; git commit -m "$emoji $message: $2"; git push
136+fi