# Dopamine jbserver Architecture and XPC Communication Explained

> Explore Dopamine's jbserver architecture and XPC communication. Understand how this RPC router handles jailbreak operations by validating permissions and dispatching requests efficiently.

- Repository: [Lars Fröder/Dopamine](https://github.com/opa334/Dopamine)
- Tags: internals
- Published: 2026-08-12

---

**Dopamine's jbserver is a lightweight RPC router that receives XPC requests from client libraries, validates caller permissions, and dispatches to domain-specific action handlers for jailbreak operations.**

The **jbserver** architecture in the [opa334/Dopamine](https://github.com/opa334/Dopamine) jailbreak tool provides the central communication backbone between the jailbreak daemon and its various components. Built on Apple's XPC framework, it organizes jailbreak functionality into structured domains and actions while enforcing security through permission callbacks. This article examines how the **jbserver architecture and XPC communication** layer works in Dopamine 3.x, referencing the actual source implementation.

## Core Data Structures in jbserver

The jbserver architecture rests on three interconnected concepts defined in [`BaseBin/libjailbreak/src/jbserver.h`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver.h).

### Global Server Instance

Every jbserver deployment shares a single global state structure:

```c
extern struct jbserver_impl gGlobalServer;

```

This `jbserver_impl` struct maintains the array of registered domains and tracks the maximum domain ID for bounds checking. Each component (system-wide, platform, boomerang) registers its domains against this global instance at startup.

### Domain Organization

A **domain** groups related actions under a logical feature boundary. Each domain specifies:

- A unique domain ID (e.g., `JBS_DOMAIN_SYSTEMWIDE`, `JBS_DOMAIN_PLATFORM`)
- An array of actions available within that domain
- A **permission callback** that validates whether the caller's audit token grants access

The permission handler signature:

```c
bool (*permissionHandler)(audit_token_t);

```

This security boundary ensures that only appropriately entitled processes can invoke sensitive jailbreak operations.

### Action Definition

An **action** represents a single remote procedure call with typed arguments:

```c
typedef struct {
    void *handler;
    jbserver_arg *args;
    size_t args_count;
} jbserver_action;

```

The `jbserver_arg` schema describes each parameter's type—boolean, uint64, string, data, file descriptor, or array variants—enabling automatic marshalling between XPC dictionaries and C function arguments.

## XPC Request Flow: Client to Server

The **jbserver XPC communication** follows a strict request-reply pattern with clear separation between client marshalling and server dispatch.

### Client-Side Request Construction

The [`BaseBin/libjailbreak/src/jbclient_xpc.c`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbclient_xpc.c) file provides helper functions that construct XPC dictionaries. A typical call looks like this:

```c
xpc_object_t reply = jbserver_xpc_send(JBS_DOMAIN_SYSTEMWIDE,
                                       JBS_SYSTEMWIDE_GET_JBROOT,
                                       NULL);

```

Behind this convenience wrapper, `jbserver_xpc_send_dict` performs the actual work:

1. Creates a new XPC dictionary
2. Sets domain and action IDs via `xpc_dictionary_set_uint64`
3. Serializes arguments according to the action's `jbserver_arg` schema using `xpc_dictionary_set_value`
4. Transmits to the server's Mach port via `xpc_connection_send_message_with_reply_sync`

The domain and action enumerations live in [`BaseBin/libjailbreak/src/jbserver_domains.h`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver_domains.h), providing compile-time safety for RPC endpoints.

### Server-Side Message Dispatch

All incoming XPC traffic converges at a single entry point in [`BaseBin/libjailbreak/src/jbserver.c`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver.c):

```c
void jbserver_received_xpc_message(struct jbserver_impl *server,
                                   xpc_object_t message,
                                   xpc_object_t reply);

```

The dispatcher executes this sequence:

1. **Extract routing information**: Parses domain ID and action ID using `xpc_uint64_get_value`
2. **Validate domain**: Bounds-checks against `server->maxDomain` and verifies `server->domains[domainID]` exists
3. **Permission check**: Invokes the domain's `permissionHandler` with the sender's audit token
4. **Argument unmarshalling**: Walks the action's `jbserver_arg` array, extracting typed values from the XPC dictionary
5. **Handler invocation**: Calls the stored function pointer with unpacked arguments
6. **Reply population**: If the handler returns success, fills the reply dictionary with return values

This centralized dispatch design means adding new jailbreak capabilities requires only registering a new domain or action—no changes to the XPC plumbing.

## Mach Message Auxiliary Channel

Certain privileged operations bypass pure XPC for direct Mach message exchange. These include:

- Process check-in sequences during jailbreak initialization
- Fork-fix coordination for freshly spawned processes
- Raw message forwarding to `hookd`

### Mach Message Structures

[`BaseBin/libjailbreak/src/jbserver.h`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver.h) defines the Mach message layouts:

```c
typedef struct {
    mach_msg_header_t header;
    // Additional fields for specific operations
} jbserver_mach_msg;

typedef struct {
    jbserver_mach_msg base;
    // Checkin-specific data
} jbserver_mach_msg_checkin;

typedef struct {
    mach_msg_header_t header;
    uint64_t status;
    // Reply data
} jbserver_mach_msg_reply;

```

### Client Mach Interface

[`BaseBin/libjailbreak/src/jbclient_mach.c`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbclient_mach.c) exposes `jbclient_mach_send_msg`, which:

1. Constructs the appropriate `jbserver_mach_msg` subtype
2. Sends via `mach_msg` to the server's registered Mach port
3. Waits for the `jbserver_mach_msg_reply` and returns status

### Unified Dispatch Integration

Despite being Mach-based, these messages integrate with the XPC dispatch path. The server wraps incoming Mach messages into XPC dictionaries before calling `jbserver_received_xpc_message`, maintaining a single handler code path for all request types.

## Boomerang Domain: Cross-Process Helper

The **boomerang** helper binary demonstrates jbserver's flexibility for multi-process architectures. Located in [`BaseBin/libjailbreak/src/jbserver_boomerang.c`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver_boomerang.c), it implements a minimal jbserver instance for inter-process coordination.

### Boomerang Server Setup

```c
static struct jbserver_impl gBoomerangServer;
static jbserver_domain gBoomerangDomain;

// Registration during initialization
gBoomerangDomain.domain_id = JBS_DOMAIN_BOOMERANG;
gBoomerangDomain.permissionHandler = boomerang_permission_check;
gBoomerangDomain.actions = boomerang_actions;
gBoomerangDomain.actions_count = BOOMERANG_ACTION_COUNT;

jbserver_register_domain(&gBoomerangServer, &gBoomerangDomain);

```

### Boomerang-Specific Entry Point

The boomerang client calls `jbserver_received_boomerang_xpc_message`, which forwards to the generic dispatcher:

```c
void jbserver_received_boomerang_xpc_message(xpc_object_t message,
                                              xpc_object_t reply) {
    jbserver_received_xpc_message(&gBoomerangServer, message, reply);
}

```

Key boomerang actions include `JBS_BOOMERANG_DONE`, signaling completion of elevated operations run in the separate helper process.

## Security Architecture

The jbserver designembeds security at the domain boundary:

- **Audit token inspection**: Permission handlers receive the caller's `audit_token_t`, enabling PID, UID, and entitlement verification
- **No ambient authority**: Every call re-validates; there are no persistent privileged sessions
- **Type-safe arguments**: The `jbserver_arg` schema prevents type confusion attacks across the IPC boundary
- **Domain isolation**: Compromise of one domain's handler does not automatically grant access to other domains

## Summary

- **jbserver architecture** centers on `struct jbserver_impl` with registered domains, each containing typed actions and permission callbacks
- **XPC communication** marshals requests in [`jbclient_xpc.c`](https://github.com/opa334/Dopamine/blob/main/jbclient_xpc.c), dispatches via `jbserver_received_xpc_message` in [`jbserver.c`](https://github.com/opa334/Dopamine/blob/main/jbserver.c), with automatic argument packing/unpacking
- **Mach auxiliary channel** handles privileged operations through [`jbclient_mach.c`](https://github.com/opa334/Dopamine/blob/main/jbclient_mach.c), integrating into the same dispatch path
- **Boomerang domain** shows extensibility for multi-process scenarios with [`jbserver_boomerang.c`](https://github.com/opa334/Dopamine/blob/main/jbserver_boomerang.c)
- All core definitions live in [`BaseBin/libjailbreak/src/jbserver.h`](https://github.com/opa334/Dopamine/blob/main/BaseBin/libjailbreak/src/jbserver.h) with domain/action IDs in [`jbserver_domains.h`](https://github.com/opa334/Dopamine/blob/main/jbserver_domains.h)

## Frequently Asked Questions

### How does jbserver validate which processes can call jailbreak functions?

Each domain registers a `permissionHandler` callback that receives the caller's `audit_token_t`. The handler inspects this token—checking PID, UID, or code signatures—to grant or deny access. This validation runs on every RPC invocation; there are no cached privileges.

### What types of arguments can jbserver actions accept?

The `jbserver_arg` system supports: boolean, uint64, int64, string, data, file descriptor, and array variants of each. The schema describes direction (in/out/inout) enabling automatic XPC dictionary serialization and deserialization.

### Why does Dopamine use both XPC and Mach messages rather than pure XPC?

Mach messages provide lower-level control needed for specific kernel interactions: process check-in during early bootstrap, fork-fix coordination requiring precise timing, and direct `hookd` communication. These wrap into the XPC dispatch path to maintain architectural consistency.

### Where are new jailbreak capabilities added in the jbserver architecture?

New features require: (1) adding domain/action IDs to [`jbserver_domains.h`](https://github.com/opa334/Dopamine/blob/main/jbserver_domains.h), (2) implementing the handler function, (3) registering the action in the appropriate domain table, and (4) adding a client helper in [`jbclient_xpc.c`](https://github.com/opa334/Dopamine/blob/main/jbclient_xpc.c). The core dispatch machinery needs no modification.