aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhrbrmstr <bob@rud.is>2026-06-10 09:26:45 -0400
committerhrbrmstr <bob@rud.is>2026-06-10 09:26:45 -0400
commit8d2890e93ff96fc21d6e00fe0a03cf4b1a871271 (patch)
tree11382e05b81b7e251c8c59f77c56763032e54053
parentbd417023bfb20a65052611064b6df9e6bf9ad597 (diff)
add apple container machine artifacts: two Dockerfiles (recon-machine, ubuntu-systemd-admin) and the apple-container-machine skill
-rw-r--r--2026/apple-container-machine-machines/Dockerfile.recon-machine72
-rw-r--r--2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin51
-rw-r--r--2026/apple-container-machine-machines/apple-container-machine-skill.md525
3 files changed, 648 insertions, 0 deletions
diff --git a/2026/apple-container-machine-machines/Dockerfile.recon-machine b/2026/apple-container-machine-machines/Dockerfile.recon-machine
new file mode 100644
index 0000000..79897ff
--- /dev/null
+++ b/2026/apple-container-machine-machines/Dockerfile.recon-machine
@@ -0,0 +1,72 @@
+FROM ubuntu:24.04
+
+ENV container container
+
+# Base system + networking
+RUN apt-get update && \
+ apt-get install -y \
+ dbus systemd openssh-server \
+ curl wget ca-certificates \
+ sudo iproute2 iputils-ping net-tools \
+ dnsutils whois traceroute mtr-tiny \
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Recon CLI tooling
+RUN apt-get update && \
+ apt-get install -y \
+ nmap \
+ netcat-openbsd \
+ nikto \
+ sqlmap \
+ hydra \
+ jq \
+ python3 python3-pip python3-venv \
+ dnsmap \
+ dnsrecon \
+ dnswalk \
+ dirb \
+ wfuzz \
+ sslscan \
+ testssl.sh \
+ whatweb \
+ smbclient \
+ ldap-utils \
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Go for installing Go-based tools
+RUN apt-get update && \
+ apt-get install -y golang-go && \
+ apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Install Go-based recon tools
+RUN go install github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest && \
+ go install github.com/projectdiscovery/httpx/cmd/httpx@latest && \
+ go install github.com/projectdiscovery/naabu/v2/cmd/naabu@latest && \
+ go install github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest && \
+ go install github.com/tomnomnom/httprobe@latest && \
+ go install github.com/tomnomnom/gf@latest && \
+ go install github.com/tomnomnom/waybackurls@latest && \
+ go install github.com/tomnomnom/assetfinder@latest && \
+ go install github.com/ffuf/ffuf/v2@latest && \
+ go install github.com/OJ/gobuster/v3@latest
+
+# Copy go-installed binaries to system PATH and add GOPATH/bin to profile
+RUN cp /root/go/bin/* /usr/local/bin/ 2>/dev/null; true
+RUN echo 'export PATH=$PATH:/root/go/bin' > /etc/profile.d/gopath.sh && chmod +x /etc/profile.d/gopath.sh
+
+# Init setup for container machine
+RUN >/etc/machine-id && >/var/lib/dbus/machine-id
+
+RUN systemctl set-default multi-user.target && \
+ systemctl mask \
+ dev-hugepages.mount \
+ sys-fs-fuse-connections.mount \
+ systemd-update-utmp.service \
+ systemd-tmpfiles-setup.service \
+ console-getty.service && \
+ systemctl disable \
+ networkd-dispatcher.service
+
+# SSH config for remote access
+RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config && \
+ sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
diff --git a/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin b/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin
new file mode 100644
index 0000000..4f5ebaf
--- /dev/null
+++ b/2026/apple-container-machine-machines/Dockerfile.ubuntu-systemd-admin
@@ -0,0 +1,51 @@
+FROM ubuntu:24.04
+
+ENV container container
+
+# Base system + networking
+RUN apt-get update && \
+ apt-get install -y \
+ dbus systemd openssh-server \
+ curl ca-certificates \
+ sudo iproute2 iputils-ping net-tools \
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Systemd admin tooling
+RUN apt-get update && \
+ apt-get install -y \
+ systemd-container \
+ tmux \
+ fzf \
+ jq \
+ ripgrep \
+ bash-completion \
+ && apt-get clean && rm -rf /var/lib/apt/lists/*
+
+# Init setup for container machine
+RUN >/etc/machine-id && >/var/lib/dbus/machine-id
+
+RUN systemctl set-default multi-user.target && \
+ systemctl mask \
+ dev-hugepages.mount \
+ sys-fs-fuse-connections.mount \
+ systemd-update-utmp.service \
+ systemd-tmpfiles-setup.service \
+ console-getty.service && \
+ systemctl disable \
+ networkd-dispatcher.service
+
+# SSH config for remote access
+RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config && \
+ sed -i 's/^#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config
+
+# Admin directory structure
+RUN mkdir -p /etc/systemd-admin /etc/skel/.ssh/controlmasters
+
+# SSH client config template for new users (applies to host user via home mount)
+COPY ssh_config /etc/skel/.ssh/config
+RUN chmod 600 /etc/skel/.ssh/config
+
+# Copy hosts file and profile helpers
+COPY hosts /etc/systemd-admin/hosts
+COPY systemd-admin.sh /etc/profile.d/systemd-admin.sh
+RUN chmod 644 /etc/systemd-admin/hosts /etc/profile.d/systemd-admin.sh
diff --git a/2026/apple-container-machine-machines/apple-container-machine-skill.md b/2026/apple-container-machine-machines/apple-container-machine-skill.md
new file mode 100644
index 0000000..f0fd19b
--- /dev/null
+++ b/2026/apple-container-machine-machines/apple-container-machine-skill.md
@@ -0,0 +1,525 @@
+---
+name: apple-container-machine
+description: >
+ Use this skill when working with Apple's `container machine` subcommand on
+ Apple Silicon Macs — creating persistent Linux environments, building custom
+ machine images, managing lifecycle, setting up VSCode Remote-SSH development,
+ or configuring host integration (home-dir mount, SSH, DNS). Triggers on
+ "container machine", "container m", "machine create", "machine run",
+ "apple container machine", "container machine image", "m run", or any
+ request involving Apple's container tooling for persistent Linux environments.
+---
+
+# Apple Container Machine Skill
+
+## What This Is
+
+`container machine` (alias `m`) is a subcommand of Apple's [container](https://github.com/apple/container) tool — an open-source CLI that runs Linux containers as lightweight virtual machines on Apple Silicon Macs (macOS 26+). Unlike standard `container run` which models an application container, `container machine` models a **persistent Linux workstation environment**: it runs the image's init system (`/sbin/init`), maps your macOS user account and home directory into the VM, and survives stop/start cycles.
+
+**Key differences from `container run`:**
+- Runs `/sbin/init` (supports `systemd`, `openrc`, etc.) for long-running services
+- Auto-maps host username, UID, GID, and `$HOME` into the VM
+- Persistent filesystem across stop/start — data survives
+- Resources (CPU, memory) configurable at create time and resizeable later
+- One machine per target distro — test the same app under Alpine, Ubuntu, Debian
+
+## Prerequisites
+
+- Apple Silicon Mac (M1+)
+- macOS 26 recommended (macOS 15 has limitations: no container-to-container networking, no `container network` commands)
+- `container` tool installed and `container system start` has been run
+- Linux kernel installed (`container system kernel set --recommended`)
+
+## Quickstart
+
+```bash
+# Create a container machine from an OCI image
+container machine create alpine:3.22 --name dev
+
+# Run a command (boots automatically if stopped)
+container machine run -n dev whoami # your host username, not root
+container machine run -n dev pwd # your Mac home directory, mounted in
+
+# Open an interactive shell
+container machine run -n dev # login shell as your host user
+
+# List all machines
+container machine ls
+
+# Stop and delete
+container machine stop dev
+container machine rm dev
+```
+
+The alias `m` works everywhere `machine` does: `m ls`, `m run -n dev`, `m stop dev`.
+
+## Container Machine Lifecycle
+
+```
+pull image → create → boot → run commands / services → stop → delete
+```
+
+### Create
+
+```bash
+container machine create <image> --name <name> [--cpus N] [--memory N] [--home-mount rw|ro|none]
+```
+
+The image must contain `/sbin/init`. See "Image Requirements" below.
+
+- `--no-boot` — create without booting
+- `--set-default` — also set as the default machine
+- `--cpus` — virtual CPUs (default: half of host count)
+- `--memory` — RAM (default: half of host memory, e.g., `8G`, `16G`)
+- `--home-mount` — home directory access: `rw` (default, read-write), `ro` (read-only), `none` (no mount)
+
+### Boot
+
+A machine boots automatically on first `container machine run` or when `run` is invoked on a stopped machine. No separate boot command is needed.
+
+### Run Commands
+
+```bash
+container machine run -n <name> [command...] # run a single command and exit
+container machine run -n <name> # interactive login shell
+container machine run -n <name> -- <command...> # pass args after --
+container machine run -n <name> --root # run as root instead of host user
+container machine run -n <name> --detach <command> # run in background
+```
+
+**Quoting limitation.** `container machine run` wraps the command in a shell layer that re-interprets quoting. Complex shell constructs — `&&` chaining, heredocs (`<<`), pipes to the command, mixed quote styles — are unreliable. Use simple single commands; for multi-step work, write a script on the mounted home dir and invoke it:
+
+```bash
+# Reliable: simple command
+container machine run -n sdamin -- cat /etc/os-release
+
+# Unreliable: this will likely mangle quoting
+container machine run -n sdamin -- sh -c "systemctl status nginx && journalctl -u nginx -n 5"
+
+# Reliable workaround: source the script from the mounted home
+container machine run -n sdamin -- bash ~/projects/scripts/check.sh
+```
+
+Process flags: `-e KEY=value`, `--env-file`, `-u user`, `--uid`, `--gid`, `-w /workdir`, `-i` (interactive), `-t` (tty).
+
+### Inspect
+
+```bash
+container machine inspect <name> # JSON detail
+container machine ls # table: name, IP, cpus, memory, disk, state
+container machine logs <name> # container machine stdio logs
+container machine logs --boot <name> # VM boot log
+```
+
+### Stop
+
+```bash
+container machine stop <name> # graceful stop
+```
+
+### Configure (resize resources)
+
+```bash
+container machine set -n <name> cpus=4 memory=8G
+container machine stop <name>
+container machine run -n <name> # restart for changes to take effect
+```
+
+### Set Default
+
+```bash
+container machine set-default <name> # drop the -n flag for subsequent commands
+```
+
+### Delete
+
+```bash
+container machine stop <name>
+container machine rm <name> # removes machine AND persistent storage
+```
+
+If the deleted machine was the default, set a new one: `container machine set-default <new-name>`.
+
+## Image Requirements
+
+Container machines require OCI images that include `/sbin/init`. The machine runs the image's init system as PID 1 on boot.
+
+### Ubuntu 24.04 + systemd (canonical example)
+
+This Dockerfile builds a machine image with systemd and common tools:
+
+```dockerfile
+FROM ubuntu:24.04
+
+ENV container container
+
+RUN apt-get update && \
+ apt-get install -y \
+ dbus systemd openssh-server net-tools iproute2 iputils-ping curl wget vim-tiny man sudo && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/* && \
+ yes | unminimize
+
+RUN >/etc/machine-id
+RUN >/var/lib/dbus/machine-id
+
+RUN systemctl set-default multi-user.target
+RUN systemctl mask \
+ dev-hugepages.mount \
+ sys-fs-fuse-connections.mount \
+ systemd-update-utmp.service \
+ systemd-tmpfiles-setup.service \
+ console-getty.service
+RUN systemctl disable \
+ networkd-dispatcher.service
+
+RUN sed -i -e 's/^AcceptEnv LANG LC_\*$/#AcceptEnv LANG LC_*/' /etc/ssh/sshd_config
+```
+
+Build and create:
+
+```bash
+container build -t local/ubuntu-machine:latest -f Dockerfile .
+container machine create local/ubuntu-machine:latest --name ubuntu
+```
+
+### systemd on other distros
+
+The same pattern applies to any systemd-based distro (Debian, Fedora, Rocky Linux):
+- Set `ENV container container`
+- Install `dbus` and `systemd`
+- Create empty `/etc/machine-id` and `/var/lib/dbus/machine-id`
+- Set `multi-user.target` as default
+- Mask unnecessary services
+
+### init systems other than systemd
+
+Any Linux image with `/sbin/init` works. For Alpine (busybox-init) or distros with OpenRC, the init system handles PID 1 responsibilities.
+
+### First-boot provisioner
+
+By default, `container` runs a built-in setup script on first boot to create the user matching your host account. To use your own setup instead, add an executable script at `/etc/machine/create-user.sh` to the image. It runs once, as root, on first boot, with these environment variables:
+
+- `CONTAINER_GID` — group ID
+- `CONTAINER_HOME` — home directory path
+- `CONTAINER_MACHINE_ID` — machine UUID
+- `CONTAINER_UID` — user ID
+- `CONTAINER_USER` — username
+
+## Commands Reference
+
+`m` is the alias for `machine` — both forms work everywhere.
+
+| Command | Description |
+|---------|-------------|
+| `container machine create <image>` | Create and boot a machine. Flags: `--name`, `--cpus`, `--memory`, `--home-mount`, `--no-boot`, `--set-default`, `--scheme`, `--progress` |
+| `container machine run` | Run a command or open a shell in a machine (boots if stopped). Flags: `-n <name>`, `-d` (detach), `--root`, process flags (`-e`, `-u`, `-w`, `-i`, `-t`, `--gid`, `--uid`, `--env-file`) |
+| `container machine ls` | List machines. Flags: `--format json\|table`, `-q` |
+| `container machine inspect <name>` | JSON detail of a machine |
+| `container machine set <key=value...>` | Update config. Flags: `-n <name>`. Valid keys: `cpus`, `memory`, `home-mount`. Requires stop/start. |
+| `container machine set-default <name>` | Set the default machine |
+| `container machine stop <name>` | Stop a running machine |
+| `container machine logs <name>` | Show logs. Flags: `--boot`, `-f` (follow), `-n N` |
+| `container machine rm <name>` | Delete machine and persistent storage |
+
+## Host Integration
+
+### Home directory mount
+
+The container machine automatically mounts your macOS home directory inside the VM at `/Users/<username>`. Your repos, dotfiles, and SSH keys are available from both macOS and the Linux environment.
+
+```bash
+container machine create ... --home-mount rw # read-write (default)
+container machine create ... --home-mount ro # read-only
+container machine create ... --home-mount none # no mount
+```
+
+- The host user is automatically created in the machine with matching UID/GID
+- Edit on macOS with your native tools; compile and run in the Linux VM
+- Profilers, screenshot tools, and debuggers on macOS see the same files
+
+**Important: `$HOME` path mismatch.** The machine sets `$HOME` to `/home/<username>` inside the VM, but the actual mounted home is at `/Users/<username>`. Tools that resolve `~` relative to `$HOME` (SSH key lookup, config file discovery) may look in the wrong place. This most commonly breaks SSH identity file resolution. Workaround:
+
+```bash
+# Symlink the VM home .ssh to the mounted macOS .ssh
+container machine run -n <name> --root ln -s /Users/<username>/.ssh /home/<username>/.ssh
+```
+
+Or use absolute paths when referencing files on the mounted home.
+
+### SSH agent forwarding
+
+Add an SSH config entry so the machine can access your SSH agent:
+
+```bash
+cat >> ~/.ssh/config <<EOT
+
+Host *.machine
+ ForwardAgent yes
+ UserKnownHostsFile /dev/null
+EOT
+```
+
+### DNS domain setup
+
+Create a local DNS domain so you can reach machines by hostname:
+
+```bash
+sudo container system dns create machine
+```
+
+After this, `ping <machine-name>.machine` resolves to the machine's IP:
+
+```bash
+ping -c 1 ubuntu.machine
+```
+
+To remove the domain later:
+
+```bash
+sudo container system dns delete machine
+```
+
+### Passwordless sudo inside a machine
+
+Services started via `systemctl` inside the machine run as root by default. For interactive sudo access:
+
+```bash
+container machine run -n <name> sudo passwd $(whoami)
+# Enter a password, then use sudo normally in shells
+```
+
+## VSCode Remote-SSH Development
+
+This workflow lets you use Visual Studio Code with the Remote-SSH extension to develop code stored on your Mac, compiled and run inside the container machine.
+
+### Setup
+
+1. Create a machine with systemd and SSH:
+
+```dockerfile
+FROM ubuntu:24.04
+
+ENV container container
+
+RUN apt-get update && \
+ apt-get install -y \
+ dbus systemd openssh-server sudo curl wget git && \
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+RUN >/etc/machine-id
+RUN >/var/lib/dbus/machine-id
+
+RUN systemctl set-default multi-user.target
+RUN systemctl mask \
+ dev-hugepages.mount \
+ sys-fs-fuse-connections.mount \
+ systemd-update-utmp.service \
+ console-getty.service
+```
+
+2. Build and create:
+
+```bash
+container build -t ubuntu-machine:latest .
+container machine create --set-default --name ubuntu ubuntu-machine:latest
+```
+
+3. Set up DNS:
+
+```bash
+sudo container system dns create machine
+```
+
+4. Add SSH config:
+
+```bash
+cat >> ~/.ssh/config <<EOT
+
+Host ubuntu.machine
+ HostName ubuntu.machine
+ ForwardAgent yes
+ UserKnownHostsFile /dev/null
+EOT
+```
+
+5. Set a password for SSH:
+
+```bash
+container machine run -it sudo passwd $(whoami)
+```
+
+### Connect
+
+1. In VSCode, press Cmd-Shift-P and run **Remote-SSH: Connect to Host**
+2. Select `ubuntu.machine`
+3. Accept the SSH fingerprint
+4. Enter the password you configured
+5. In the new window, install any language extensions (Swift, Python, Rust, etc.) in the remote context
+
+Your macOS home directory is mounted at `/Users/<username>` inside the machine — all your repos are available.
+
+### Debugging
+
+Set breakpoints in VSCode, run the application inside the machine, and debug normally. The home-dir mount means compiled artifacts are visible to both the machine and macOS tools.
+
+### Clean up
+
+```bash
+container machine stop ubuntu
+container machine rm ubuntu
+container image rm ubuntu-machine:latest
+sudo container system dns delete machine
+# Remove the SSH config entry from ~/.ssh/config
+```
+
+## Resource Configuration
+
+### At create time
+
+```bash
+container machine create alpine:3.22 --name dev --cpus 4 --memory 8G
+```
+
+- Default CPUs: half of host count
+- Default memory: half of host memory
+- Memory format: `512M`, `2G`, `8G`, etc. (1 MiB granularity)
+
+### Post-creation resize
+
+```bash
+container machine set -n dev cpus=8 memory=16G
+container machine stop dev
+container machine run -n dev -- nproc # verify: 8
+```
+
+Changes only take effect after a stop/start cycle.
+
+### Check current resources
+
+```bash
+container machine inspect dev | jq '.cpus, .memoryInBytes'
+container machine ls # table shows CPUS and MEMORY
+```
+
+## Multi-Distro Workflows
+
+Create one container machine per target distribution. Each machine mounts the same `$HOME`, so your repos and dotfiles are available everywhere:
+
+```bash
+container machine create alpine:3.22 --name dev-alpine
+container machine create ubuntu:24.04 --name dev-ubuntu # uses /sbin/init from systemd
+container machine create debian:latest --name dev-debian
+```
+
+```bash
+container machine run -n dev-alpine apk add build-base
+container machine run -n dev-ubuntu apt-get install build-essential
+container machine run -n dev-debian apt-get install build-essential
+```
+
+Each machine runs its own init system under its own Linux VM, fully isolated. Test your application under different distributions and init systems from the same source tree.
+
+## Troubleshooting
+
+### Machine fails to boot
+
+Ensure a Linux kernel is installed:
+```bash
+container system kernel set --recommended
+```
+
+Check boot logs:
+```bash
+container machine logs --boot <name>
+```
+
+### DNS not resolving (`<name>.machine` unknown)
+
+Verify the DNS domain was created:
+```bash
+cat /etc/resolver/machine
+sudo container system dns create machine
+```
+
+Verify the machine is running and has an IP:
+```bash
+container machine ls
+```
+
+### SSH connection refused
+
+Ensure SSH is installed and running inside the machine:
+```bash
+container machine run -n <name> sudo systemctl status ssh
+container machine run -n <name> sudo systemctl enable --now ssh
+```
+
+On images without systemd, start sshd explicitly:
+```bash
+container machine run -n <name> sudo /usr/sbin/sshd
+```
+
+Check SSH config:
+```bash
+ssh -v ubuntu.machine
+```
+
+### Home-mount permission errors
+
+If the machine uses `--home-mount ro`, writes to `$HOME` will fail. Switch to `rw`:
+
+```bash
+container machine set -n <name> home-mount=rw
+container machine stop <name>
+container machine run -n <name>
+```
+
+### macOS 15 limitations
+
+On macOS 15, the following do not work:
+- Container-to-container networking over the virtual network
+- `container network` commands
+- `--network` flag on `container run`/`container create`
+
+Container machines still work but are limited to host-only networking.
+
+### Resource not updating after `container machine set`
+
+Changes to CPU/memory/home-mount only apply after a stop/start cycle:
+
+```bash
+container machine set -n dev cpus=4
+container machine stop dev # must run after set
+container machine run -n dev # boot with new config
+```
+
+### Machine appears but is unreachable
+
+Rare vmnet subnet conflict on macOS 15. If your machine has no network access:
+1. Stop the machine: `container machine stop <name>`
+2. Stop the container system: `container system stop`
+3. Start again: `container system start`
+4. Boot the machine: `container machine run -n <name>`
+
+## Common Pitfalls
+
+1. **macOS 15 network isolation**: Container-to-container communication does not work on macOS 15. Only the host can reach the container's IP.
+2. **No default kernel**: `container system start` prompts to install one automatically. If skipped, run `container system kernel set --recommended`.
+3. **SSH host key changes**: After deleting and recreating a machine with the same DNS name, `ssh` will warn about host key change. Remove the old key from `~/.ssh/known_hosts`.
+4. **Home-mount read-only**: `--home-mount ro` causes any write to `$HOME` to fail. Use `rw` unless you have a specific security reason.
+5. **Forgetting to stop before delete**: `container machine rm` stops the machine first, but data loss is still permanent.
+6. **Images without `/sbin/init`**: A container machine will not boot from a standard application image (e.g., `python:alpine`). Use images built with `/sbin/init`.
+7. **Disk space**: Each machine has its own persistent disk. Monitor usage with `container system df`.
+8. **Multiple machines sharing the same home directory**: Concurrent writes from multiple machines to the same files can cause conflicts. Use different working directories per machine.
+9. **`$HOME` is not `/Users/<user>`**: The machine sets `$HOME` to `/home/<user>` but the mounted macOS home is at `/Users/<user>`. SSH identity file paths in `~/.ssh/config` resolve relative to `$HOME` and will point to `/home/<user>/.ssh/` where keys don't exist. Symlink `/home/<user>/.ssh` to `/Users/<user>/.ssh` to fix.
+10. **Shell quoting with `container machine run`**: Avoid `&&`, heredocs, and pipes in commands passed to `container machine run`. The command wrapper re-interprets quoting. Use single commands or invoke scripts from the mounted home directory instead.
+
+## Sources
+
+- [Apple container GitHub repo](https://github.com/apple/container)
+- `docs/container-machine.md` — full container machine guide
+- `docs/command-reference.md` — Machine section with all flags
+- `docs/how-to.md` — container machines how-to
+- `examples/container-machine-vscode/README.md` — VSCode Remote-SSH walkthrough