# Permissions API

This page documents the Lua API for managing PipeWire client permissions: the `Perm` constants that name the permission flags, and the `PermissionManager` object that computes and maintains per-object permissions for clients.

For the architecture these APIs are part of, see Client Access Control. For the configuration file equivalents, see Access configuration.

## Constants

Perm

  PipeWire client permission flags:

  Constant | Meaning
  --- | ---
  `Perm.NONE` | no permissions
  `Perm.R` | read: the object is visible to the client
  `Perm.W` | write: the client may modify the object
  `Perm.X` | execute: the client may call methods on it
  `Perm.M` | metadata: the client may set metadata on it
  `Perm.L` | link: the client may link to it

  The combinations `Perm.RW`, `Perm.RX`, `Perm.WX`, `Perm.RWX`, `Perm.RWXM`, `Perm.RWXML` and `Perm.ALL` are also provided. Note that `Perm.ALL` is `RWXM` and does not include `L`.

  See The permission model for what each flag actually allows.

## Permission Manager

The `PermissionManager` object manages per-object permissions for clients. It is created with the global `PermissionManager()` constructor and configured with default permissions, core permissions, and match rules. It must be activated with `Features.ALL` before it can be attached to a client with `Client.attach_permission_manager()`; see Permission managers for how the permission list is computed and when it is refreshed.

PermissionManager()

  Creates a new permission manager. Its default permissions start out as `Perm.RWX`.

  Returns:

    a new permission manager

  Return type:

    WpPermissionManager

PermissionManager.set_default_permissions(self, perms)

  Binds `wp_permission_manager_set_default_permissions()`

  Sets the default permissions applied to all objects that don't match any rule.

  Parameters:

    - self -- the permission manager

    - perms -- a permission string (e.g. "rx") or an integer bitmask (e.g. `Perm.RX`)

PermissionManager.get_default_permissions(self)

  Binds `wp_permission_manager_get_default_permissions()`

  Returns the default permissions as an integer bitmask. This can be compared against the `Perm` constants using bitwise operators.

  Example:

      local pm = client:get_permission_manager()
      local perms = pm:get_default_permissions()
      if (perms & Perm.RX) == Perm.RX then
        -- client has at least read + execute
      end

  Parameters:

    self -- the permission manager

  Returns:

    the default permissions bitmask

  Return type:

    integer

PermissionManager.set_core_permissions(self, perms)

  Binds `wp_permission_manager_set_core_permissions()`

  Sets the permissions applied specifically to the PipeWire core object (ID 0). If not set, the core inherits the default permissions.

  Parameters:

    - self -- the permission manager

    - perms -- a permission string or an integer bitmask

PermissionManager.add_rules_match(self, rules)

  Binds `wp_permission_manager_add_rules_match()`

  Adds a set of match rules that grant specific permissions to objects matching the given constraints. The rules use the standard `matches`/`actions` form, with a `set-permissions` action whose value is a permission string.

  Parameters:

    - self -- the permission manager

    - rules (WpSpaJson) -- a JSON array of match rules

  Returns:

    the match id (can be used with `remove_match`)

  Return type:

    integer

PermissionManager.add_interest_match(self, callback, interest)

  Binds `wp_permission_manager_add_interest_match_closure()`

  Adds a dynamic match that calls the given callback to determine permissions for objects matching the given interest. The callback is called with the permission manager, the client whose permissions are being computed, and the matched object, and must return a permission bitmask.

  Example:

      pm:add_interest_match (
        function (_, client, object)
          return client:get_property ("my.sandbox.id") ==
                 object:get_property ("my.sandbox.id") and Perm.ALL or Perm.NONE
        end,
        Interest {
          type = "node",
          Constraint { "media.class", "=", "Audio/Sink" },
        }
      )

  Parameters:

    - self -- the permission manager

    - callback (function) -- a function that returns the permissions for the matched object

    - interest (WpObjectInterest) -- the interest to match

  Returns:

    the match id

  Return type:

    integer

PermissionManager.add_interest_match_simple(self, perms, interest)

  Binds `wp_permission_manager_add_interest_match_simple()`

  Adds a static match that grants the given permissions to objects matching the given interest.

  Parameters:

    - self -- the permission manager

    - perms (integer) -- the permissions bitmask to grant

    - interest (WpObjectInterest) -- the interest to match

  Returns:

    the match id

  Return type:

    integer

PermissionManager.remove_match(self, match_id)

  Binds `wp_permission_manager_remove_match()`

  Removes a previously added match.

  Parameters:

    - self -- the permission manager

    - match_id (integer) -- the match id returned by an `add_*_match` method

PermissionManager.update_permissions(self)

  Binds `wp_permission_manager_update_permissions()`

  Forces a recalculation and update of permissions on all attached clients. This is needed when a decision made by an `add_interest_match` callback depends on external state that has changed, since the permission manager only recomputes automatically when objects are added to or removed from the graph.

  Parameters:

    self -- the permission manager

### Signals

client-properties-changed

  Emitted when the properties of an attached client change. The callback receives the permission manager and the client.

  Example:

      pm:connect ("client-properties-changed", function (pm, client)
        if client:get_property ("my.gated") == "true" then
          client:update_permissions { [0] = "rwx" }
        end
      end)

## Introspecting a client's permissions

Scripts that need to know how much a client is trusted before acting on its behalf can retrieve the permission manager attached to it with `Client.get_permission_manager()`:

    local client_om = ObjectManager { Interest { type = "client" } }
    client_om:activate()

    -- Check if a client has at least read + execute permissions
    local pm = client:get_permission_manager()
    if pm then
      local perms = pm:get_default_permissions()
      if (perms & Perm.RX) == Perm.RX then
        -- Client has sufficient permissions
      end
    end

Note that this returns `nil` for clients whose permissions were set directly instead of through a permission manager, i.e. those matched by an `access.rules` entry that sets `default_permissions`.