Disco ParrotDisco Parrot Home
Docs
Request a Demo

Approved actions and least privilege

Least privilege has two halves, and Disco Parrot enforces both. An agent's reach is an enumerated set of tools, leased credentials, and the changes an environment lets it apply. A person's reach is a set of named scopes their roles grant. Both are bounded by design, both are auditable, and neither has a hidden way around it.

Least privilege is the principle that nothing, and no one, should hold more reach than the work in front of them needs. In a platform where agents act and people direct them, that principle has two halves: what an agent is allowed to do, and what a person is allowed to do. Disco Parrot bounds both, by construction rather than by policy, and both are something you can enumerate and attest. This page is the governance view of both halves. The everyday version of the agent half is approved actions; the everyday version of the people half is roles and permissions.

Agent reachwalled by the sandboxAn enumerated tool setAn MCP allowlist per connectionLeased credentials, expire in minutesA per-environment change policybounded by what it may call and changePerson reachenforced at every routeNamed scopes, one per actionThe union of the roles you holdA danger level on every scopeNo owner bypassbounded by the scopes their roles grantneither inherits the other
Least privilege has two halves. An agent is bounded by an enumerated tool set, leased credentials, and a per-environment change policy, walled by the sandbox. A person is bounded by the named scopes their roles grant, enforced at every route. Neither inherits the other's reach.

An agent's reach is an enumerated set

An agent acts only through the tools it has been given, and that set is enumerated, not open-ended. In a chat or a flow step, the agent works through a fixed list of registered tools: it reads and writes files, runs commands inside its own sandbox, and calls whichever external tools a connected tool server exposes. External tools arrive through MCP, and each connection declares exactly which of its tools are turned on; a tool that is not on that allowlist is not available to the agent at all.

Those connections authenticate without handing the agent a standing secret of their own, through a sign-in the platform holds, a reused CLI session, or a scoped token resolved at launch, so adding a tool does not mean placing a new key in the agent's hands. And a connection that is not ready fails closed: if its sign-in has lapsed, its tools are not loaded for the run at all, so the agent is left unable to call a tool it cannot properly authenticate rather than reaching for one unauthenticated.

The thing that makes this a governance control is that the reach is legible. Every tool an agent can call is a tool you can see on a list, and a flow step can narrow that list further to fit the work, down to a step that runs with no shell access when it should only touch specific tools. Its reach is written down, so "what can this agent reach" is a question you answer by reading the set, not by guessing. Inside that set the agent is fully capable, which is what gets the work done; the boundary is the part you can audit.

Approval here is at the step, not the call. Inside the sandbox the agent is not prompted before each individual tool call, which is what lets a run move at the speed of the work; the bound is the enumerated set and the allowlist, and the sandbox is the wall around everything that set can touch. The place a person signs off is the run's checkpoints, on the step rather than the keystroke.

Credentials it never holds

An agent's reach into your real systems is bounded the same way: it never holds your standing keys. Model-provider keys, Git tokens, and other managed secrets stay on the server, and every sandbox host refuses them at the boundary, so a run cannot read a key that was never placed where it could see it. When a step genuinely needs to act, the platform hands the sandbox a short-lived credential lease scoped to a single operation and measured in minutes, not a key it keeps. A token to push to Git lasts about ten minutes; a token handed to an external tool can be as short as one. The full model is credentials and the secret policy, with the per-operation grant on credential leases. Even the grants are bounded: a single run can request only so many leases a minute and so many over its whole life, so a run that started misbehaving cannot mint an unbounded stream of credentials. For governance the summary is enough: an agent's standing reach into your systems is zero, and what it does hold is a grant that expires on its own.

What an agent may change

The last edge of an agent's reach is what it may change in your real targets, and that is set per environment. An environment carries a change policy across five kinds of change, each marked apply-allowed or propose-only, and the defaults lead with caution:

Kind of changeDefault
CodeApply allowed
Database schemaPropose only
InfrastructurePropose only
Pipeline and environment configPropose only
Secrets and identityPropose only

So out of the box an agent writes code freely in its sandbox and opens a pull request, while a schema migration or an infrastructure change comes back to a person as a proposal. You set this per environment, so a staging target and a production target can hold different lines, and the policy reaches a run through its sandbox profile: choosing the profile chooses the lines the run will hold. How propose-only pauses a run and routes the change back is covered under human oversight and approvals.

add_photo_alternate
Screenshot to capture
A sandbox profile detail panel, dark theme, titled 'Production Backend', with three stacked cards. A 'Tools' card lists four rows each with a small green dot, 'Files', 'Shell', 'Git', and a connected tool server 'Linear (3 tools)', plus a greyed row reading 'Shell disabled on the ship step'. A 'Credentials' card shows two leased-grant chips, 'Git push lease 10 min' and 'Tool token lease 1 min', above a line reading 'No standing keys in the sandbox'. A 'Change policy' card lists five rows: 'Code apply-allowed' in green, and 'Database schema', 'Infrastructure', 'Pipeline and env config', and 'Secrets and identity' each 'propose-only' in orange. Surface #131316, border #27272a.
save as: public/docs-media/profile-reach-summary.png
Caption when added: One run's reach on a single screen: the tools it can call, the short-lived leases it acts through, and the per-environment line on what it may apply versus only propose. Reach you read off a list, not behavior you discover.

A person's reach is a set of named scopes

The other half of least privilege is what a person may do, and it rests on the same idea: every action is named, and reach is the set of named actions a person's roles grant. Each action in Disco Parrot is a scope, with an id like tenant.delete or secrets.tenant.manage that says exactly what it allows; there are more than a hundred and forty of them, grouped into nine areas so the catalog stays legible, and the permission scope catalog lists every one with its danger level. What a person can do is the union of the scopes their roles grant, and nothing else. A request either carries the scope its action requires, or it is refused; there is no separate policy language to interpret and no rules engine whose verdict you have to predict, so the answer to "is this allowed" is always the same readable check.

Least privilege here goes finer than the workspace, not only broader. Some scopes are resource-scoped: the right to manage a project, or to launch a sandbox from a particular profile, is granted on that one resource through an access list, and the check at launch is both-and, a person must hold the scope and be on that resource's list, so a person cleared for one is not cleared for the rest. Others are self-only: you can always act on your own sessions and settings, and holding the same scope never lets you act on someone else's. And some powerful actions are entitlement-gated, available only on the plans that include them, so exporting the audit log or managing custom tool integrations is a power your plan grants, not just a role. The grant is always the smallest one that fits the action.

Roles exist so you rarely think in single scopes. Eleven built-in roles cover the common shapes, from a read-only Viewer to an Owner, and a clone-and-adjust custom role covers the rest, so granting access is choosing a role rather than assembling a permission set. The full model, the scopes, the built-in roles, custom roles, and the resource-level grants that scope access to a single team or project, is roles and permissions, with the built-in roles reference listing all eleven and their scopes. For governance, the part that matters is that the model is made of precise, enumerable pieces, so "who can do this, and how do we know" has a precise answer: the scopes the person's roles grant, and nothing else.

The whole reach surface, in nine areas

A review rarely needs all the individual scopes; it needs the shape of the reach surface, the answer to "what kinds of things can someone touch here." The scopes group into nine areas, and reading the areas a person's roles draw from tells you the reach of their role at a glance, before you ever look at a single scope id.

  • Workspace governs the workspace itself: creating it, renaming it, configuring it, deleting it.
  • Identity and sessions governs a person's own logins, active sessions, and self-service settings.
  • Planning governs the day-to-day work: initiatives, plans, bugs, projects, portfolios, teams, sprints, goals, reports, and flows.
  • Membership tier governs the per-resource grants a team carries, the access that opens a single team, portfolio, or project to the right people at the right level.
  • Chat and sandboxes governs running agents, chats, and background tasks, and the sandboxes they use.
  • Events and streams governs subscribing to the live event feeds the platform emits.
  • Notifications governs notification preferences.
  • Platform governs the tooling layer: repositories, providers, connected tool servers, model configs, sandbox profiles, hosts, and the workspace's secrets.
  • Operator is reserved for the platform operator and is never granted to a workspace role at all.

Because a person's reach is the union of their roles, the governance question is which of these areas a role touches and how deeply. A Billing Manager touches license and audit visibility and none of the work; a Workspace Operator touches the Platform area without touching Workspace administration or membership; a Planner touches Planning across the workspace and reads the rest. Each built-in role is a deliberate slice of these areas, which is what lets you hand someone the reach of a real job without handing them the whole surface.

Danger is a named property, and powerful grants are deliberate

Not every permission is equally consequential, and the model says so. Every scope carries one of four danger levels, and they do real work rather than sitting as documentation.

LevelWhat it meansIn the editorLowroutine, low-risk actions, most readsno special handlingElevatedchanges shared state, like managing membersgranted like any scopeDestructivepermanently destroys datamust be re-checked before savingPlatform-onlyreserved for the platform operatornever allowed in a workspace role
Every scope carries one of four danger levels, and the editor acts on them: a destructive scope must be re-confirmed before a role will save, and a platform-only scope can never enter a workspace role at all.
  • Low: routine actions, most of them reads.
  • Elevated: higher-privilege actions that change shared state, like managing members or providers.
  • Destructive: actions that permanently destroy data, like deleting the workspace or a project.
  • Platform-only: reserved for the platform operator, a single scope that can never be put in a workspace role at all.

Two guardrails follow from these levels. Adding a destructive scope to a custom role makes you confirm each one deliberately before the role will save, so a role never quietly acquires the power to erase data. And platform-only scopes are kept out of workspace roles entirely: the editor hides them and the server refuses to save a role that includes one, so the operator's powers can never leak into a workspace role even by mistake. A role gaining a dangerous power is always an explicit act, never a side effect of a fast edit.

add_photo_alternate
Screenshot to capture
A custom role editor in its review state, dark theme, for a role named 'Release Manager', with a left column showing the role name and a 'Cloned from Planner' label. The main panel summarizes the granted scopes by danger level as four labeled rows, each a thin horizontal bar: 'Low 22 scopes' in green, 'Elevated 4 scopes' in orange, 'Destructive 1 scope' in pink-red with a small inline 'Confirmed' note beside it, and 'Platform-only 0 scopes' as a greyed empty bar. A 'Save role' button sits at the bottom right. Surface #131316, border #27272a.
save as: public/docs-media/role-danger-summary.png
Caption when added: A custom role at save time, summarized by danger level. The one destructive scope it carries was confirmed deliberately, and platform-only powers are not available to add, so a role gaining a dangerous power is always an explicit act you can see.

Where the two halves meet

The natural question once you hold both halves in view is whether one can be used to escape the other: can a powerful person reach further by directing a permissive agent, or can a capable agent give a narrowly-scoped person more than their roles allow? The answer is no, because a run is bounded on both sides at once.

A run only happens because a person started it, on a sandbox profile they were allowed to use, so it can never be a run that person had no authority to launch. And within that run, the agent can only reach the tools the profile turns on and apply only what the environment permits, so a broadly-scoped person does not widen what the sandbox itself allows. The effective reach of any run is whichever side is tighter. A permissive agent profile does not lift a person above their roles, and a powerful person does not loosen the walls of the sandbox, because the two bounds are enforced independently and a run lives inside both.

The model you can attest

The reason to bound both halves the same way is that it makes the whole thing answerable. A security owner reviewing this does not have to trust a posture; they can read the mechanism:

  • Every route declares how it is guarded, as one of a fixed set of kinds, a required scope, a self-only check, a public route, or an internal one, and a route that reaches real data without declaring fails the test gate before it can ship, so coverage is enforced in the build rather than left to discipline.
  • There is no hidden bypass. The Owner is powerful because the Owner role holds a superset of scopes, not because the code special-cases anyone; reaching a private team's work still means holding the scope for it.
  • An agent's reach is the enumerated tool set, narrowable per step, with the sandbox as the wall around it.
  • An agent's credentials are leases, scoped to one operation and expiring in minutes, with no standing key in its environment.
  • The riskiest changes are propose-only by default, routed back to a person rather than applied.
  • Danger is a named level on every scope, with a confirmation gate on destruction and a hard wall around platform-only powers.
  • Granting a role is itself a controlled act. Changing who holds a role takes its own administrative scope, not a quiet one, and the change is recorded in the audit trail, so the access a person holds is as auditable as the actions it permits.

Each of those is a fact you can produce on demand, which is what lets least privilege be something you verify in a review rather than something you assert in a posture document.

Drawing one role to the line of a job

Sarah's release engineers need to manage plans and bugs and run the ship process, and they need nothing else, not members, not providers, not the workspace itself. Rather than hand them a broad role and hope they stay in their lane, she clones the Planner role, names it Release Manager, trims the planning scopes her engineers do not use, and saves it. From then on it appears alongside the built-in roles, and her engineers carry exactly the reach she drew, no more. When one of them runs an agent, that agent in turn works through an enumerated tool set, holds no standing key, and can apply code while a schema change routes back for review. The person is scoped to a job; the agent they direct is scoped to a sandbox; and both bounds are ones Sarah can point to.

Why least privilege works this way

Most tools bolt permissions on after the fact: a few coarse tiers for people, a vague "the agent can do what it can do" for automation, and a long tail of special cases nobody can fully enumerate when a review asks. Disco Parrot starts from the other end on both halves. Every action a person can take is a named scope from the beginning, every route declares the scope it needs, and an agent's reach is an enumerated tool set, a lease instead of a key, and a per-environment line on what it may change. The result is a system where the reach of any actor, human or agent, is a set you can read rather than a behavior you have to discover.

The two halves reinforce each other. A person is scoped to the actions their job needs; the agent they direct is scoped to a sandbox, an enumerated tool set, and the changes an environment allows. Neither inherits the other's reach, and neither has a hidden path around its bound. That is what lets "least privilege" be a property you verify here, rather than an aspiration you restate.

For the person who owns security, this is least privilege you can attest on both sides of the line. A person holds the union of their roles' scopes and nothing more; an agent holds an enumerated tool set, leased credentials, and a per-environment change policy. Every route declares its scope, danger is a named level with a gate on it, and there is no owner bypass under the surface.

For an administrator, the tools for least privilege are quick to use: clone a built-in role and trim it to the exact reach of a job, and the destructive-confirmation step keeps any role from quietly becoming more dangerous than you meant. The agent side is set the same way, by choosing the profile and environment a run holds to.

For a platform engineer, the agent's reach is the detail to check: a fixed registered tool set per context, an MCP allowlist for anything external, a step that can drop to no shell, leases instead of standing keys, and a change policy that defaults the risky axes to propose-only.

For a prospect evaluating the platform, this is why agent autonomy and least privilege are not in tension. The agent is capable inside a boundary you can read, the people directing it hold exactly the scopes their roles grant, and both bounds are enforced in the runtime and visible in the record, not promised in a posture statement.