---
title: Agent permissions
description: What an agent may do, who decides it, and what the person who runs it contributes.
---

An agent acts on your project through the CLI, the API, git, connectors, and
Apps. This page explains how Beeuni decides what an agent may do.

**An agent acts as itself, not as the person who started it.** Its authority is
its Beeuni permissions ∩ its ceiling role. Permissions alone decide, the same
way they do for a person. Nobody
lends an agent their own role, so the agent does the same thing whoever runs
it.

## The agent is the acting principal

Every agent session has two identities:

| Identity | What it decides |
|---|---|
| **Acting principal — the agent** | Everything on shared resources: project permissions, shared connector accounts, project secrets, Apps, git. |
| **On behalf of — the person** | Only that person's own resources, and only in that person's own private session. |

Each agent has one identity per project: a service account that Beeuni
creates for it. You never hold a credential for it.

## Beeuni permissions — the manifest decides

Each agent declares its Beeuni permissions in `beeuni.yaml`, under
`agents.<name>.beeuni_permissions`. The manifest on the default branch is the
source of truth.

```yaml
agents:
  report-writer:
    beeuni_permissions: [project.file.read, project.connector.read, project.app.read]
    connectors: [reports-dashboard-api]
    secrets: [REPORTS_API_KEY]
    apps: [reports-dashboard]
```

- The list is deny-by-default. An agent without `beeuni_permissions` gets none.
- `all` grants every project permission, still capped by the ceiling.
- `beeuni_cli` is the deprecated spelling. It still validates, with a warning.
  Both keys with different values on one agent is a validation error.

Edit the list in **Customize → Agents → the agent → Beeuni permissions**. A
save commits `beeuni.yaml`. See [the manifest reference](/docs/project/manifest).

## The ceiling — an admin decides

The ceiling is a project role that an admin binds to the agent's service
account. The agent's Beeuni permissions never exceed it.

- **No role bound:** the default agent ceiling applies. It is every project
  permission except `project.credentials.issue`.
- **A role bound:** built-in `member` or `manager`, or a custom role. The agent
  keeps only the Beeuni permissions that the role also grants.
- A ceiling never adds a permission. A `manager` ceiling does not give an
  agent a permission its manifest does not list.

Set the ceiling in the **Access hub → Projects → the project → Add**. Pick the
agent under **Agents** and choose a role. Only admins who manage roles see
agents in the picker. The project's access list shows each agent ceiling with
an **Agent** badge.

## `all` means every permission

`beeuni_permissions: all`, `"*"`, and any list that contains `"*"` mean the
same thing: every project permission. That includes `project.members.manage`
(add, change, or remove project members and grants) and `project.delete`.

One permission stays with people: `project.credentials.issue` (mint a project
token or a project-scoped PAT). A token an agent minted would carry no agent
permissions, so it would act outside them. The refusal answers `403` with
`code: "agent_human_only_action"`.

An entry that is not a grantable permission is ignored at runtime and the rest
of the list still applies. `beeuni validate` and a change request merge report
it as an error.

`project.read` is the reverse: an agent always holds it inside its own project.

## Running an agent lends its authority

The person who runs an agent does not cap it. So the right to
run an agent is the delegation. Beeuni checks "may this person run this agent"
at every entry point:

| Entry point | Required |
|---|---|
| Create, start, or prompt a session; switch its agent | The person may run the agent |
| Fire a trigger by hand | The person who fires it may run the trigger's agent |
| Start a child session from an agent session | The person behind the parent may run the child's agent. With no person behind it, only the same agent. |

Grant "may run" on the agent's **People** tab, or in the Access hub. Agents
are closed by default: a member runs an agent only when an assignment names
them or one of their groups.

## Personal resources stay personal

An agent session reaches a resource that one person owns only when both are
true:

1. The session runs on behalf of that person.
2. The session is private.

Personal resources are member-owned connector connections, personal project
secrets, personal provider keys, and the person's own computer. A shared
session, a trigger or channel session, and an unattended run reach none of
them.

When another person prompts a private session — for example an account admin
who can open members' private sessions — the session stops acting on behalf of
its creator, for good. The agent keeps its own permissions. The person who
prompts never acts through someone else's accounts.

## Apps

An agent opens a restricted or private [App](/docs/feature-flags/apps) only
when its `apps:` grant names the App's slug, or is `all`, and its effective
permissions include `project.app.read`.

| App access mode | The agent is admitted when |
|---|---|
| Public | Always |
| Project | `project.app.read` is effective |
| Restricted or private | The slug is in `apps:` and `project.app.read` is effective |
| Password | Never |

The App's **Access** dialog lists the agents with access, read from
`agents.<name>.apps`. Change the list on the agent's **Apps** page
(Customize → Agents → the agent → Apps), with
`beeuni agents scope <agent> --apps <slug,slug>`, or with a change request to
`beeuni.yaml`. The Apps page appears only when the project has the
[Apps](/docs/feature-flags/apps) feature flag on.

## Backends

A project can own Managed project backends. Two permissions govern them.
They follow the Apps permissions: a role that holds `project.app.read` or
`project.app.write` holds the matching Backend permission.

| Permission | Allows |
|---|---|
| `project.backend.read` | List and inspect the project's Backends: name, status, and URL |
| `project.backend.write` | Create and delete Backends, and read a Backend's admin credentials. Implies `project.backend.read` |

The admin key gives full control of a Backend's data and code, so reading it
needs `project.backend.write`. The built-in Manager role holds both
permissions. The built-in Member role holds `project.backend.read`.

## Changing agents and triggers

`beeuni.yaml` shapes what agents may do, so whoever lands a change on the
default branch shapes agents. The same permissions decide for an agent and for
a person:

1. Merging a change request needs `project.gitops.merge`.
2. A merge that changes `beeuni.yaml` also needs the permission the direct
   route for that change needs: `agents.*` → `project.agent.write`;
   `triggers` → `project.trigger.create`, `.update`, or `.delete`;
   `default_agent` → `project.agent.write`. The Manager role holds these,
   and the role editor adds them to any role that gets
   `project.gitops.merge`. An agent's `beeuni_permissions` must list them, or
   be `all`. So an agent that may only merge cannot widen itself.
3. An agent grants only what it holds. When an agent writes any agent's grant
   (the agent editor, the scope and secret-grant routes, or a change request
   it merges), every permission, connector, secret and App the write adds must
   be in the writing agent's own effective grant. Granting `all` needs every
   one of them. Narrowing or removing a grant is always allowed. A refusal
   answers `403` with `code: "agent_grant_escalation"`.
4. A push straight to the default branch skips the change request. An agent
   session needs `project.gitops.ref.any` and every permission in item 2. The
   pushed manifest cannot be checked before it lands, so the agent must also
   hold every grant (`all` permissions, connectors, secrets and Apps).
   Otherwise it opens a change request.
5. A refusal answers `403` with `code: "agent_scope_insufficient"` (agent) or
   `"project_role_insufficient"` (person) and the missing `action`. When the
   manifest cannot be read, the merge answers `503` with
   `code: "CR_GOVERNANCE_UNVERIFIED"` and `Retry-After`. Retry it.
6. The ceiling is admin-only IAM state. The manifest never exceeds it.

## Read a denial

Every `403` from an authorization check carries `code` and `action`:

| `code` | Meaning | Fix |
|---|---|---|
| `agent_scope_insufficient` | `action` is not in the agent's Beeuni permissions | Add it to `agents.<name>.beeuni_permissions` |
| `agent_ceiling_insufficient` | `action` is outside the agent's ceiling role | An admin raises the ceiling |
| `agent_not_accessible` | The person may not run this agent | Grant them the agent |
| `agent_grant_escalation` | The write gives an agent something the writing agent does not hold | Grant only what the writer holds, or have a person make the change |
| `project_role_insufficient` | The person's own role denies `action` | Change their project role |

## Audit

Every agent action records three fields: `actor` is the agent,
`on_behalf_of` is the person or `null`, and `initiator` is `human`,
`trigger`, or `channel`. A trigger run records no person.

## See it in the dashboard

Open **Customize → Agents → the agent → Beeuni permissions**. The **What this
agent can do** panel shows:

- the Beeuni permissions declared in `beeuni.yaml`,
- the ceiling role, or **Default agent ceiling**,
- the one human-only permission, `project.credentials.issue`,
- the effective permissions: "People who may run this agent act with these
  permissions."

## Scope

This page applies to every agent a project declares under `agents:` in
`beeuni.yaml`. A project with no `agents:` map declares no governed agent, so
there is nothing to switch on. There is no switch back to the old
launcher-role model.
