diff --git a/content/guides/images/mistral-vibe-sandbox-credential.jpg b/content/guides/images/mistral-vibe-sandbox-credential.jpg new file mode 100644 index 0000000000..0b4934084c Binary files /dev/null and b/content/guides/images/mistral-vibe-sandbox-credential.jpg differ diff --git a/content/guides/images/mistral-vibe-sandbox-models.jpg b/content/guides/images/mistral-vibe-sandbox-models.jpg new file mode 100644 index 0000000000..6695721d56 Binary files /dev/null and b/content/guides/images/mistral-vibe-sandbox-models.jpg differ diff --git a/content/guides/images/mistral-vibe-sandbox-repair.jpg b/content/guides/images/mistral-vibe-sandbox-repair.jpg new file mode 100644 index 0000000000..2d5ed4747e Binary files /dev/null and b/content/guides/images/mistral-vibe-sandbox-repair.jpg differ diff --git a/content/guides/mistral-vibe-sandbox.md b/content/guides/mistral-vibe-sandbox.md index ebfe2c9c01..4816d43012 100644 --- a/content/guides/mistral-vibe-sandbox.md +++ b/content/guides/mistral-vibe-sandbox.md @@ -1,251 +1,413 @@ --- title: Run Mistral Vibe in a Docker Sandbox -description: Package Mistral's Vibe coding agent as a Docker Sandbox kit so it runs in an isolated microVM and reaches the Mistral API through the sandbox proxy, keeping your API key off the VM. +description: Package Mistral's Vibe coding agent as a v3 Docker Sandbox kit with a pinned agent version and host-managed API credentials. summary: | - Build a pinned, reusable image for Mistral's Vibe CLI and wire it into a - Docker Sandbox agent kit. Vibe runs in an isolated microVM and reaches the - Mistral API through the sandbox proxy, so your API key never enters the VM. + Build a reusable v3 workload kit for Mistral's Vibe CLI. Vibe runs in an + isolated microVM and reaches Mistral through the sandbox proxy, so your API + key stays on the host. Verify the setup with a small coding task. keywords: ai, mistral, vibe, docker sandboxes, sbx, coding agent, microvm, security, kits, uv params: tags: [ai] - time: 20 minutes + time: 30 minutes --- Mistral Vibe is Mistral's open source coding agent. This guide shows how to package it as a Docker Sandbox agent so it runs in an isolated microVM instead -of directly on your host. The agent reaches the Mistral API through the -sandbox proxy, so your API key stays on the host and never enters the VM. +of directly on your host. The agent reaches Mistral through the sandbox +proxy, so your API key stays on the host. -Rather than install the agent fresh on every run, you'll bake a pinned image -once and reuse it. Pinning the agent version and building a dedicated image -gives you reproducible sandboxes and faster startups, and it's the approach -Docker recommends for building your own agent. +Install a pinned Vibe version when you build the kit, then reuse that image +for each sandbox. The kit also declares the network access and credentials +Vibe needs at runtime. In this guide, you'll learn how to: - Store your Mistral API key on the host as a sandbox secret -- Build a pinned, multi-architecture image that ships Vibe on the `shell` template -- Write an agent kit that wires Vibe to the Mistral API through the proxy -- Validate, launch, and iterate on the sandbox +- Build and publish a v3 workload kit with Vibe installed +- Launch Vibe and verify that it receives a credential placeholder +- Run a sample coding task and inspect the result -This guide uses the v2 format. For its reference and migration -guidance, see [Kits v2](../manuals/ai/sandboxes/customize/kits-v2.md). +This guide uses [v3 kits](../manuals/ai/sandboxes/customize/_index.md). +Kits are Early Access, and `sbx env` is experimental. ## How isolation works -Every outbound request from a sandbox passes through a proxy that runs on -your host. The proxy enforces network policy and injects credentials, so the -agent inside the VM never handles the real key. +Outbound TCP traffic from the sandbox passes through a proxy on your host. +The proxy enforces network policy and injects credentials into matching +HTTP requests. Vibe sees `MISTRAL_API_KEY=proxy-managed` inside the microVM. +The real key remains in the host secret store. Mistral is a -[built-in service](../manuals/ai/sandboxes/configuration/credentials.md#built-in-services): -`sbx` already maps the `mistral` service name to the `MISTRAL_API_KEY` -environment variable and the `api.mistral.ai` domain. Inside the VM, Vibe -sees only a sentinel value for `MISTRAL_API_KEY`. The proxy swaps in the real -key, and only for requests to `api.mistral.ai`. If the agent reads the -variable for any other purpose, it gets the sentinel. - -Built-in doesn't mean automatic. The kit you write in -[Step 4](#step-4-write-the-agent-kit) still declares where the key comes from -and how the proxy attaches it to requests. +[built-in service](../manuals/ai/sandboxes/configuration/credentials.md#built-in-services). +The kit declares the `mistral` credential and the three hosts where Vibe +uses it: `api.mistral.ai` for inference, `console.mistral.ai` for account +lookup, and `chat.mistral.ai` for managed config. Network access and +credential injection are separate declarations. An allowed package download +doesn't receive the Mistral authorization header. ## Prerequisites Before you start, make sure you have: -- [Docker Desktop](../get-started/get-docker.md) or Docker Engine installed -- [Docker Sandboxes (`sbx`) installed and signed in](../manuals/ai/sandboxes/install.md) -- A [Mistral API key](https://console.mistral.ai/) -- A Docker Hub namespace, or another registry, to publish the image to +- [Docker Desktop](../get-started/get-docker.md) or Docker Engine with Buildx +- [Docker Sandboxes (`sbx`) installed and signed in](../manuals/ai/sandboxes/install.md), version 0.45.0 or later for v3 kit support +- A [Mistral API key](https://console.mistral.ai/) with an active completion allowance +- Git, for cloning the sample project +- A Docker Hub namespace, or another registry, if you want to publish your kit -## Step 1: Store the Mistral key on the host +The sample run uses Vibe 2.25.8 and was verified with `sbx` 0.46.0-rc5. -Provide the key once on the host. Because Mistral is a built-in service, -`sbx` resolves it under the `mistral` name without any extra wiring: +## Step 1: Store the Mistral key + +Run this command on your host and paste the key when prompted: ```console $ sbx secret set mistral ``` -Service secrets are global by default, so any sandbox that declares the -`mistral` service can use it. Use `--sandbox` to scope a secret to a single -sandbox instead. For how the proxy resolves and injects credentials, see +Service secrets are global by default, so sandboxes that declare the +`mistral` service can request this key. Use `--sandbox` to scope a secret to +one sandbox. The key is stored separately from the kit and workspace. See [Credentials](../manuals/ai/sandboxes/configuration/credentials.md). -## Step 2: Write a pinned Vibe image +## Step 2: Install Vibe in the image -Vibe is a Python application. The official `shell` template already ships -`uv`, `git`, `ripgrep`, and Python, so build on top of it and install a -pinned Vibe version with `uv`. +Create a kit directory beside the project you want Vibe to work on. Run the +following command from the directory that will contain both: -Create a `Dockerfile`: +```console +$ mkdir mistral-vibe +``` -```dockerfile -# syntax=docker/dockerfile:1 -ARG BASE_IMAGE=docker/sandbox-templates:shell -FROM ${BASE_IMAGE} +If you don't have an existing project, create an empty workspace beside +the kit to verify the agent launch: -# Pin the agent version for reproducible sandboxes. -# Check https://pypi.org/project/mistral-vibe/ and bump as needed. -ARG VIBE_VERSION=2.24.5 +```console +$ mkdir my-project +``` + +The kit has two files with matching filename stems: + +```text +mistral-vibe/ +├── vibe.yaml +└── vibe.dockerfile +``` + +Create `mistral-vibe/vibe.dockerfile`. The `shell-docker` template supplies +the agent account, Python, `uv`, and the sandbox's Docker tooling: + +```dockerfile {title="mistral-vibe/vibe.dockerfile"} +FROM docker/sandbox-templates:shell-docker + +ARG VIBE_VERSION -# Install Vibe as the non-root agent user. The socks extra is installed -# explicitly so the agent works through the sandbox proxy. USER agent -RUN uv tool install "mistral-vibe==${VIBE_VERSION}" --with "httpx[socks]" \ +RUN uv tool install "mistral-vibe==${VIBE_VERSION}" \ + && uv cache clean \ && vibe --version -CMD ["vibe", "--agent", "auto-approve"] +ENV PATH=/home/agent/.local/bin:${PATH} \ + VIBE_ENABLE_TELEMETRY=false \ + VIBE_ENABLE_AUTO_UPDATE=false \ + GIT_TERMINAL_PROMPT=0 + +WORKDIR /home/agent/workspace +ENTRYPOINT ["vibe", "--trust"] +CMD [] ``` -Three choices are worth calling out: +The descriptor in the next step supplies `VIBE_VERSION`. Installing as +`agent` puts the executable in `/home/agent/.local/bin`. Turning off Vibe's +automatic update keeps the installed agent version fixed until you rebuild. -- Pinning `mistral-vibe` to an explicit version keeps the image - reproducible. To move to a newer release, change `VIBE_VERSION` and rebuild. -- Installing the `httpx[socks]` extra explicitly avoids a startup failure - when the agent runs behind the sandbox proxy. Some Vibe releases don't pull - it in on their own. -- The `CMD` launches Vibe with `--agent auto-approve`, so the agent approves - tool executions automatically. Baking the flags into the image keeps the - kit's launch behavior in one place, so the kit doesn't override the - entrypoint. +`--trust` lets Vibe load the mounted project's configuration without a +workspace trust prompt. Vibe still uses its tool permission prompts. Review +the project configuration before mounting it. -## Step 3: Build and publish the image +## Step 3: Declare access and credentials -Build for both common architectures and publish to your namespace. The -build attaches provenance and SBOM attestations, which record how the image -was built and what it contains. Replace `YOUR_NAMESPACE` with your Docker Hub -username: +Create `mistral-vibe/vibe.yaml`. The syntax directive selects the v3 kit +frontend, which builds the companion Dockerfile and packages the descriptor +with the image: -```console -$ docker buildx build \ - --platform linux/amd64,linux/arm64 \ - --provenance=true \ - --sbom=true \ - -t YOUR_NAMESPACE/sbx-mistral-vibe:0.1.0 \ - --push . -``` - -Tag the image with a real version rather than a moving tag, so the kit in the -next step always resolves to the same build. - -## Step 4: Write the agent kit - -The kit ties the image, network policy, and credentials together. The image -already sets the launch command in its `CMD`. Create a directory for the kit -with a `spec.yaml` inside. Replace -`YOUR_NAMESPACE` with the namespace you published to: - -```yaml {title="mistral-vibe/spec.yaml"} -schemaVersion: "2" -kind: sandbox -name: mistral-vibe +```yaml {title="mistral-vibe/vibe.yaml"} +# syntax=docker/sandbox-kit:3 +schemaVersion: "3" +kind: workload displayName: Mistral Vibe +description: Mistral Vibe with host-managed API credentials -sandbox: - image: docker.io/YOUR_NAMESPACE/sbx-mistral-vibe:0.1.0 +args: + version: + default: "2.25.8" + pattern: '^[0-9]+\.[0-9]+\.[0-9]+$' + buildArg: VIBE_VERSION -agentInstructions: - filename: AGENTS.md - content: | - You are running inside an isolated Docker Sandbox microVM. - Network access is restricted to the Mistral API. Prefer tools and - packages already available in the workspace. +provides: ["vibe@${{ kit.args.version }}"] -permissions: - network: - allow: - - "api.mistral.ai:443" +capabilities: + - type: com.docker.sandbox/sbx@1 -credentials: - - service: mistral - apiKey: - name: MISTRAL_API_KEY - inject: - - domain: api.mistral.ai - scheme: bearer + - type: com.docker.sandbox/network-policy@1 + config: + runtime: + allow: + - api.mistral.ai:443 + - console.mistral.ai:443 + - chat.mistral.ai:443 + + - type: com.docker.sandbox/credential@1 + description: Mistral API access + config: + service: mistral + phase: runtime + apiKey: + name: MISTRAL_API_KEY + proxyManaged: true + inject: + - domain: api.mistral.ai + header: Authorization + format: "Bearer %s" + - domain: console.mistral.ai + header: Authorization + format: "Bearer %s" + - domain: chat.mistral.ai + header: Authorization + format: "Bearer %s" ``` -Each field does the following: +`kind: workload` declares a kit that supplies the sandbox's root filesystem. +`args.version` selects the Vibe release, maps it to the Dockerfile build +argument, and records that version in `provides`. To install another release, +change the default and rebuild. -| Field | Purpose | -| --------------------------- | -------------------------------------------------------------------------------------------------------------- | -| `kind: sandbox` | Declares a sandbox agent: a complete image plus its launch configuration. | -| `name` | The kit's identifier. Pass the kit directory to `sbx run`. | -| `sandbox.image` | The pinned image you published in Step 3. Its `CMD` launches Vibe, so the kit doesn't set an entrypoint. | -| `agentInstructions.filename`| The instructions file Vibe reads in the project. | -| `agentInstructions.content` | Markdown appended to `AGENTS.md` at creation to prime the agent about its environment. | -| `permissions.network.allow` | The hosts the sandbox may reach. Without it, requests are blocked by the default deny policy. | -| `credentials[].service` | The built-in service that supplies the key. `mistral` maps to `MISTRAL_API_KEY` and `api.mistral.ai`. | -| `credentials[].apiKey.name` | The environment variable the proxy manages. Vibe sees a sentinel value; the proxy swaps in the real key. | -| `credentials[].apiKey.inject`| Where and how the proxy attaches the key. `scheme: bearer` sets `Authorization: Bearer ` for the domain. | +The `sbx@1` capability tells Docker Sandboxes to manage the agent's launch. +The image supplies its user, working directory, and entrypoint. +`network-policy@1` permits HTTPS traffic to the three Mistral hosts. Package +installation happens during the image build, so PyPI doesn't need a runtime +rule unless your coding task downloads packages. -> [!WARNING] -> `--agent auto-approve` runs Vibe in a mode that approves every tool -> execution without prompting. The sandbox isolates the agent from your host, -> but review the agent's actions before you run it against sensitive -> workspaces. +`credential@1` gives Vibe a placeholder in `MISTRAL_API_KEY` and tells the +host proxy to write the real key into the `Authorization` header. List each +injection host explicitly in the network allowlist. The v3 validator checks +these declarations against each other; a wildcard alone doesn't cover an +injection host for validation. -## Step 5: Validate and run +## Step 4: Build and publish the kit -Validate the kit before you launch it: +From the directory containing `mistral-vibe`, validate and build the kit +without publishing it: ```console -$ sbx kit validate ./mistral-vibe +$ docker buildx build ./mistral-vibe \ + --file ./mistral-vibe/vibe.yaml \ + --output type=cacheonly ``` -Then, from your project directory, launch the agent with the kit: +The build validates the descriptor and installs Vibe. Use the descriptor +with `--file`, rather than the companion Dockerfile, so the output includes +the kit metadata and capabilities. + +Optional. To share the kit, sign in to your registry, replace +`YOUR_NAMESPACE` with your Docker Hub namespace, and publish for both common +architectures: ```console -$ sbx run ./mistral-vibe --name mistral-vibe . +$ docker login +$ docker buildx build ./mistral-vibe \ + --file ./mistral-vibe/vibe.yaml \ + --platform linux/amd64,linux/arm64 \ + --provenance=true \ + --sbom=true \ + --tag docker.io/YOUR_NAMESPACE/sbx-kit-vibe:2.25.8 \ + --push ``` -- `./mistral-vibe` is the sandbox kit reference, pointing to the folder that - contains `spec.yaml`. -- `--name mistral-vibe` names the sandbox. Without it, `sbx` derives a name - from the agent and the working directory, and the commands below won't match. -- `.` is the project directory to mount in the sandbox. +This publishes the workload and its descriptor as one OCI image, with +provenance and software bill of materials attestations. You can run the +published reference in place of the local kit path in the next step. -Vibe starts in an isolated microVM, talks to the Mistral API through the -proxy, and the real key never touches the container. +## Step 5: Run and verify Vibe -## Iterate on the kit - -If the agent can't reach a domain it needs, or a request behaves -unexpectedly, inspect what the proxy saw: +From the same directory, launch the local kit against your project. Replace +`./my-project` with the path to the workspace Vibe should edit: ```console -$ sbx policy log +$ sbx run --name mistral-vibe ./mistral-vibe ./my-project ``` -Each entry shows the request, the rule it matched, and how the proxy handled -it. Use it to spot a host missing from `permissions.network.allow`. After you -change `spec.yaml`, recreate the sandbox for a clean start: +`--name` gives the sandbox a predictable name for later commands. The kit +path identifies the workload; the project path identifies the directory +mounted read-write. Vibe's changes to that workspace are visible on your host. + +When prompted, approve the kit's request to use your stored Mistral key. +Storing the key and approving its use are separate steps. See +[Credential bindings](../manuals/ai/sandboxes/configuration/credentials.md#credential-bindings). + +At the Vibe prompt, prefix a shell command with `!` to inspect the value +available inside the sandbox: + +```text +!printf 'MISTRAL_API_KEY=%s\n' "$MISTRAL_API_KEY" +``` + +Expected output: + +```text +MISTRAL_API_KEY=proxy-managed +``` + +Vibe receives the placeholder while the proxy authenticates its Mistral +requests. The following capture shows this check in the sample workspace +used in the next section. Vibe displays the mounted workspace's path; your +path and account greeting will differ from the screenshots: + +![Vibe 2.25.8 displaying MISTRAL_API_KEY=proxy-managed inside the sandbox](images/mistral-vibe-sandbox-credential.jpg) + +## Try a coding task + +The community-maintained +[Mistral Vibe sample](https://github.com/shelajev/mistral-vibe-sbx-demo) +contains a small FastAPI application, three tests, and one intentional bug. +It uses the published +[Vibe kit](https://github.com/shelajev/vibe-sbx-kit), so you can try this +exercise without building or publishing your own kit. This starts a +separate sandbox from the sample's checked-in environment file. Your local +kit sandbox remains available for later use. + +Type `/exit` at the Vibe prompt to return to your host terminal. Then run +these commands from a directory where you want to keep the sample: ```console -$ sbx rm mistral-vibe && sbx run ./mistral-vibe --name mistral-vibe . +$ git clone https://github.com/shelajev/mistral-vibe-sbx-demo.git +$ cd mistral-vibe-sbx-demo +$ sbx env run ``` +`sbx env run` reads the repository's `sbxenv.yaml`, pulls +`docker.io/olegselajev241/sbx-kit-vibe:2.25.8`, mounts only `app/`, and opens +Vibe in `mistral-vibe-demo-v3`. Approve the environment plan and credential +binding when prompted. Later runs attach to the same sandbox. + +The environment file uses schema version 1; the kit uses schema version 3. +These formats have independent version numbers. See +[Environment files](../manuals/ai/sandboxes/configuration/environment-files.md). + +Before starting the coding task, open a second host terminal and permit +Python package downloads for this sandbox: + +```console +$ sbx policy allow network --sandbox mistral-vibe-demo-v3 \ + pypi.org,files.pythonhosted.org +``` + +The published kit permits Mistral traffic; the application needs these +additional hosts if `uv` downloads its test dependencies. The rule is scoped +to the sample sandbox, and package requests receive no Mistral credential. + +Return to Vibe, which uses the project's `mistral-medium-3.5` model, and +paste this repair prompt: + +```text +Run the test suite and diagnose the failure. Fix the application code without changing +the tests or the public API contract. Run the full test suite again and summarize the +cause and your change. +``` + +Approve the shell commands and edits when Vibe asks. The failing test checks +`GET /tasks?completed=false`: the endpoint returns all tasks instead of only +incomplete tasks. A correct repair changes `if completed:` to +`if completed is not None:`. + +![Vibe explaining the boolean-filter bug, its application-code repair, and that all three tests pass](images/mistral-vibe-sandbox-repair.jpg) + +Check the test suite yourself at the Vibe prompt: + +```text +!uv run pytest +``` + +The test suite should end with `3 passed`. In your second host terminal, +change to the `mistral-vibe-sbx-demo` repository and inspect the edit: + +```console +$ git diff -- app/main.py +``` + +The repository's `.git` directory stays on the host because only `app/` +is mounted. The diff should show the filter change. To repeat the exercise, +type `/exit` in Vibe and run `git restore app/main.py` from the host +repository to restore the intentional bug. + +Optional. Run `/model` inside Vibe and select **GLM 5.2 (Mistral hosted)**. +The sample adds `zai-glm-5-2` to `app/.vibe/config.toml`; the local kit you +built earlier doesn't add this model configuration. + +![Vibe's model menu showing Mistral Medium 3.5 and GLM 5.2 hosted by Mistral](images/mistral-vibe-sandbox-models.jpg) + +## Troubleshoot and iterate + +If Vibe can't reach a service, inspect the sandbox's proxy log from your host: + +```console +$ sbx policy log mistral-vibe +``` + +For the sample environment, use `mistral-vibe-demo-v3` instead. If another +coding task needs additional services, add the required hosts to the +descriptor or allow them with `sbx policy allow network --sandbox`. + +After editing your local descriptor or Dockerfile, return to the host +directory containing `mistral-vibe` and create another sandbox to test it. +Restarting an existing sandbox doesn't apply kit changes: + +```console +$ sbx run --name mistral-vibe-test-2 ./mistral-vibe ./my-project +``` + +If Vibe keeps retrying without a response, inspect its log from the host: + +```console +$ sbx exec mistral-vibe -- tail -n 50 /home/agent/.vibe/logs/vibe.log +``` + +For the sample, use `mistral-vibe-demo-v3`. Repeated `HTTP 429` responses +indicate a rate or allowance limit. Check the key's organization and workspace +at [Mistral limits](https://admin.mistral.ai/plateforme/limits). A response +with `X-Ratelimit-Limit-Req-Minute: 0` indicates zero request allowance; +the verified sample required activating pay-as-you-go. For authentication +failures, check `sbx secret ls` for a `mistral` entry and confirm the +credential binding covers all three Mistral hosts. + ## Clean up -Sandboxes persist after Vibe exits. To stop one without deleting it: +Sandboxes persist after Vibe exits. To stop the local sandbox: ```console $ sbx stop mistral-vibe ``` -To remove the sandbox and everything inside it: +To remove it: ```console $ sbx rm mistral-vibe ``` -Files in your workspace are unaffected. +If you created `mistral-vibe-test-2`, remove it with +`sbx rm mistral-vibe-test-2` as well. From the sample repository, remove its +environment with: + +```console +$ sbx env rm --force +``` + +Removing a sandbox preserves the mounted workspace and its edits. ## Learn more - [Get started with Docker Sandboxes](../manuals/ai/sandboxes/get-started.md) -- [Build a v3 agent workload](/manuals/ai/sandboxes/customize/author/build-an-agent.md) -- [Customize sandboxes with kits](/manuals/ai/sandboxes/customize/_index.md) +- [Build an agent workload](../manuals/ai/sandboxes/customize/author/build-an-agent.md) +- [Customize sandboxes with kits](../manuals/ai/sandboxes/customize/_index.md) - [Credentials and built-in services](../manuals/ai/sandboxes/configuration/credentials.md#built-in-services) - [Mistral Vibe](https://github.com/mistralai/mistral-vibe)