# How to Manage Users and Permissions in PI Desktop: A Complete Guide to RACP Roles

> Master PI Desktop user and permission management with RACP roles. Learn how viewer, controller, approver, and owner roles authorize operations through the Principal object for secure access.

- Repository: [Lan/PI-Desktop](https://github.com/vastsa/PI-Desktop)
- Tags: how-to-guide
- Published: 2026-09-12

---

**PI Desktop implements a fine-grained permission model based on the Remote Agent Control Protocol (RACP), using four distinct roles—viewer, controller, approver, and owner—to authorize every operation through a centralized `Principal` object.**

All user authorization in PI Desktop flows through the Remote Agent Control Protocol (RACP), ensuring consistent security policy enforcement across the UI, plugins, and remote clients. The architecture centralizes role definitions and permission checks in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts), while the host implementation in [`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts) validates every request against the caller's **Principal** object. Understanding how to create Principals and check roles programmatically is essential for extending the application securely.

## Understanding the RACP Permission Model

The permission system revolves around the **Principal** type, which carries a unique subject identifier and an array of RACP roles. When the host receives any request, it extracts the caller's Principal and validates it against the required role for the requested operation.

### The Four RACP Roles

RACP defines four hierarchical roles in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts) at line 36:

```ts
export const RACP_ROLES = ["viewer", "controller", "approver", "owner"] as const;

```

Each role grants specific capabilities:

- **viewer** – Can read most data but cannot modify state or issue commands.
- **controller** – May issue commands affecting a session, such as starting or stopping turns.
- **approver** – Can grant or deny approval requests within the system.
- **owner** – Has full-privilege access and implicitly includes all other roles.

### Role Hierarchy and Ranking

The system calculates permission precedence using a numeric ranking defined at line 572 in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts):

```ts
const ROLE_RANK: Record<RacpRole, number> = { viewer: 0, controller: 1, approver: 2, owner: 3 };

```

This hierarchy ensures that higher-ranked roles automatically satisfy requirements for lower-ranked operations. The **owner** role bypasses all permission checks entirely, making it suitable for the local desktop user who requires unrestricted access.

## How Permission Checks Work in the Code

All authorization logic funnels through the `rolesAllowOperation` function, which evaluates whether a set of roles satisfies the requirements for a specific RACP operation.

### The rolesAllowOperation Function

Located at line 583 in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts), this function implements the core permission logic:

```ts
export function rolesAllowOperation(roles: readonly RacpRole[], operation: RacpOperation): boolean {
  const required = RACP_OPERATIONS[operation].role;
  if (roles.includes("owner")) return true;          // owner can do everything
  if (required === "viewer") return roles.length > 0;
  return roles.includes(required);
}

```

The function returns `true` immediately if the role set includes `"owner"`. For other roles, it checks whether the provided roles array contains the specific role required for the operation. This helper is imported and used throughout the codebase to maintain a single source of truth for authorization logic.

### Host-Side Enforcement

The **agent host** enforces permissions before delegating to the runtime. In [`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts) at line 359, the implementation uses a guard pattern to validate the Principal:

```ts
if (!principal.roles.includes(role) && !principal.roles.includes("owner")) {
  throw new RACPError("FORBIDDEN", "Caller lacks required role");
}

```

This check ensures that even if a caller attempts to bypass UI-level restrictions, the backend will reject unauthorized operations. All UI-level permission enforcement delegates to these same backend checks, eliminating security disparities between interface layers.

## Implementing User Management

To manage users programmatically, create a **Principal** object and pass it to host API functions. The host will re-validate the Principal internally before executing any sensitive operations.

### Creating a Principal

Define the current user with appropriate roles in your application code:

```ts
import type { Principal } from "./agent-host/types";

const me: Principal = {
  subject: "desktop",          // unique identifier for this host
  roles: ["owner"],           // give full access (typical for the local desktop)
  pairedDevice: true,
};

```

### Checking Permissions Client-Side

Use the shared RACP utility to verify capabilities before attempting restricted operations:

```ts
import { rolesAllowOperation } from "packages/shared/src/racp";
import type { RacpOperation } from "packages/shared/src/racp";

const canStartTurn = rolesAllowOperation(me.roles, "turn/start");
if (!canStartTurn) {
  console.error("User lacks permission to start a turn");
}

```

### Executing Restricted Operations

Pass the validated Principal to host API methods, which perform secondary authorization:

```ts
import { startTurn } from "packages/agent-host/src/agent-host";

await startTurn(me, {
  sessionId: "sess_123",
  input: { text: "Explain the permission model" },
  context: { requestId: "req_001" },
});

```

## Key Files in the Permission Architecture

Understanding these source files is essential for modifying or extending the security model:

- **[`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts)** – Defines the RACP contract, role enumeration (`RACP_ROLES`), role ranking (`ROLE_RANK`), and the `rolesAllowOperation` helper used universally for permission checks.
- **[`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts)** – Implements the host-side API, extracts the caller's `Principal`, and enforces role-based guards before delegating to the runtime.
- **[`packages/agent-host/src/agent-host.test.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.test.ts)** – Contains test suites demonstrating permission scenarios, including owner versus non-owner access patterns.
- **[`packages/shared/src/types.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/types.ts)** – Houses the `Principal` type definition and related UI message role definitions.
- **[`packages/agent-runtime/src/subagent.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-runtime/src/subagent.ts)** – Validates inbound messages for role (`user` versus `assistant`) before processing by the sub-agent runtime.

## Summary

- **PI Desktop uses RACP roles** (viewer, controller, approver, owner) defined in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts) to control access to all operations.
- **The `rolesAllowOperation` function** at line 583 provides the canonical permission check, automatically granting all rights to owners.
- **Principals carry authorization context** through the `subject` identifier and `roles` array, validated by the host in [`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts).
- **Backend guards enforce security** regardless of UI state, ensuring consistent policy application across plugins and remote clients.

## Frequently Asked Questions

### What is the difference between controller and approver roles in PI Desktop?

The **controller** role allows users to issue commands that affect session state, such as starting or stopping turns, while the **approver** role specifically grants the ability to approve or deny requests within the workflow. According to the role ranking in [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts), approver (rank 2) outranks controller (rank 1), meaning approvers can perform controller actions only if they also hold the controller role or the owner role.

### How does PI Desktop handle permission checks for the owner role?

The owner role receives special treatment in the `rolesAllowOperation` function at line 583 of [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts): if the roles array includes `"owner"`, the function returns `true` immediately without checking specific operation requirements. This hardcoded exception ensures owners bypass all permission gates, making the role suitable for local desktop administrators who require unrestricted system access.

### Can UI components bypass the backend permission checks in PI Desktop?

No. While UI components can perform client-side permission checks using `rolesAllowOperation` to improve user experience, the host implementation in [`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts) re-validates every request using the Principal object. The guard at line 359 explicitly throws a `RACPError` with code `"FORBIDDEN"` if the caller lacks the required role, ensuring backend enforcement remains the definitive authority for all security decisions.

### What files should developers modify to add new permission roles to PI Desktop?

To extend the permission system, developers must update the `RACP_ROLES` array at line 36 of [`packages/shared/src/racp.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/shared/src/racp.ts) and adjust the `ROLE_RANK` mapping at line 572 to include the new role's hierarchy position. Additionally, the `RACP_OPERATIONS` definition must be updated to specify which new role is required for specific operations, and the host guards in [`packages/agent-host/src/agent-host.ts`](https://github.com/vastsa/PI-Desktop/blob/main/packages/agent-host/src/agent-host.ts) may need modification to handle role-specific logic beyond the standard `rolesAllowOperation` checks.