diff --git a/docs/cli/image-commands.mdx b/docs/cli/image-commands.mdx index 52e42573..b9b0c2ae 100644 --- a/docs/cli/image-commands.mdx +++ b/docs/cli/image-commands.mdx @@ -18,6 +18,8 @@ msb pull ghcr.io/my-org/my-image:v1 |------|-------------| | `-f`, `--force` | Force re-download even if cached | | `-q`, `--quiet` | Suppress progress output | +| `--insecure` | Connect over plain HTTP instead of HTTPS | +| `--ca-certs ` | Path to a PEM file with additional CA root certificates | Pre-pulling is useful when you want sandbox creation to be instant. Without a pre-pull, the first `Sandbox.create` with a new image will block on the download. diff --git a/docs/cookbook/local-images.mdx b/docs/cookbook/local-images.mdx new file mode 100644 index 00000000..3ff09e75 --- /dev/null +++ b/docs/cookbook/local-images.mdx @@ -0,0 +1,71 @@ +--- +title: Using Local Docker Images +description: Use locally built Docker images with microsandbox via a local registry +icon: "docker" +--- + +microsandbox pulls images from OCI-compatible registries. If you build an image locally with `docker build`, microsandbox can't access it directly from Docker's local store. The workaround is to run a local registry and push your image there. + +## Step 1: Start a local registry + +Run a local OCI registry on port 5050: + +```bash +docker run -d -p 5050:5000 --name registry registry:2 +``` + + + Port 5000 is used by AirPlay on macOS. This guide uses port 5050 to avoid conflicts. + + +## Step 2: Build, tag, and push your image + +Build your Docker image and tag it for the local registry: + +```bash +docker build -t localhost:5050/my-image:latest . +``` + +If you already have an existing image, re-tag it: + +```bash +docker tag my-image:latest localhost:5050/my-image:latest +``` + +Push to the local registry: + +```bash +docker push localhost:5050/my-image:latest +``` + +## Step 3: Pull with microsandbox + +Since the local registry runs over plain HTTP, use the `--insecure` flag: + +```bash +msb pull localhost:5050/my-image:latest --insecure +``` + +## Step 4: Use it in microsandbox + + +```rust Rust +let sb = Sandbox::builder("worker") + .image("localhost:5050/my-image:latest") + .registry(|r| r.insecure()) + .create() + .await?; +``` + +```typescript TypeScript +const sb = await Sandbox.create({ + name: "worker", + image: "localhost:5050/my-image:latest", + registry: { insecure: true }, +}) +``` + + + + For persistent configuration that applies to all CLI and SDK operations, add the registry to `~/.microsandbox/config.json` instead. See [Registry TLS](/images/overview#registry-tls). + diff --git a/docs/docs.json b/docs/docs.json index 45354f97..f495d57a 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -108,6 +108,18 @@ ] } ] + }, + { + "tab": "Recipes", + "groups": [ + { + "group": "Recipes", + "icon": "book-open", + "pages": [ + "cookbook/local-images" + ] + } + ] } ] }, diff --git a/docs/images/overview.mdx b/docs/images/overview.mdx index 5659ec08..1fe3522a 100644 --- a/docs/images/overview.mdx +++ b/docs/images/overview.mdx @@ -50,10 +50,10 @@ Authenticate to private registries by passing credentials. ```rust Rust let sb = Sandbox::builder("worker") .image("registry.corp.io/team/app:latest") - .registry_auth(RegistryAuth::Basic { + .registry(|r| r.auth(RegistryAuth::Basic { username: "deploy".into(), password: std::env::var("REGISTRY_PASSWORD")?, - }) + })) .pull_policy(PullPolicy::Always) .create() .await?; @@ -63,12 +63,85 @@ let sb = Sandbox::builder("worker") const sb = await Sandbox.create({ name: "worker", image: "registry.corp.io/team/app:latest", - registryAuth: { username: "deploy", password: process.env.REGISTRY_TOKEN }, + registry: { auth: { username: "deploy", password: process.env.REGISTRY_TOKEN } }, pullPolicy: PullPolicy.Always, }) ``` +## Registry TLS + +By default, microsandbox connects to registries over HTTPS using system CA roots. You can customize this per-registry in `~/.microsandbox/config.json`. + +### Plain HTTP registries + +Local registries often run without TLS. Mark them as insecure to connect over plain HTTP: + +```json +{ + "registries": { + "hosts": { + "localhost:5050": { + "insecure": true + } + } + } +} +``` + +### Custom CA certificates + +For registries using self-signed or internal CA certificates, point `ca_certs` to a PEM file. This applies globally to all registry connections: + +```json +{ + "registries": { + "ca_certs": "/path/to/ca-bundle.pem" + } +} +``` + +The certificates are added to the default system roots — public registries continue to work normally. + +### Combined configuration + +```json +{ + "registries": { + "ca_certs": "/path/to/corporate-ca.pem", + "hosts": { + "localhost:5050": { + "insecure": true, + "auth": { "username": "deploy", "password_env": "REGISTRY_TOKEN" } + }, + "ghcr.io": { + "auth": { "username": "user", "store": "keyring" } + } + } + } +} +``` + +These settings can also be set per-sandbox via the SDK, overriding global config: + + +```rust Rust +let sb = Sandbox::builder("worker") + .image("localhost:5050/my-app:latest") + .registry(|r| r.insecure()) + .create() + .await?; +``` + +```typescript TypeScript +const sb = await Sandbox.create({ + name: "worker", + image: "localhost:5050/my-app:latest", + registry: { insecure: true }, +}) +``` + + ## Image storage Images are cached in the global microsandbox home directory: diff --git a/docs/sdk/sandbox.mdx b/docs/sdk/sandbox.mdx index 30900656..d2ed5c35 100644 --- a/docs/sdk/sandbox.mdx +++ b/docs/sdk/sandbox.mdx @@ -177,56 +177,113 @@ const sb2 = await Sandbox.create({ With OCI images, microsandbox stacks layers and adds a copy-on-write overlay so sandboxes share the base. With disk images, the guest gets the block device directly, so there's no copy-on-write isolation between sandboxes using the same disk image. Each sandbox needs its own copy (or use QCOW2's built-in snapshot/backing file support). -## Private registry +## Registry -Authenticate to private container registries and control when images are pulled. +Configure authentication, TLS, and connection settings for OCI registries. + +### Private registry + +Authenticate to private container registries. ```rust Rust use microsandbox::{Sandbox, RegistryAuth, PullPolicy}; -// Explicit auth with pull policy let sb = Sandbox::builder("worker") .image("registry.corp.io/team/app:latest") - .registry_auth(RegistryAuth::Basic { + .registry(|r| r.auth(RegistryAuth::Basic { username: "deploy".into(), password: std::env::var("REGISTRY_PASSWORD")?, - }) + })) .pull_policy(PullPolicy::Always) .create() .await?; - -// Use cached image, never pull -let sb = Sandbox::builder("offline") - .image("python") - .pull_policy(PullPolicy::Never) - .create() - .await?; ``` ```typescript TypeScript import { PullPolicy, Sandbox } from 'microsandbox' -// Explicit auth with pull policy const sb = await Sandbox.create({ name: "worker", image: "private.registry.io/org/app:v1", - registryAuth: { username: "deploy", password: process.env.REGISTRY_TOKEN! }, + registry: { auth: { username: "deploy", password: process.env.REGISTRY_TOKEN! } }, pullPolicy: PullPolicy.Always, }) +``` + + +### Insecure registry + +Connect to a registry over plain HTTP (e.g. a local registry without TLS). + + +```rust Rust +let sb = Sandbox::builder("worker") + .image("localhost:5050/my-app:latest") + .registry(|r| r.insecure()) + .create() + .await?; +``` + +```typescript TypeScript +const sb = await Sandbox.create({ + name: "worker", + image: "localhost:5050/my-app:latest", + registry: { insecure: true }, +}) +``` + + +### Custom CA certificates + +Trust additional CA certificates for registries using self-signed or internal CAs. + + +```rust Rust +let sb = Sandbox::builder("worker") + .image("registry.corp.io/team/app:latest") + .registry(|r| r.ca_certs(std::fs::read("/path/to/ca.pem")?)) + .create() + .await?; +``` + +```typescript TypeScript +const sb = await Sandbox.create({ + name: "worker", + image: "registry.corp.io/team/app:latest", + registry: { caCertsPath: "/path/to/ca.pem" }, +}) +``` + + + + For persistent registry settings that apply to all sandboxes, use `~/.microsandbox/config.json` instead. See [Registry TLS](/images/overview#registry-tls). + + +### Pull policies +Control whether the SDK fetches the image from the registry. + + +```rust Rust // Use cached image, never pull -const sb2 = await Sandbox.create({ +let sb = Sandbox::builder("offline") + .image("python") + .pull_policy(PullPolicy::Never) + .create() + .await?; +``` + +```typescript TypeScript +// Use cached image, never pull +const sb = await Sandbox.create({ name: "offline", image: "python", pullPolicy: PullPolicy.Never, }) ``` - -Pull policies control whether the SDK fetches the image from the registry: - | Policy | Behavior | |--------|----------| | `Always` | Pull the image every time, even if cached locally |