Skip to content

OrbStack Isolated Machine Backend

Status: Open — research and design proposal

jackin’ works well on macOS through Docker-compatible backends, and many macOS operators already use OrbStack as their Docker engine. OrbStack now also exposes isolated Linux machines that are useful for AI agent sandboxing. That creates a pragmatic path between today’s Docker container boundary and a future Kubernetes or true microVM backend.

The question is not “should jackin’ stop using Docker?” The desired backend is:

run the jackin’ role environment inside an OrbStack isolated machine, while preserving Docker workflows inside that machine when the role needs them.

This matters because jackin’ roles often expect Docker-in-Docker style behavior: the agent may build sibling containers, run Compose, use Testcontainers, or inspect Docker state while working. An OrbStack backend that cannot provide a private Docker-compatible engine would not satisfy the current role contract.

Official OrbStack documentation describes:

  • Architecture: OrbStack uses a lightweight Linux virtual machine with a shared kernel. Its Linux machines are not independent VMs in the strict sense, even though they behave like traditional Linux machines for most development workflows.
  • Linux machines: machines run full Linux distros with init systems such as systemd, OpenRC, or runit; commands can be executed through the orb CLI.
  • Machine commands: orb can create, start, stop, delete, inspect, run commands in, push files to, and pull files from machines.
  • Cloud-init: orb create supports -c / --user-data cloud-init files.
  • File sharing: normal machines expose Mac files under the same /Users/... path and /mnt/mac.
  • Machine networking: services in machines are reachable from macOS at localhost, and machines also receive *.orb.local names.
  • Release notes: v2.1.0 added isolated machines without filesystem integration, explicitly called useful for AI agent sandboxing; v2.1.1 added selective file sharing mounts in isolated machines; v2.1.2 added a setting to block network connections to host/machines and opt-in SSH agent forwarding.

Important correction: this roadmap item should not call OrbStack isolated machines “microVMs” in jackin’ product language. OrbStack itself says machines share a kernel and are not strictly independent VMs. The right label is orbstack-isolated or OrbStack isolated machine backend.

A research pass against the OrbStack release notes, settings docs, and the orbstack/orbstack issue tracker established what is documented and what is not. Current stable is v2.1.3 (May 10, 2026) per the release notes; v2.1.3 fixed proxy support and rseq compatibility but did not change the isolated-machine surface.

Flag / capabilityConfirmedSource
orb create --isolated <distro> <name> creates an isolated machineorbstack/orbstack#2409 (maintainer kdrag0n showed orbctl create -a arm64 ubuntu:questing isolated-too --isolated --mount ... invocation) and v2.1.0 release notes
Selective shares use --mount "host_abs_path:container_path", repeatablesame issue thread (error path “mount source must be an absolute path” confirms the syntax) and v2.1.1 release notes
Read-only enforcement at the share layer (:ro suffix or equivalent)❌ undocumentedRelease notes, settings page, file-sharing docs, and issue tracker have no mention. Treat as unsupported until verified with orb create --help on an installed 2.1.3
Network host/machine blocking (v2.1.2 setting)⚠️ unspecified CLIRelease notes call it “Added setting to block network connections to host/machines”. No documented orb config set key. Likely GUI-only as of 2.1.3
SSH agent forwarding per-machine opt-in (v2.1.2)⚠️ unspecified CLIRelease notes describe the opt-in but do not name the flag. Needs orb create --help verification
OrbStack auto-update can be disabled❌ not possibleorbstack/orbstack#577 — maintainer kdrag0n: “no plans to allow manually disabling update checks for now.” Installing the update is the operator’s choice, but the check runs unconditionally
Resource limits per-machine❌ global onlySettings docs: orb config set memory_mib and orb config set cpu are documented as global percentages
orb push / orb pull work on isolated machines❌ broken in 2.1.3orbstack/orbstack#2469 — pull/push route through /mnt/mac which isolated machines block. kdrag0n: “Fixed for the next version” (post-2.1.3)
orb -m <name> <cmd> PTY / SIGWINCH / detach-key behavior❌ undocumentedOrbStack docs page /machines/ shows the invocation but says nothing about PTY allocation, window-resize forwarding, or detach. No issues mention SIGWINCH or PTY behavior in the orbstack/orbstack tracker. Empirical test required before committing to this as the interactive-session transport
Multi-user / multi-operator on one host❌ undocumentedOrbStack is a per-user macOS app; multi-user concurrency is not documented as supported

The CLI-verified facts shift several design decisions in this roadmap from “research” to “build against” or “block on workaround”. See Decided In This Pass and Open Before Implementation Can Start below.

Add an experimental macOS backend that:

  1. Creates one OrbStack isolated machine per jackin’ instance.
  2. Shares only the operator-approved workspace/global mounts into that machine.
  3. Installs or starts a Docker-compatible engine inside the machine when the role needs Docker workflows.
  4. Runs the jackin’ role container inside the machine-owned Docker engine, with jackin-capsule as PID 1 exactly as the Docker backend does today.
  5. Preserves attach, reconnect, inspect, eject, purge, and state-recovery semantics.
  6. Reports the exact boundary and residual risks in the launch/session contract.
  • Do not replace Docker hardening as the near-term default.
  • Do not claim Docker Sandboxes parity. OrbStack does not provide jackin’ with Docker Sandboxes’ host-side credential proxy or per-domain egress policy by itself.
  • Do not call this backend a true microVM boundary.
  • Do not silently enable global Mac filesystem integration. The provider must use isolated machines with explicit shares only.
  • Do not mutate OrbStack global settings, macOS firewall settings, SSH config, or host Docker context silently.
  • Do not pursue Kubernetes support in this item. Kubernetes remains future platform expansion.

Use a backend name that reflects the actual provider:

docker
orbstack-isolated

Example future CLI:

Terminal window
jackin load the-architect . --backend orbstack-isolated
jackin load reviewer . --backend auto --docker-profile hardened

Example future config:

[runtime]
default_backend = "docker"
[runtime.orbstack]
enabled = true
default_distro = "ubuntu"
require_isolated = true
block_host_network = true
ssh_agent = "off" # off | ask | on
[workspaces.my-service.runtime]
backend = "orbstack-isolated"

The exact schema must be designed with the backend abstraction. If it touches versioned config files, it must ship with the required migrations and fixtures.

Current Docker flow:

host Docker -> build role image
host Docker -> create per-agent network
host Docker -> start docker:dind sidecar
host Docker -> start role container
role container -> talks to DinD over TLS

Proposed OrbStack isolated flow:

host Docker or selected builder -> build role image
host -> export image artifact or push to local registry
host -> orb create --isolated jackin-<instance> -c cloud-init.yml
host -> share only approved mount sources into the machine
machine -> install/start Docker engine if needed
host -> transfer/import role image into machine Docker
machine Docker -> start role container with jackin-capsule
role container -> talks to machine-local Docker when required
host -> attach via orb command/SSH/tunnel to jackin-capsule

The key property: Docker remains inside the isolated machine boundary for agent workflows. The host Docker daemon is only a build/export helper unless a later phase moves builds into the machine.

jackin’ role environments are Docker-shaped:

  • roles are authored as Dockerfiles
  • the construct image provides the common base environment
  • agents expect Docker CLI and Compose in many workflows
  • Testcontainers and local service stacks need a Docker-compatible daemon
  • operators want to inspect what the agent built and ran

Running Docker inside the isolated machine preserves that model while moving the runtime daemon out of the host Docker engine.

There are two possible modes:

ModeDescriptionWhen useful
containerized-roleImport the role image and run it as a container inside machine Docker.Best fit for current jackin’ architecture.
machine-native-roleProvision the role environment directly in the machine without an outer role container.Future research only; breaks more assumptions.

Start with containerized-role.

OptionFlowProsCons
Host build + docker save + stream into machine via orb -m ... sh -c 'cat > /tmp/img.tar' + docker loadBuild with current host Docker, export archive, stream-import inside machine.Smallest change to existing build path. Avoids the broken orb push path on isolated machines.Image archives can be large; transfer cost on every rebuild. Stream path is awkward but works today.
Host build + orb push + machine docker loadBuild with current host Docker, push tarball, import inside machine.Simpler than stream.Broken on isolated machines in v2.1.3 per orbstack/orbstack#2469. Available once OrbStack ships the fix.
Host build + local registry + machine docker pullPush image to a host-local or OrbStack-reachable registry.Better layer reuse; aligns with future workspace registry cache.Requires registry lifecycle and network policy design. Registry must be reachable from inside the isolated machine — needs network-block setting awareness.
Machine-local buildCopy role repo/build context into machine and run Docker build there.Strongest boundary for build-time code execution.Slower first implementation; more file-sync complexity. Same orb push workaround needed for the build context.

Recommended phased path:

  1. V1: host build + stream-import via docker save <img> | orb -m <machine> sh -c 'cat > /tmp/img.tar && docker load -i /tmp/img.tar && rm /tmp/img.tar'. Works today on v2.1.3. Latency cost: full image archive transferred per rebuild. Acceptance budget: first-launch import under 60 s for a typical 1.5-2 GB role image; subsequent reattach should not re-import.
  2. V2: workspace registry cache integration for layer reuse once Workspace registry cache lands. Image transfer cost should drop to changed layers only.
  3. V3: machine-local builds for roles where build-time isolation matters. Build context still needs the stream workaround until orb push is fixed for isolated machines.

V1 is intentionally honest: runtime isolation improves but the host build path still executes role Dockerfiles, and the stream-import path is awkward enough that operators should expect noticeable first-launch latency. Document both honestly in the launch contract.

Use cloud-init because OrbStack supports it directly with orb create -c.

Provisioning responsibilities:

  • create a jackin user or use the OrbStack-created user with explicit sudo policy
  • install Docker engine and Compose where the selected distro does not include them
  • create /jackin/ directories for runtime, state, run, host, and agent state
  • install or copy jackin-capsule
  • configure the machine-local Docker daemon
  • install basic debugging tools needed for hardline --inspect
  • write a backend marker so support logs can identify the instance

All jackin’-owned paths inside the role container still follow the /jackin/ container convention. Inside the OrbStack machine itself, jackin’-owned machine state should also live under /jackin/ unless a third-party package forces a standard path.

OrbStack isolated machines are useful only if jackin’ uses selective file sharing instead of default full macOS filesystem integration.

Design requirements:

  • Every workspace/global mount must become an explicit machine share (orb create --isolated <name> --mount "<host_abs_path>:<container_path>", repeatable).
  • Read-only enforcement at the share layer is unconfirmed. The OrbStack docs and release notes are silent on a :ro modifier for --mount. Until verified on a real 2.1.3 install, jackin’ must enforce read-only at the inner Docker layer (docker run -v src:dst:ro) and report enforcement as guest-enforced in the launch contract. If OrbStack adds host-layer enforcement later, the contract upgrades to host-enforced.
  • Mount destination control remains a jackin’ feature. OrbStack’s --mount "host_abs:container_path" allows a different container-side path, so jackin’ can map at the requested dst directly.
  • Sensitive mount warnings still apply before launch.
  • Worktree/clone isolation materializes on the host first today. The machine share then exposes the materialized worktree path. Tension: an isolated-machine premise of “only declared shares” is partly defeated if the worktree’s host-side directory leaks structural information (sibling directories, parent path). Resolution: jackin’ should materialize worktrees inside ~/.jackin/data/<instance>/worktrees/ (already the convention) and only share that specific subdirectory, not its parents. Moving worktree materialization inside the machine (V3+) tightens this further.

Potential path model:

host source: /Users/operator/Projects/app
machine share: /Users/operator/Projects/app
role container: /workspace/app

For same-path mounts:

host source: /Users/operator/Projects/app
machine share: /Users/operator/Projects/app
role container: /Users/operator/Projects/app

The backend must decide when to install/start Docker.

Suggested capability flag:

[runtime.docker]
requires_inner_engine = true

If absent, use today’s compatibility default: true for existing roles until role authors can opt out.

Engine options:

OptionFit
distro docker.io packageGood first prototype; simple cloud-init install.
Docker CE apt repoMore current engine, but more bootstrap complexity.
rootless Docker inside machineStronger defense in depth, but needs testing with role workflows.
containerd-onlyFuture research; less compatible with current role expectations.

The machine Docker daemon should be private to the machine and role. Do not forward its socket to macOS by default.

The Docker backend currently relies on Docker container state for much of launch, reconnect, and cleanup behavior. OrbStack requires a backend-neutral instance registry.

Per-instance metadata should include:

instance_id
backend = "orbstack-isolated"
machine_name
machine_distro
role_container_name
role_image_ref
workspace_name or ad-hoc path
mounts and share handles
agent state handles
inner_engine_enabled
created_at
last_seen_at
cleanup_state

Lifecycle mapping:

jackin’ operationOrbStack action
launchcreate/start machine, provision, import image, run role container.
attachconnect to jackin-capsule inside the role container through orb or SSH.
inspectorb info, machine Docker inspect, capsule status, mount/share summary.
stopstop role container, then optionally stop machine.
ejectstop role container and machine, remove transient resources.
purgedelete machine, remove state, remove imported images if owned by jackin.
hardlinerestart machine if needed, verify role container, reconnect capsule.

Open design question: whether one machine maps to one jackin’ instance, one workspace, or one workspace plus many instances. V1 should use one machine per instance because it is easier to reason about cleanup and containment.

OrbStack release notes say isolated machines can block network connections to host/machines as of v2.1.2. jackin’ should use that when the operator requests it, but it is not enough for full egress policy.

Network requirements:

  • Services started in the machine may be reachable from macOS at localhost; the session contract must list exposed ports.
  • {machine}.orb.local names exist; jackin’ should not assume they are private.
  • host.orb.internal can reach Mac services in normal machine networking; a strict profile should block or report this path.
  • The backend should enable the isolated-machine “block host/machines” setting when block_host_network = true, if OrbStack exposes it through CLI/config.
  • Domain allowlists and request logs still belong to Network egress policy.

Do not claim host-enforced egress policy until jackin’ owns a host-side proxy or OrbStack exposes a policy surface that can be inspected and controlled per machine.

OrbStack normal machines forward SSH agent access by default, but v2.1.2 release notes say isolated machines have opt-in SSH agent forwarding. jackin’ must keep that opt-in.

Rules:

  • SSH agent forwarding defaults to off for this backend.
  • If an operator enables SSH agent forwarding, surface it in the launch summary.
  • Do not rely on OrbStack’s default Mac command integration for agent workflows.
  • Do not let the machine run arbitrary macOS commands through mac as part of normal role execution.
  • Existing jackin’ auth forwarding modes still apply to the role container, but stricter profiles should prefer future brokered credentials.

Launch output should say:

backend: orbstack-isolated
provider: OrbStack
machine: jackin-abcd1234
isolation: OrbStack isolated machine, shared-kernel architecture
host filesystem: explicit shares only
inner Docker: enabled, machine-local daemon
host Docker socket: not mounted
host/machine network: blocked | open | unknown
SSH agent forwarding: off | on
network egress policy: open | deny | allowlist | partial
residual risk:
OrbStack machines share a Linux kernel; this is not hypervisor-per-agent
isolation. Runtime build may still happen on host Docker in V1.

The “shared-kernel” line is important. It avoids using microVM language for a backend that OrbStack itself documents differently.

ConcernDocker backendOrbStack isolated backend
Default platformmacOS/Linux Docker contextsmacOS only
PackagingDockerfile/OCI imageDockerfile/OCI image
Runtime boundaryrole container on selected Docker enginerole container inside an isolated OrbStack machine
Inner DockerDinD sidecarmachine-local Docker daemon
Host filesystemexplicit Docker bind mountsexplicit isolated-machine shares, then inner Docker mounts
Host Docker socketnot mountednot mounted
Kernel boundaryshared with Docker VM/Linux hostshared OrbStack Linux kernel
Network policyper-agent network today; egress policy futureisolated-machine host/machine blocking plus future egress policy
Startup complexitylowhigher: machine provisioning plus image import
Best usedefault local devstronger macOS local containment without leaving Docker workflows
  • Verify exact orb create --isolated CLI flags and selective share flags.
  • Verify whether the “block host/machines” setting is scriptable per machine.
  • Verify isolated-machine SSH agent forwarding defaults and CLI control.
  • Create a machine manually, install Docker, import a jackin’ role image, and run jackin-capsule inside the role container.
  • Record cold-start, warm-start, image import, and attach latency.

Phase 1 — backend-neutral instance registry

Section titled “Phase 1 — backend-neutral instance registry”
  • Extract backend kind/provider fields from Docker-specific naming.
  • Persist OrbStack machine handles separately from Docker container names.
  • Teach hardline --inspect to report local manifest state even when the backend is not Docker.
  • Add orbstack-isolated as a hidden or experimental backend.
  • Shell out to orb for machine lifecycle.
  • Use cloud-init to install Docker and create /jackin/ directories.
  • Use host-build + image archive import for role images.
  • Run the role container inside the machine Docker engine.

Phase 3 — attach, reconnect, eject, purge

Section titled “Phase 3 — attach, reconnect, eject, purge”
  • Attach to capsule inside the role container.
  • Reconnect to stopped/running machines.
  • Cleanly stop/delete machines on eject/purge.
  • Preserve isolated worktree cleanup behavior.

Phase 4 — security controls and contract output

Section titled “Phase 4 — security controls and contract output”
  • Add launch/session contract output for machine isolation, shares, inner Docker, network host/machine blocking, SSH agent forwarding, and residual risk.
  • Connect Docker profile selection to the role container running inside the machine.
  • Fail closed when the operator requests isolated behavior but OrbStack cannot enforce it.
  • Integrate with Workspace registry cache or a machine-local image cache.
  • Reuse provisioned base machines if doing so does not blur containment.
  • Add disk/resource visibility to the console resource panel.

Backend operations should emit debug_log!("orbstack", …) (the existing macro in src/tui/mod.rs) so the firehose under JACKIN_DEBUG=1 reconstructs the full machine lifecycle. Minimum required lines:

  • orbstack version=<orb --version> isolated_supported=<yes|no> (probed at launch)
  • machine_create name=<name> distro=<distro> isolated=<yes|no> shares=<count>
  • machine_share host=<abs_path> guest=<path> mode=<rw|ro> enforcement=<host|guest>
  • cloud_init source=<path> bytes=<n>
  • image_transfer mode=<stream|orb-push|registry> bytes=<n> elapsed_ms=<n>
  • inner_docker installed=<yes|no> version=<v>
  • network_block host_machines=<on|off|unknown> (when CLI control becomes available)
  • ssh_agent_forward=<on|off>
  • attach transport=<orb-exec|ssh|tunnel> pty=<yes|no>
  • machine_state action=<stop|delete|gc> result=<ok|failure> reason=<…>

Compact line per launch (future clog! tier):

orbstack launch machine=jackin-<id> isolated=yes shares=3 inner_docker=yes block_host_network=on image_import_ms=44230

This backend introduces these versioned-config additions; each lands with a CURRENT_*_VERSION bump per AGENTS.md:

SurfaceFile kindType touchedAction
[runtime.orbstack] enabled / default_distro / require_isolated / block_host_network / ssh_agentconfig.tomlAppConfigbump CURRENT_CONFIG_VERSION, add fixture under tests/fixtures/migrations/config/from-<predecessor>/.
[workspaces.X.runtime] backend = "orbstack-isolated"~/.config/jackin/workspaces/<name>.tomlWorkspaceConfigbump CURRENT_WORKSPACE_VERSION.
[runtime.docker] requires_inner_engine = false (shared with hardening contract)jackin.role.tomlRoleManifestbump CURRENT_MANIFEST_VERSION.

All three are additive. The instance manifest at src/instance/manifest.rs also needs a backend_kind/provider_kind axis split, which is per-instance metadata (not a versioned schema in the AGENTS.md sense) but still needs a forward-compatible default for existing Docker-only instances.

  • Backend name is orbstack-isolated, not microvm-orbstack (OrbStack documents shared-kernel architecture).
  • One machine per jackin’ instance for V1 (easier cleanup and containment than per-workspace).
  • V1 image flow: host build + docker save | orb -m <name> sh -c 'cat > /tmp/img.tar && docker load && rm'. Avoids broken orb push on isolated machines.
  • Mount transport: --mount "host_abs:container_path". Read-only enforced at the inner Docker layer (-v src:dst:ro) and reported as guest-enforced until OrbStack documents share-layer enforcement.
  • SSH agent forwarding default off; block_host_network default on once the CLI surface is verified (until then, GUI-set only).
  • Inner Docker engine: distro docker.io package via cloud-init for V1; rootless Docker is a V2+ research item.
  • Acceptance: first-launch image import under 60 s for ~1.5-2 GB images; reattach must not re-import.
  • Worktree materialization stays at ~/.jackin/data/<instance>/worktrees/ on the host; only that specific directory is shared into the machine — sibling/parent directories are not exposed.

These need answers before Phase 1 code work begins. Several can be resolved in under an hour with an OrbStack 2.1.3 install:

  1. orb create --help and orb config --help verification (under 5 minutes on a real install): confirm the exact flag names for --mount ro modifier (if any), SSH agent forwarding opt-in, and the block_host_network config key. Until verified, the design has to assume each is GUI-only.
  2. orb -m <name> <cmd> PTY behavior (under 30 minutes empirical test): does orb exec allocate a real PTY? Does it forward SIGWINCH on terminal resize? Does Ctrl+P,Q detach pass through, or get consumed by orb? This is the make-or-break UX question for using orb exec as the attach transport. If PTY behavior is broken or partial, fall back to SSH tunneling.
  3. Attach transport decision (depends on PTY answer): orb -m ... docker exec, SSH into the machine + docker exec, or a forwarded Unix/TCP control channel for jackin-capsule. SSH gives the most control but adds a sshd requirement to the cloud-init.
  4. Image-import latency budget on real hardware: measure 60 s acceptance against a typical role image on an M-series Mac with v2.1.3 stream-import. If the budget fails, V2 (registry cache) becomes a precondition for shipping rather than a follow-up.
  5. OrbStack auto-update mid-session behavior: not documented. Empirical test: trigger an in-app update while a jackin’ machine is running. Document what happens (machine pause, kill, survive) and add a launch-time clog! warning if a pending update is detected.
  6. GC of orphaned machines: design the failure-mode GC. If purge is interrupted, does the next jackin inspect discover and offer to clean orphaned jackin-<id> machines via orb list?
  7. Per-arch role image strategy: if a role image is amd64-only and the operator is on Apple Silicon, OrbStack runs it under Rosetta or Linux/aarch64 emulation. Decide whether jackin’ warns, refuses, or transparently uses Rosetta.
  • Accidentally using a normal OrbStack machine and exposing the full Mac filesystem instead of isolated selective shares.
  • Calling OrbStack isolated machines “microVMs” and overstating the boundary.
  • Forwarding SSH agent or Mac command integration by default.
  • Forwarding the machine Docker socket back to macOS and recreating the host Docker socket problem.
  • Building images on host Docker while presenting the whole flow as isolated.
  • Reusing one machine across unrelated instances and making cleanup/state ownership unclear.
  • Treating OrbStack support as a substitute for Linux/Kubernetes backend work.
ItemRelationship
Docker runtime hardening contractThe Docker profile still applies to the role container inside the OrbStack machine.
Selectable sandbox backendsUmbrella for backend selection.
Network egress policyRequired for real domain allowlists and connection logs.
Session contract and explain modeRequired to explain OrbStack’s actual boundary.
Workspace registry cachePotential image-transfer optimization for machine-local Docker.
Declarative resource limitsNeeds translation to OrbStack machine and inner Docker limits.