How to Use XPC Services with the Container Tool for Inter-Process Communication

The Container tool uses the ContainerXPC Swift package to establish secure, sandboxed connections between container-apiserver and helper services, enabling container lifecycle management through serialized XPCMessage objects sent over XPCClient connections.

The apple/container repository implements a robust inter-process communication layer using macOS XPC services to isolate privileged container operations from the user-facing CLI. By leveraging the ContainerXPC package, the tool creates type-safe bridges between the API server and per-container helpers that manage VMs, networking, and image storage.

Understanding the XPC Service Architecture

The Container tool relies on three specialized XPC helpers that run with minimal privileges and register with launchd:

  • container-core-images: Manages image storage and content addressing
  • container-network-vmnet: Implements the virtual network stack and IP allocation
  • container-runtime-linux: One instance per container, handling VM lifecycle, process execution, and resource statistics

Each helper exposes an anonymous XPC endpoint that clients discover through the ContainerXPC package, serializing method calls into XPCMessage objects transmitted over XPCClient connections.

Connecting to XPC Services

The client-server workflow follows a four-step pattern implemented in Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift.

Step 1: Create the XPC Client

The client initialization requires a launchd service label matching the helper's identifier. For runtime operations, this follows the pattern com.apple.container.RuntimeLinuxHelper.{containerID}.

Step 2: Request an Endpoint

The client calls the createEndpoint route to obtain an anonymous XPC endpoint. This handshake, implemented in RuntimeClient.create, establishes the durable connection for the container's lifetime.

Step 3: Invoke Routes with XPCMessage

Each operation maps to a case in the RuntimeRoutes enum defined in Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift. The client constructs an XPCMessage with the route's raw string value and serializes arguments using type-safe setters like setArray and setDictionary.

Step 4: Receive Responses

The server side in Sources/Services/RuntimeLinux/Server/RuntimeService.swift decodes incoming messages, dispatches them to appropriate handlers using XPCClientSession tracking, and returns payload data such as PIDs, status structs, or JSON-encoded statistics.

Practical Code Examples

The following Swift examples demonstrate interacting with the XPC layer using the public APIs exposed by the Container tool.

Creating a Runtime Client

Use RuntimeClient.create to register the helper and establish a connection:

import ContainerXPC
import ContainerRuntime

/// `containerID` is the identifier of the container you want to control.
func makeRuntimeClient(containerID: String) async throws -> RuntimeClient {
    // The helper label is derived from the container ID.
    let helperLabel = "com.apple.container.RuntimeLinuxHelper.\(containerID)"

    // Register the helper (launchd will start it if needed) and obtain a client.
    return try await RuntimeClient.create(
        id: containerID,
        runtime: helperLabel,
        timeout: .seconds(10)
    )
}

Launching a Process Inside a Container

Send a createProcess message to start executable workloads:

func startProcess(in client: RuntimeClient,
                  command: [String],
                  env: [String: String] = [:]) async throws -> Int {
    // Build the request message for the `createProcess` route.
    var request = XPCMessage(route: RuntimeRoutes.createProcess.rawValue)
    request.setArray(command, forKey: "argv")
    request.setDictionary(env, forKey: "env")

    // Send the request and decode the PID from the reply.
    let response = try await client.send(request)
    return try response.getInt(forKey: "pid")
}

The RuntimeService.swift implementation validates the request and spawns the process within the container's VM.

Querying Container Statistics

Retrieve resource utilization metrics via the statistics route:

func fetchStatistics(from client: RuntimeClient) async throws -> ContainerStatistics {
    // No arguments are needed for the `statistics` route.
    let request = XPCMessage(route: RuntimeRoutes.statistics.rawValue)
    let reply = try await client.send(request)

    // Decode the JSON-encoded statistics payload.
    let json = try reply.getData(forKey: "json")
    return try JSONDecoder().decode(ContainerStatistics.self, from: json)
}

The server encodes CPU, memory, and I/O metrics as JSON before transmission over the XPC connection.

Shutting Down the Helper

Gracefully terminate the XPC service when removing a container:

func shutdownHelper(_ client: RuntimeClient) async throws {
    let request = XPCMessage(route: RuntimeRoutes.shutdown.rawValue)
    // Ignore reply – just ensure the helper exits.
    _ = try await client.send(request)
}

Calling shutdown triggers cleanup routines in RuntimeService.swift and releases the XPC endpoint.

Summary

  • The ContainerXPC package provides the Swift interface for macOS XPC communication in the Container tool
  • Three specialized helpers (container-core-images, container-network-vmnet, container-runtime-linux) handle isolated subsystems with minimal privileges
  • RuntimeClient.create in Sources/Services/Runtime/RuntimeClient/RuntimeClient.swift manages connection establishment and endpoint acquisition
  • The RuntimeRoutes enum defines type-safe RPC endpoints including createProcess, statistics, and shutdown
  • All XPC messages use the XPCMessage class with typed accessors like setArray, getInt, and getData for serialization

Frequently Asked Questions

What is the ContainerXPC package?

ContainerXPC is the Swift package within the apple/container repository that wraps macOS XPC APIs, providing XPCClient and XPCMessage classes for type-safe inter-process communication between the container CLI and privileged helper services.

How does the RuntimeClient establish a connection?

RuntimeClient.create(id:runtime:timeout:) registers the helper with launchd using the provided service label, calls the createEndpoint route to obtain an anonymous XPC endpoint, and returns a configured client instance ready for method invocation.

What routes are available in RuntimeRoutes?

The RuntimeRoutes enum in Sources/Services/Runtime/RuntimeClient/RuntimeRoutes.swift defines routes including createProcess, start, stop, kill, statistics, and shutdown, with each case's raw value representing the XPC message route identifier.

How is error handling implemented in XPC communication?

The XPC layer propagates Swift errors through the XPCClient connection, handling timeouts, permission denials, and serialization failures, while RuntimeService.swift validates incoming messages and returns structured error responses before terminating the helper on critical failures.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →