feat: ship vfkit as #1 macOS engine with Lima parity - #55
Conversation
Close buildx, host.docker.internal, localhost proxies, and proxy apply on vfkit; promote vfkit numbers to the primary benchmark table. Co-authored-by: Cursor <cursoragent@cursor.com>
📝 WalkthroughWalkthroughThe PR makes vfkit the default macOS runtime, expands vfkit lifecycle, guest networking, proxy, Rosetta, and build behavior, updates guest provisioning and benchmark documentation, and releases version 0.9.6. Changesvfkit default runtime
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Runtime as Vfkit runtime
participant Guest as Guest Docker daemon
participant Proxy as Localhost proxy
Runtime->>Guest: Start guest and initialize runtime
Runtime->>Guest: Check Docker API and configure guest networking
Runtime->>Proxy: Apply configured proxy and start resync watcher
Guest-->>Runtime: Return status and published container ports
Runtime->>Proxy: Synchronize localhost forwarding
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (1)
backend/internal/runtime/vfkit_darwin.go (1)
427-444: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMutex unlocked manually instead of via
defer.
v.mu.Lock()/v.mu.Unlock()here are not paired withdefer, unlikeSetOwnerContext/resetLifecycleelsewhere in this file. Same pattern also appears inApplyProxy(Line 605-607). As per coding guidelines, "Release acquired resources with defer immediately after acquisition, including files, mutexes, and cancellation functions."🔧 Suggested fix
v.mu.Lock() + defer v.mu.Unlock() if v.watcherCancel != nil { v.watcherCancel() v.watcherCancel = nil } - v.mu.Unlock() v.localhostProxy.stopAll()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@backend/internal/runtime/vfkit_darwin.go` around lines 427 - 444, Update Stop and ApplyProxy to defer v.mu.Unlock() immediately after acquiring v.mu.Lock(), removing the manual unlock calls while preserving the existing critical-section behavior.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/internal/runtime/vfkit_darwin.go`:
- Around line 555-570: Update the proxyResync handling in Vfkit.ListContainers
to replace the separate Load and Store operations with an atomic
CompareAndSwap(true, false), while preserving the existing forced localhost
proxy synchronization behavior.
- Around line 310-336: Extract the duplicated background setup from both Start
branches into a shared startBackgroundServices helper, and invoke it from the
already-running and freshly-launched paths. Within the helper, read v.proxy
under v.mu into a local value before checking or passing it to ApplyProxy, while
preserving the existing lifecycle context, goroutines, state stores, and watcher
startup behavior.
- Around line 268-294: Replace the check-then-clear handling of the proxyResync
flag in watchPortProxies and the corresponding ListContainers flow with atomic
CompareAndSwap(true, false). Only clear the flag after the resync succeeds and
the compare-and-swap confirms it was still true, preserving any concurrent
request that sets it back to true.
- Around line 373-379: Update the enableRosetta override in the Rosetta
configuration block so CALF_VFKIT_ROSETTA=1 can only enable Rosetta when
goruntime.GOARCH is arm64. Preserve the existing default behavior and
CALF_VFKIT_ROSETTA=0 disable behavior on arm64.
- Around line 178-192: Update guestCommandRunner’s sudo handling to preserve the
bash -c script boundary when ensureBuildx passes sudo bash -c with its script
argument. Special-case that argv shape or quote each argument before
constructing the runGuestRoot command, while preserving existing behavior for
other sudo invocations.
In `@CHANGELOG.md`:
- Around line 14-18: Revise the vfkit feature-parity changelog entry to describe
only user-visible capabilities, removing implementation details such as dnsmasq,
gateway refresh, and guest terminology, plus the ::1 protocol notation. Revise
the public benchmarks entry to describe the updated benchmark presentation
without naming BENCHMARKS.md, while preserving the user-facing performance
result and Lima comparison.
---
Nitpick comments:
In `@backend/internal/runtime/vfkit_darwin.go`:
- Around line 427-444: Update Stop and ApplyProxy to defer v.mu.Unlock()
immediately after acquiring v.mu.Lock(), removing the manual unlock calls while
preserving the existing critical-section behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: bb09a6ab-d028-413a-ac3d-048bbdd3f95f
📒 Files selected for processing (9)
BENCHMARKS.mdCHANGELOG.mdROADMAP.mdbackend/internal/runtime/select_darwin.gobackend/internal/runtime/vfkit_darwin.gobackend/version/version.godocs/phase5-race.mdscripts/guest-image/lima-vfkit.yamlui/pubspec.yaml
| // guestCommandRunner adapts guest root shells and docker CLI for shared helpers (proxy, buildx install). | ||
| func (v *Vfkit) guestCommandRunner(ctx context.Context, command string, args ...string) ([]byte, error) { | ||
| if command == "nerdctl" || command == "docker" { | ||
| return v.runLocal(ctx, command, args...) | ||
| } | ||
| if command == "bash" && len(args) >= 2 && args[0] == "-lc" { | ||
| return v.runGuestRoot(ctx, args[1]) | ||
| } | ||
| if command == "sudo" && len(args) >= 1 { | ||
| joined := strings.Join(args, " ") | ||
| return v.runGuestRoot(ctx, joined) | ||
| } | ||
| all := append([]string{command}, args...) | ||
| return v.runGuestRoot(ctx, strings.Join(all, " ")) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect ensureBuildx's call pattern into the commandRunner to verify argument shapes.
rg -n -A15 'func ensureBuildx' backend/internal/runtimeRepository: enegalan/calf
Length of output: 1515
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the runtime helpers around guestCommandRunner and runGuestRoot.
sed -n '1,260p' backend/internal/runtime/vfkit_darwin.go
printf '\n--- buildx.go ---\n'
sed -n '1,120p' backend/internal/runtime/buildx.go
printf '\n--- proxy.go ---\n'
sed -n '1,220p' backend/internal/runtime/proxy.goRepository: enegalan/calf
Length of output: 12931
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find all commandRunner call sites to see whether the sudo/default branches
# receive arguments containing whitespace or shell metacharacters.
rg -n 'run\([^)]*"sudo"|run\([^)]*"bash"|run\([^)]*"docker"|run\([^)]*"nerdctl"' backend/internal/runtimeRepository: enegalan/calf
Length of output: 5310
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the shell-quoting helper and any runGuestRoot implementation details.
rg -n 'shellQuote|runGuestRoot|guestCommandRunner' backend/internal/runtimeRepository: enegalan/calf
Length of output: 1715
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Read the exact helper implementations and all commandRunner call sites.
sed -n '1,260p' backend/internal/runtime/vfkit_darwin.go
printf '\n--- buildx.go ---\n'
sed -n '1,120p' backend/internal/runtime/buildx.go
printf '\n--- proxy.go ---\n'
sed -n '1,220p' backend/internal/runtime/proxy.go
printf '\n--- commandRunner call sites ---\n'
rg -n -A2 -B2 'run\(ctx, "sudo"|run\(ctx, "bash"|run\(ctx, "docker"|run\(ctx, "nerdctl"' backend/internal/runtimeRepository: enegalan/calf
Length of output: 30047
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find the implementation and usages of runGuestRoot and shellQuote directly.
rg -n -A6 -B6 'func \(v \*Vfkit\) runGuestRoot|shellQuote\(' backend/internal/runtimeRepository: enegalan/calf
Length of output: 3641
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the exact vfkit helper implementations around the adapter.
sed -n '170,250p' backend/internal/runtime/vfkit_darwin.goRepository: enegalan/calf
Length of output: 2975
Quote the sudo command before re-entering the shell
ensureBuildx passes sudo bash -c "DEBIAN_FRONTEND=... && ...", but guestCommandRunner flattens args with strings.Join and then feeds them to bash -lc, so the bash -c script boundary is lost. Preserve argv boundaries here—either special-case sudo bash -c or quote each arg before joining.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/internal/runtime/vfkit_darwin.go` around lines 178 - 192, Update
guestCommandRunner’s sudo handling to preserve the bash -c script boundary when
ensureBuildx passes sudo bash -c with its script argument. Special-case that
argv shape or quote each argument before constructing the runGuestRoot command,
while preserving existing behavior for other sudo invocations.
| // watchPortProxies periodically resyncs localhost proxies with published container ports. | ||
| func (v *Vfkit) watchPortProxies(ctx context.Context) { | ||
| ticker := time.NewTicker(10 * time.Second) | ||
| defer ticker.Stop() | ||
| for { | ||
| select { | ||
| case <-ctx.Done(): | ||
| return | ||
| case <-ticker.C: | ||
| status, err := v.Status(ctx) | ||
| if err != nil || status.State != State(constants.RuntimeStateRunning) || !v.started.Load() { | ||
| continue | ||
| } | ||
| containers, err := listContainers(ctx, v.runLocal) | ||
| if err != nil { | ||
| v.proxyResync.Store(true) | ||
| continue | ||
| } | ||
| force := v.proxyResync.Load() | ||
| v.localhostProxy.sync(publishedTCPPorts(containers), force) | ||
| if force { | ||
| v.proxyResync.Store(false) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Lost-update race on proxyResync flag.
force := v.proxyResync.Load() followed later by v.proxyResync.Store(false) is a check-then-act sequence. If another goroutine sets proxyResync back to true in between (e.g. a concurrent Start()/Status()), this resets it to false anyway, silently swallowing the new forced-resync request. The same pattern exists in ListContainers (Line 562-566) — same root cause, worth fixing together with atomic.Bool.CompareAndSwap(true, false).
🔧 Suggested fix
- force := v.proxyResync.Load()
- v.localhostProxy.sync(publishedTCPPorts(containers), force)
- if force {
- v.proxyResync.Store(false)
- }
+ force := v.proxyResync.CompareAndSwap(true, false)
+ v.localhostProxy.sync(publishedTCPPorts(containers), force)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // watchPortProxies periodically resyncs localhost proxies with published container ports. | |
| func (v *Vfkit) watchPortProxies(ctx context.Context) { | |
| ticker := time.NewTicker(10 * time.Second) | |
| defer ticker.Stop() | |
| for { | |
| select { | |
| case <-ctx.Done(): | |
| return | |
| case <-ticker.C: | |
| status, err := v.Status(ctx) | |
| if err != nil || status.State != State(constants.RuntimeStateRunning) || !v.started.Load() { | |
| continue | |
| } | |
| containers, err := listContainers(ctx, v.runLocal) | |
| if err != nil { | |
| v.proxyResync.Store(true) | |
| continue | |
| } | |
| force := v.proxyResync.Load() | |
| v.localhostProxy.sync(publishedTCPPorts(containers), force) | |
| if force { | |
| v.proxyResync.Store(false) | |
| } | |
| } | |
| } | |
| } | |
| // watchPortProxies periodically resyncs localhost proxies with published container ports. | |
| func (v *Vfkit) watchPortProxies(ctx context.Context) { | |
| ticker := time.NewTicker(10 * time.Second) | |
| defer ticker.Stop() | |
| for { | |
| select { | |
| case <-ctx.Done(): | |
| return | |
| case <-ticker.C: | |
| status, err := v.Status(ctx) | |
| if err != nil || status.State != State(constants.RuntimeStateRunning) || !v.started.Load() { | |
| continue | |
| } | |
| containers, err := listContainers(ctx, v.runLocal) | |
| if err != nil { | |
| v.proxyResync.Store(true) | |
| continue | |
| } | |
| force := v.proxyResync.CompareAndSwap(true, false) | |
| v.localhostProxy.sync(publishedTCPPorts(containers), force) | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/internal/runtime/vfkit_darwin.go` around lines 268 - 294, Replace the
check-then-clear handling of the proxyResync flag in watchPortProxies and the
corresponding ListContainers flow with atomic CompareAndSwap(true, false). Only
clear the flag after the resync succeeds and the compare-and-swap confirms it
was still true, preserving any concurrent request that sets it back to true.
| lifeCtx := v.resetLifecycle() | ||
| if v.dockerAPIReady(ctx) && v.processAlive() { | ||
| v.ensureHostMountSymlink(ctx) | ||
| v.ensureBuildxAsync(lifeCtx) | ||
| if os.Getenv("CALF_BENCHMARK") != "1" { | ||
| go func() { | ||
| setupCtx, cancel := context.WithTimeout(lifeCtx, constants.DefaultActionTimeout) | ||
| defer cancel() | ||
| v.ensureHostDockerInternal(setupCtx) | ||
| }() | ||
| } | ||
| if v.proxy != (ProxyConfig{}) { | ||
| proxy := v.proxy | ||
| go func() { | ||
| applyCtx, cancel := context.WithTimeout(lifeCtx, constants.DefaultActionTimeout) | ||
| defer cancel() | ||
| if err := v.ApplyProxy(applyCtx, proxy); err != nil { | ||
| if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) { | ||
| return | ||
| } | ||
| vfkitLogger.Warn("proxy application during start failed (non-fatal)", "error", err) | ||
| } | ||
| }() | ||
| } | ||
| v.started.Store(true) | ||
| v.proxyResync.Store(true) | ||
| go v.watchPortProxies(lifeCtx) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
Data race on v.proxy, plus duplicated background-setup block.
v.proxy != (ProxyConfig{}) and proxy := v.proxy are read here without holding v.mu, while ApplyProxy (Line 605-607) writes v.proxy under the lock. Two goroutines calling Start and ApplyProxy concurrently race on this field.
Separately, this entire background-setup sequence (host mount symlink, buildx bootstrap, host.docker.internal goroutine, proxy-apply goroutine, started/proxyResync stores, watcher start) is duplicated verbatim between the "already running" branch (310-336) and the "freshly launched" branch (399-424). Extracting a single helper would fix the race in one place and prevent the two paths from silently diverging in the future.
🔒 Suggested fix: extract a shared helper and lock the proxy read
+// startBackgroundServices kicks off buildx bootstrap, host.docker.internal setup,
+// and pending proxy application once the guest is confirmed reachable.
+func (v *Vfkit) startBackgroundServices(ctx, lifeCtx context.Context) {
+ v.ensureHostMountSymlink(ctx)
+ v.ensureBuildxAsync(lifeCtx)
+ if os.Getenv("CALF_BENCHMARK") != "1" {
+ go func() {
+ setupCtx, cancel := context.WithTimeout(lifeCtx, constants.DefaultActionTimeout)
+ defer cancel()
+ v.ensureHostDockerInternal(setupCtx)
+ }()
+ }
+ v.mu.Lock()
+ proxy := v.proxy
+ v.mu.Unlock()
+ if proxy != (ProxyConfig{}) {
+ go func() {
+ applyCtx, cancel := context.WithTimeout(lifeCtx, constants.DefaultActionTimeout)
+ defer cancel()
+ if err := v.ApplyProxy(applyCtx, proxy); err != nil {
+ if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
+ return
+ }
+ vfkitLogger.Warn("proxy application during start failed (non-fatal)", "error", err)
+ }
+ }()
+ }
+ v.started.Store(true)
+ v.proxyResync.Store(true)
+ go v.watchPortProxies(lifeCtx)
+}Then call v.startBackgroundServices(ctx, lifeCtx) from both branches instead of duplicating the block.
Also applies to: 399-424
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/internal/runtime/vfkit_darwin.go` around lines 310 - 336, Extract the
duplicated background setup from both Start branches into a shared
startBackgroundServices helper, and invoke it from the already-running and
freshly-launched paths. Within the helper, read v.proxy under v.mu into a local
value before checking or passing it to ApplyProxy, while preserving the existing
lifecycle context, goroutines, state stores, and watcher startup behavior.
| // Rosetta on by default for arm64 (match Lima); disable with CALF_VFKIT_ROSETTA=0. | ||
| rosettaEnv := strings.TrimSpace(os.Getenv("CALF_VFKIT_ROSETTA")) | ||
| enableRosetta := goruntime.GOARCH == "arm64" && rosettaEnv != "0" | ||
| if rosettaEnv == "1" { | ||
| enableRosetta = true | ||
| } | ||
| if enableRosetta { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does vfkit support the rosetta virtio device on x86_64 hosts?
💡 Result:
No, vfkit does not support the Rosetta virtio device on x86_64 hosts [1][2]. Rosetta support in vfkit is specifically designed to run x86_64 binaries within Linux virtual machines on Apple Silicon (ARM64) hosts [1][3][2]. According to the official documentation and technical specifications for vfkit, the --device rosetta option is exclusive to machines with Apple CPUs [1]. If this option is used on an Intel-based (x86_64) machine, vfkit will fail with an error [1]. This functionality leverages Apple's Virtualization framework to provide cross-architecture execution, which is not applicable or supported on x86_64 hardware [1][4][5].
Citations:
- 1: https://github.com/crc-org/vfkit/blob/main/doc/usage.md
- 2: https://microvm-nix.github.io/microvm.nix/microvm-options.html
- 3: vfkit: Rosetta support - run amd64 containers on Apple silicon macs kubernetes/minikube#20559
- 4: https://crc.dev/blog/Container%20Plumbing%202023%20-%20vfkit%20-%20A%20minimal%20hypervisor%20using%20Apple%27s%20virtualization%20framework.pdf
- 5: https://github.com/crc-org/vfkit
Gate the Rosetta override on arm64
CALF_VFKIT_ROSETTA=1 should still respect the arm64 check; otherwise vfkit will try to add an unsupported Rosetta device on Intel hosts and fail to start.
Suggested fix
- if rosettaEnv == "1" {
- enableRosetta = true
- }
+ if rosettaEnv == "1" && goruntime.GOARCH == "arm64" {
+ enableRosetta = true
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Rosetta on by default for arm64 (match Lima); disable with CALF_VFKIT_ROSETTA=0. | |
| rosettaEnv := strings.TrimSpace(os.Getenv("CALF_VFKIT_ROSETTA")) | |
| enableRosetta := goruntime.GOARCH == "arm64" && rosettaEnv != "0" | |
| if rosettaEnv == "1" { | |
| enableRosetta = true | |
| } | |
| if enableRosetta { | |
| // Rosetta on by default for arm64 (match Lima); disable with CALF_VFKIT_ROSETTA=0. | |
| rosettaEnv := strings.TrimSpace(os.Getenv("CALF_VFKIT_ROSETTA")) | |
| enableRosetta := goruntime.GOARCH == "arm64" && rosettaEnv != "0" | |
| if rosettaEnv == "1" && goruntime.GOARCH == "arm64" { | |
| enableRosetta = true | |
| } | |
| if enableRosetta { |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/internal/runtime/vfkit_darwin.go` around lines 373 - 379, Update the
enableRosetta override in the Rosetta configuration block so
CALF_VFKIT_ROSETTA=1 can only enable Rosetta when goruntime.GOARCH is arm64.
Preserve the existing default behavior and CALF_VFKIT_ROSETTA=0 disable behavior
on arm64.
| func (v *Vfkit) ListContainers(ctx context.Context) ([]Container, error) { | ||
| return emptyIfStopped(ctx, v.Status, func(ctx context.Context) ([]Container, error) { return listContainers(ctx, v.runLocal) }) | ||
| return emptyIfStopped(ctx, v.Status, func(ctx context.Context) ([]Container, error) { | ||
| if !v.started.Load() { | ||
| return []Container{}, nil | ||
| } | ||
| containers, err := listContainers(ctx, v.runLocal) | ||
| if err == nil { | ||
| force := v.proxyResync.Load() | ||
| v.localhostProxy.sync(publishedTCPPorts(containers), force) | ||
| if force { | ||
| v.proxyResync.Store(false) | ||
| } | ||
| } | ||
| return containers, err | ||
| }) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Same proxyResync lost-update race as watchPortProxies.
Line 562-566 repeats the Load-then-Store pattern flagged at Line 268-294; same fix (CompareAndSwap(true, false)) applies here.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/internal/runtime/vfkit_darwin.go` around lines 555 - 570, Update the
proxyResync handling in Vfkit.ListContainers to replace the separate Load and
Store operations with an atomic CompareAndSwap(true, false), while preserving
the existing forced localhost proxy synchronization behavior.
| - **vfkit feature parity** — buildx builds, `host.docker.internal` (dnsmasq + gateway refresh), localhost `::1` port proxies, HTTP proxy apply inside the guest, and Rosetta on by default on Apple silicon (`CALF_VFKIT_ROSETTA=0` to disable). | ||
|
|
||
| ### Changed | ||
|
|
||
| - **Public benchmarks** — the primary `BENCHMARKS.md` table now uses the vfkit engine (Calf leads or ties OrbStack on every metric on the reference Mac); Lima numbers move to a legacy section. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove implementation details, file paths, and protocol jargon from changelog entries.
The changelog entries include implementation details (dnsmasq + gateway refresh, inside the guest), protocol jargon (::1), and file paths (BENCHMARKS.md). As per coding guidelines, changelog entries must be written in user-facing terms, excluding these details.
Consider revising the entries to focus on the user-visible impact.
📝 Proposed fix
- - **vfkit feature parity** — buildx builds, `host.docker.internal` (dnsmasq + gateway refresh), localhost `::1` port proxies, HTTP proxy apply inside the guest, and Rosetta on by default on Apple silicon (`CALF_VFKIT_ROSETTA=0` to disable).
+ - **Fast-boot engine parity** — Added support for `docker buildx`, `host.docker.internal` resolution, IPv6 localhost port forwarding, HTTP proxy configuration, and enabled Apple Rosetta by default (`CALF_VFKIT_ROSETTA=0` to disable).
### Changed
- - **Public benchmarks** — the primary `BENCHMARKS.md` table now uses the vfkit engine (Calf leads or ties OrbStack on every metric on the reference Mac); Lima numbers move to a legacy section.
+ - **Public benchmarks** — the primary performance comparison now uses the fast-boot engine (Calf leads or ties OrbStack on every metric on the reference Mac); previous engine results are moved to a legacy section.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - **vfkit feature parity** — buildx builds, `host.docker.internal` (dnsmasq + gateway refresh), localhost `::1` port proxies, HTTP proxy apply inside the guest, and Rosetta on by default on Apple silicon (`CALF_VFKIT_ROSETTA=0` to disable). | |
| ### Changed | |
| - **Public benchmarks** — the primary `BENCHMARKS.md` table now uses the vfkit engine (Calf leads or ties OrbStack on every metric on the reference Mac); Lima numbers move to a legacy section. | |
| - **Fast-boot engine parity** — Added support for `docker buildx`, `host.docker.internal` resolution, IPv6 localhost port forwarding, HTTP proxy configuration, and enabled Apple Rosetta by default (`CALF_VFKIT_ROSETTA=0` to disable). | |
| ### Changed | |
| - **Public benchmarks** — the primary performance comparison now uses the fast-boot engine (Calf leads or ties OrbStack on every metric on the reference Mac); previous engine results are moved to a legacy section. |
🧰 Tools
🪛 LanguageTool
[uncategorized] ~14-~14: Did you mean the proper noun “Apple Silicon”?
Context: ...the guest, and Rosetta on by default on Apple silicon (CALF_VFKIT_ROSETTA=0 to disable). #...
(APPLE_PRODUCTS)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CHANGELOG.md` around lines 14 - 18, Revise the vfkit feature-parity changelog
entry to describe only user-visible capabilities, removing implementation
details such as dnsmasq, gateway refresh, and guest terminology, plus the ::1
protocol notation. Revise the public benchmarks entry to describe the updated
benchmark presentation without naming BENCHMARKS.md, while preserving the
user-facing performance result and Lima comparison.
Source: Coding guidelines
Summary
Test plan
Made with Cursor
Summary by CodeRabbit
New Features
host.docker.internalaccess, BuildKit/buildx support, and Rosetta configuration on Apple Silicon.Documentation
Release