The Bitbucket access no permissions tab will show you
Ask a simple question about a Bitbucket workspace - "who can push to this repository?" - and you will not get a simple answer. The workspace has a members page. Each user group has its own page. Every repository has its own permissions tab. Branch restrictions live somewhere else again. To answer the question for the whole workspace you open all of those, in order, and hold the running total in your head.
The reason it is scattered is that repository access is not one list. A person reaches a repository three different ways: granted on the repository directly, granted through a user group they belong to, or granted nothing in particular because they own the workspace and owners can reach everything. Bitbucket renders those three paths in three different places and leaves the join to you.
The Bitbucket extension for Kunobi does that join. It pulls a workspace in as ordinary rows - groups, members, repositories, and one row per permission grant - that you can sort, filter, and drill through, and it attaches to every grant the path that produced it. This is a tour of what it surfaces. It covers Bitbucket Cloud only, it is version 0.1.0, and it is a Pro feature.
Connecting a workspace
Bitbucket appears on Kunobi's Sources page as a credentialed source. It authenticates with HTTP Basic, but not in the way most people's muscle memory expects: the username is your Atlassian account email, and the password is an API token, not an app password. That distinction is no longer a preference. Atlassian retired app passwords after a series of brownouts through July 2026, and on 28 July 2026 they stopped working entirely. Any integration still holding one is already broken, so an API token is the only credential that will connect here.
The token needs read scope on the workspace, its users, and its repositories, plus
read/write/delete on permissions if you want to use the mutation actions later:
read:workspace:bitbucket, read:user:bitbucket, read:repository:bitbucket,
and the :permission:bitbucket scopes. You give the workspace slug, the email,
and the token, and that is the whole Add form. Editing a connection afterwards
exposes one more field, its refresh interval.
Once the connection is live, the row shows the workspace, its connection status, and when it was last checked. Behind that plain row sits a real constraint: Bitbucket's rate limit is tight - repository data access is metered at 1,000 requests per hour for a normal authenticated account (larger paid workspaces get more). That budget is small enough that the background refresh cadence is a per-connection setting rather than a fixed constant, and small enough that the extension defers expensive reads until you actually ask for them, a decision you will see the effect of in the drilldowns below.
Four kinds
On the Data page the workspace expands into four lists: User Groups, Members, Repositories, and Repository Grants. The choice of four is deliberate. These are the four things a permission is computed from - a principal (a member or a group), a repository, and the grant edge that connects them - so they are the four things worth having as independent, filterable lists before you start asking how they combine.
User Groups
The first list is every user group in the workspace, each with a member count, plus the workspace itself as a top-level row so that every member has a group to sit under. The count is not a field Bitbucket hands back; it is derived from the membership edges the extension holds, so a group that has grown past the size anyone intended is visible without opening it.
Members
The Members list is every member of the workspace with their nickname, name,
user uuid, two-factor status, and invitation state. Two-factor status is
tri-state: enabled, disabled, or unknown. Bitbucket does not always report
it, and the extension keeps that absence as its own value rather than folding it
into disabled, which would manufacture a security finding the data does not
support; unknown means go and check, not "this person is unprotected". The
invitation column separates members who have accepted from invitations still
outstanding.
Repositories
The Repositories list carries name, full name, visibility, whether the repo is archived, and its slug. Its value shows up in combination with the other three: filtering it and reading the grants that land on each row is how you find a repository whose access outlived its use.
Repository Grants
The fourth list is one row per grant edge, not per repository, so a repo that five people and two groups can reach is seven rows. Each row names the principal and its type, the repository the grant lands on, and the permission Bitbucket recorded, alongside three things Bitbucket's own UI does not give you side by side: a normalized permission level, an origin, and whether the grant is editable.
This is the one list that is not populated the moment you connect. Reading permissions costs a request per repository, which is the fan-out the rate-limit budget cannot afford at boot, so the list fills in as you drill into repositories rather than arriving whole.
The normalization keeps Bitbucket's raw permission word and also maps it to a level shared across providers, so "what does this grant actually let someone do" has a consistent answer whether you are looking at Bitbucket, GitHub, or GitLab. The permission column preserves exactly what Bitbucket returned; the normalized level column is what the extension resolves it to. On a small workspace the two often read the same, and they diverge on the provider-specific words that have no direct equivalent elsewhere.
The origin is the provenance of the grant: direct when a member was granted on
the repository, group when the access comes through a user group, and
effective when Bitbucket would only tell us the computed result without the
underlying rule that produced it. An effective grant can be observed but not
edited, because there is no rule to edit; that constraint returns in the actions
below.
Following the derivation
The four lists are the inputs. The drilldowns perform the joins you would otherwise assemble across four Bitbucket pages.
Press Enter on a user group and you get its members. Bitbucket's legacy group endpoint hands memberships back inline with the group list, so this costs a single request for every group at once rather than one per group. The expensive reads are elsewhere: per-repository permissions and branch restrictions are fetched when you ask for them rather than at boot.
Drill the other relation on a group and you get the repositories that group can reach, derived from its grants. This one is reached from the command palette rather than Enter, because a row gets only one default drilldown and members is the more common question; the repositories view is a keystroke away rather than the default.
The member-to-repositories drilldown answers "what can this person actually
reach" by unioning all three derivation paths at once: repositories granted to
them directly, repositories they reach through every group they belong to, and -
if they own the workspace - every repository in it. Amelia has exactly one grant
in the list above, admin on kunobi, so the other three rows below come from
ownership alone. No permissions tab anywhere shows that path, because an owner's
reach is never written down as a grant; it follows from the ownership, and the
drilldown materializes it as rows.
And from the other side, drilling a repository gives you the grants that land on it - the same grant rows as the top-level list, narrowed to one repo, so "who can reach this one repository, and how" is a single view rather than a mental join across a permissions tab and two group pages.
Changing access
Reading is most of the job, but the extension can also change repository permissions.
There are three actions. Granting access asks for a principal type, the principal itself (a user uuid or a group slug), a permission, and - when it is not launched from a repository row - the repository slug.
Changing an existing grant's level works from the grant row itself.
Revoking access is destructive, so it asks you to type the repository slug to confirm before it will proceed.
This is where the effective origin from earlier returns. An effective-only grant
is one Bitbucket reported as a computed result without the rule behind it, so there
is no rule for the extension to edit. The change and revoke dialogs check for that
and disable their submit, explaining why the grant is read-only instead of letting
you fill in a form the API will reject.
One action is absent entirely: there is no "add member to group" or "remove member from group". Bitbucket's group-membership mutation API is legacy and unreliable, so rather than ship an action that fails in ways we cannot predict, the extension declares that capability unsupported and the action does not exist. There is also no Credentials kind here, which the GitHub extension has - Bitbucket ships none in this version. All three mutation actions are part of the Pro feature and are enforced against your license.
Protection posture
The last drilldown is read-only by design. Drill a repository's protection rules and you get its branch restrictions, grouped per pattern into a rule row: the pattern and the rule name, whether it requires review, how many approvals it needs, whether it blocks force-pushes, and whether it is enforced on admins. This maps onto Bitbucket's branch restrictions API, and it is deliberately observe-only - the extension reads restrictions but does not write them, so this view is posture you can audit, not a control surface. Like the group members, it is warmed lazily on the drill, and because it has no top-level preset it is reached from the command palette.
Where this goes
Today this is a version 0.1.0 extension for Bitbucket Cloud, behind Pro. It reads groups, members, repositories, and permissions, derives the paths between them, and can grant, change, and revoke repository access; it does not mutate group membership, it does not yet read the archived flag from Bitbucket, and it manages no credentials. The same model - permissions as data, with the derivation attached - already backs the GitHub and GitLab extensions, which share a common git-provider core underneath, so "who can reach what, and why" is answered the same way whichever provider a workspace lives on.
Cluster updates, in your inbox.
Kubernetes deep dives, GitOps field notes, and platform-engineering essays from the team building Kunobi. Two posts a month. No fluff.

