Skip to content

Bitbucket

The Bitbucket extension connects a Bitbucket Cloud workspace to Kunobi and turns its access model into browsable data. You get four lists - user groups, members, repositories, and repository permission grants - plus the drilldowns that join them: the members of a group, the repositories a group can reach, everything a single person can reach and why, and every grant that lands on one repository. From those views you can grant, change, and revoke repository permissions.

Bitbucket appears in two places: the Sources page, where you add and manage workspace connections, and the Kubernetes data page, where you browse what they contain. That page hosts non-cluster resources too, which is why Bitbucket's lists live there. If Kunobi's layout is new to you, Interface & Navigation covers the window regions and Workspaces covers the workspace selector and presets both pages use.

The extension talks to Bitbucket Cloud only (api.bitbucket.org); Bitbucket Data Center and Server are not supported. One connection reads exactly one workspace, and access is read-mostly: repository permissions are the only thing it can change. Group membership cannot be edited from Kunobi because Bitbucket's REST API does not support it, so no such action exists.

Bitbucket is a Pro feature - without a Pro license the Sources workspace is replaced by a "Workspace sources" upgrade notice reading "Upgrade to Kunobi Pro to connect and manage workspace sources.", and the data views are replaced by a "Bitbucket requires a Pro license" pane with an Upgrade button. Because that pane takes over the whole table there is normally no row to select, so the permission actions are out of reach rather than shown as locked. See Plans & Pro.

Setup

  1. Install the Bitbucket extension from Settings → Extensions (see Extensions). It starts automatically. If you open a Bitbucket view while the extension isn't running, Kunobi shows a "Bitbucket extension required" notice reading "Enable the Bitbucket extension to manage workspaces."

  2. Create an Atlassian API token for the account that will read the workspace. Bitbucket app passwords are not accepted - they reached end of life and the extension only takes API tokens.

  3. Give the token these scopes:

    • read:workspace:bitbucket
    • read:user:bitbucket
    • read:repository:bitbucket
    • read:permission:bitbucket
    • write:permission:bitbucket
    • delete:permission:bitbucket

The workspace, user and repository scopes cover what the extension reads; the three :permission: scopes cover the permission grant reads and the grant, change and revoke actions. Check the current names against Atlassian's own API token documentation - Atlassian owns these strings and has changed them before.

Two things a token can be short of without being obviously wrong:

  • If the connection is refused with a 403, the extension's hint points at the workspace and repository read scopes (read:workspace:bitbucket and read:repository:bitbucket) as the first things to check.
  • Reading a repository's protection rules additionally requires admin on that repository. A token that reads the workspace fine can still be refused every branch-restriction read.

User groups are read through Bitbucket's legacy /1.0/groups/{workspace} endpoint, because Bitbucket Cloud's current API has no way to enumerate a workspace's user groups.

How Permissions Are Modelled

Bitbucket's access model is the thing this extension exists to make legible, and it is worth reading before you change anything with it. The short version: the same repository can be reachable by one person through several paths at once, only some of those paths can be edited from Kunobi, and the views name the path rather than resolving it for you.

What a grant row is

A Repository Grant row is one edge joining a principal to a repository with a permission. The principal is either a member (identified by their Bitbucket user uuid) or a user group (identified by its group slug). Each row carries:

ColumnWhat it holds
Principal typemember or group.
PrincipalThe human-readable name - a member's nickname, or the group's name. The uuid or slug the write actions actually send is kept behind the scenes, so renaming a person never changes what a mutation targets.
RepositoryThe repository slug.
PermissionThe raw permission string Bitbucket returned, preserved verbatim - normally read, write or admin.
Normalized levelThe same permission on Kunobi's cross-provider ladder: read, write, admin, or unknown if Bitbucket returned something unrecognized. Display and sorting only.
OriginHow the edge was derived - direct, group or effective. See Every way a grant can be derived.
EditableWhether this specific edge is addressable for a write. true is necessary but not sufficient: if the repository's permission read is anything less than clean, every action on it is refused regardless - see Locked grants.
Last errorBlank on a healthy row. Carries the reason when the read behind this row failed - see the Last error column.

One principal can hold several rows (one per repository), and one repository can collect many rows. The rows are edges, not a merged verdict.

Every way a grant can be derived

There are three edge origins and two further reach paths that never become grant rows.

direct - a per-repository user permission, read from Bitbucket's repository permissions-config users endpoint. This is what "add a user to this repository" produces. Editable.

group - a per-repository group permission, read from the repository permissions-config groups endpoint. Editable.

effective - a fallback. When the permissions-config users endpoint is unavailable for a token or repository (Bitbucket answers 401, 403 or 404), the extension falls back to Bitbucket's effective repository permissions instead of showing nothing. Effective permissions are the flattened answer: they tell you a person's resulting permission on a repository without saying whether it came from a direct grant or from a group. Every row derived this way is marked effective, is always read-only, and carries a note explaining the substitution in its Last error column. On a fallback read, group permissions have no representation at all - a permission a person only holds via a group appears as a member row, and the group itself gets no row.

The two permissions-config reads fail independently. If only the groups read is refused, there is no fallback: you get the normal editable direct rows, a note that group permission provenance is unavailable, and no group rows at all - which looks exactly like a repository that genuinely has no group grants. Check the repository's Last error column for that note before concluding no group can reach it. Those direct rows still read Editable true, but the note counts as a failed read of the repository's permissions, so every permission action on that repository is refused anyway - see Locked grants.

Two more paths exist that Bitbucket never writes down as a grant, so they can never appear in the Repository Grants list:

Workspace ownership - a member whose workspace role is owner reaches every repository in the workspace with full authority. Bitbucket stores this as a workspace role, not as a repository grant. Kunobi materializes it as rows in Member → Repositories with the origin org-role.

Group membership - belonging to a group that holds a group grant on a repository. The grant row names the group, not the person, so the individual's reach through it only becomes visible in Member → Repositories with the origin group.

How the derivation path is displayed

Repository Grants shows the edge's own origin in its Origin column: direct, group or effective.

Member → Repositories shows a different, member-centred origin in its Origin column: direct, group or org-role. It is computed by unioning all three reach paths for that member and deduplicating by repository.

Group → Repositories derives its rows from the group's grants, so every row there reads group.

Two things to know about the two principal-scoped drilldowns. First, they read the same per-repository grant data as everything else, so they only see repositories whose grants have been loaded (see Grants are loaded on demand): on a freshly connected workspace, Group → Repositories is empty, and the only reach visible in Member → Repositories is org-role, which comes from the workspace-wide member read and is always complete.

Second, an effective grant still counts as a direct reach here, so a repository a member reaches only through a read-only effective row shows direct in Member → Repositories. That column tracks the path, never whether the underlying grant is editable - Repository Grants is where you read that.

When several paths overlap

If a member reaches the same repository more than one way, Member → Repositories lists that repository once and shows the most specific origin. Specificity is assigned weakest first, so the strongest label wins:

  1. Workspace ownership marks every repository org-role.
  2. A group grant on a repository the member's groups hold overwrites it with group.
  3. A direct grant to the member overwrites it with direct.

That column answers how the member reaches the repository. It is not a computed effective permission level: Kunobi does not merge overlapping paths into a single resulting permission, and the Member → Repositories row carries no permission column at all. To read the levels, go to Repository Grants, which lists every edge separately - a person can appear there once for their own write grant while a group they belong to appears again with admin on the same repository. Bitbucket resolves the real outcome; Kunobi shows you every input to that resolution.

Locked grants

A grant with Editable false cannot be changed or revoked from Kunobi. For Bitbucket this happens for exactly one reason: the row came from the effective-permissions fallback, and the reason recorded on it is effective. Such a row is a flattened reading of Bitbucket's answer, not a real, addressable edge - there is nothing at that address to write to, and writing what the row appears to say could easily create a direct grant that did not exist while leaving the underlying group grant untouched.

The block is enforced twice, and the two are not the same shape:

  • In the dialog, per grant. Opening Change permission level on a locked grant shows "This grant is effective-only / managed externally and cannot be changed here: effective" and disables the confirm button. Revoke repository access shows the same sentence with "revoked" in place of "changed", and also disables its button.
  • In the backend, per repository. A mutation only runs when the target repository's permission read completed cleanly and completely. Anything less blocks it - the effective-permissions fallback, a refused permissions-config/groups read, a plain error, or a read that stopped at Kunobi's page cap - with a message containing "snapshot unavailable or incomplete for this connection" and naming the repository's permissions slice. That refusal covers every principal on that repository, including granting access to somebody who has none there yet, and including a call made outside the dialog.

So whenever a repository's permission read is anything less than clean, treat the whole repository as read-only from Kunobi, not just the rows marked locked - and note that the rows can still show Editable true while the repository as a whole is refused. That is deliberate: the extension cannot see the real edges.

A capped read is refused for a related reason. If a repository's grant list stopped at Kunobi's page limit, the list on screen is real but short, and the grant you want to change might be one of the rows past the cap - which is indistinguishable from a grant that does not exist. The refusal says so and points you at Bitbucket. A cap elsewhere is harmless: the only read this guard consults is the target repository's own permission slice.

To change an effective-only permission, change it in Bitbucket at whichever source actually produces it, or give the token the permission scopes it needs so the permissions-config reads succeed and the grants come back editable.

Managing Workspaces

Bitbucket workspaces are managed on the Sources page, which covers the table, the dialogs and the shortcuts they all share. Open Sources, press W, and pick Bitbucket Workspaces. What follows is what is specific to Bitbucket.

Workspace shows the workspace slug on both lines - there is no separate connection name, and editing the slug relabels the connection with it.

StatusMeaning
disconnectedNot connected. Also what a connection shows before it has ever been connected in this session - lifecycle state is not restored across restarts.
connectingThe token probe is running.
connectedThe token probe succeeded and the background refresh is running.
errorThe connection attempt failed. The reason is kept on the connection record.

The sidebar Overview lists Id, Label, Workspace, Email, Token state, Locked, Rate remaining, Last poll and Status, plus Last error and Last updated once the connection has been connected at least once in this session.

Token state is the field to read when a connection will not come up: active when the token works, invalid when Bitbucket rejected it, and unknown both before any probe has run and after a probe that was rate-limited rather than answered. Last poll is the sidebar's name for what the table calls Last check. Rate remaining is never filled in, so it always shows -.

Adding a Workspace

Press Add (n) and fill in three fields:

FieldNotes
Workspace slugThe workspace this connection reads, for example acme. One connection covers one workspace, and a second connection to a workspace that is already configured is rejected (case-insensitively) with "connection to workspace 'acme' is already configured".
UsernameThe Atlassian account email. This is the HTTP Basic username - not a Bitbucket nickname.
PasswordThe Atlassian API token. This is the HTTP Basic password.

Poll interval (s) is not on this form - a new connection uses the extension default, and you can change it afterwards by editing the connection. It is never pre-filled there either, so leaving it blank keeps the stored cadence.

The token is written to the keychain before the connection row is created, so a keychain failure leaves nothing half-created. The connection then connects immediately, which is why a new row goes straight to connecting.

Connecting & Disconnecting

Bitbucket is a per-tab source: each drilldown tab reads one connection you choose for it, and there is no single global "active" workspace. Connect (c) probes the token by reading the workspace, and only a genuinely active token becomes connected - a token that reaches Bitbucket but is rejected lands in error and starts no background refresh.

Disconnect (d) stops the background refresh and drops that connection's cached snapshot, so reconnecting starts from a fresh workspace-wide read. The connection and its stored token are kept; this is not a removal. Disconnecting is app-wide, so it removes the connection from every tab at once.

Browsing Bitbucket Data

Open the Kubernetes data page, open the workspace selector (press W), and pick Bitbucket Workspaces.

Until a connection is bound to the tab, the data pane shows a "No workspace connected" notice reading "Connect a workspace to see its data here." with a Manage connections button that opens the connection picker. Pick one there (see Switching connections) and the views fill in.

The sidebar lists four presets - filter them with the box at the top:

  • User Groups - the workspace's user groups, plus the workspace itself as the top-level group. Columns: Name, Slug, Workspace (the parent workspace slug, blank for the workspace's own row), Members (the group's member count, read inline with the group listing), Group id, Last error.
  • Members - everyone seen across the connection. Columns: Nickname, Name (display name), User uuid, Workspace role (owner, collaborator or member, as Bitbucket reports it), Group role, 2FA, Invitation, Last error. Group role is only filled in inside the Group → Members drilldown, where it repeats the workspace role inside the workspace's own top-level group, and is always member inside a real user group - Bitbucket has no per-member role on a user group. 2FA is deliberately three-valued: enabled, disabled, or unknown when Bitbucket did not report it at all - an absent answer is never shown as disabled. Invitation would read pending for somebody invited but not yet accepted, but Bitbucket Cloud exposes no public endpoint for pending invitations, so in practice the column is always blank; the missing endpoint is treated as a silent no-op rather than an error.
  • Repositories - the workspace's repositories. Columns: Name (short name), Full name (workspace/repo), Visibility (private or public), Archived, Slug, Origin, Last error. Archived is always false: Bitbucket's repository listing does not report an archived flag, so the column carries no information. Origin is blank here and only fills in inside the principal-scoped drilldowns.
  • Repository Grants - the permission edges, with the columns described in How Permissions Are Modelled. It covers the repositories whose grants have been loaded, not necessarily every repository in the workspace - see Grants are loaded on demand.

These behave like any other resource view: search them, select rows, and open details in the sidebar. While the Bitbucket workspace is active, the kind filter shows only Bitbucket kinds, and it empties when the tab's connection is not connected.

Protection Rules

A fifth kind, Protection Rules, has no preset of its own and is reached only by drilling a repository. It shows one row per branch pattern, with columns Pattern, Name, Requires review, Approvals, Blocks force-push, Enforces admins, Last error.

Pattern and Name always match: Bitbucket branch restrictions have no separate name. A pattern matched by branching model rather than a glob is shown in brackets, for example [development].

Approvals is the highest count required across that pattern's count-bearing review restrictions. The review-group variant still sets Requires review but is excluded from the count, because it gates on group membership rather than a number - so a pattern can require review with Approvals blank.

Enforces admins is a judgement, not a Bitbucket field. Bitbucket has no "applies to admins" flag, so the rule reads true only when no restriction on that pattern exempts any user or group, and false as soon as anyone can bypass it.

Grants are loaded on demand

Connecting a workspace and every background poll fetch only the cheap workspace-wide lists: the workspace, its members and their roles, the user groups with their members, the repositories, and any pending invitations. Per-repository permission grants and protection rules are not part of that read - Bitbucket's request budget is small enough that fanning out across every repository on every poll would exhaust it.

A repository's grants are fetched when you drill from a repository into its Repository Grants, and its rules when you drill into its Protection Rules. Group members are re-fetched when you drill from a group into its Members.

Once loaded, they stay loaded. A background poll re-reads only the workspace-wide lists, so it carries every already-warmed repository's grants forward rather than dropping them - but only for repositories the fresh listing still returns, so a repository deleted in Bitbucket disappears from Kunobi rather than lingering. Carried grants are older than the rest of the snapshot and nothing marks them as such; drilling the repository again re-reads them.

One thing loads every repository's grants at once: the full re-read that follows a successful permission action (and the bitbucket__refresh tool over MCP). After one of those, the top-level Repository Grants preset covers the whole workspace rather than only what you opened.

Loading also gates permission changes. A change is refused while the target repository's permissions have not been read, because the lock check described under Locked grants would otherwise be running blind - see "A permission action is refused" under Troubleshooting.

The Last error column

Every one of the five kinds ends with a Last error column, blank on a healthy row. Bitbucket reads a workspace in slices - the members list, the repository list, one slice per repository's permissions, one per group's members - and a slice that fails is recorded without aborting the others. Last error is where a failure lands on the rows it affected.

It matters most where an empty result is ambiguous. A user group whose members read was refused shows the reason here; without it, that group would be indistinguishable from one that genuinely has no members. Same for a repository whose permission read failed versus one with no grants.

A read that succeeded but stopped at a page cap also shows here, prefixed truncated:. That is not a failure - the rows you can see are real, there are just more of them in Bitbucket - but it does block permission changes on that repository, as described under Locked grants.

When the slice a view is built from fails outright, that view is replaced by a full-pane error instead, headed "Unable to Load Resources" and naming the slice and Bitbucket's message. Each view watches its own: User Groups the group listing, Members the member listing, Repositories and Repository Grants the repository listing, and Protection Rules that repository's branch restrictions. A failure is contained to the views that read it, so a failed member listing blanks Members while Repositories still lists rows normally. A page cap alone never triggers it.

Drilling Between Kinds

Each kind links to the next. Pressing Enter (or double-clicking) follows the first relation; the others are reachable from the command palette (⌘P / Ctrl+P). Escape goes back. See Drilldown.

FromEnterCommand palette
User GroupMembers - the group's membersRepositories - repositories the group holds a grant on
MemberRepositories - everything the member reaches, with the Origin that produced it
RepositoryRepository Grants - the grants landing on that repositoryProtection Rules - that repository's branch restrictions

Drilling a repository into its grants or its rules triggers the on-demand fetch described in Grants are loaded on demand, so the first entry into one of those views may briefly show a loading state. Drilling a group into its members re-reads members that are already present, so that view fills in immediately.

Resource Details

Bitbucket rows are not Kubernetes objects, so selecting one opens two sidebar tabs:

  • Overview - the generic resource properties: name, namespace, created, API version, kind and uid. Namespace and created are always - for Bitbucket rows.
  • YAML - the full row, including every field in its spec. This is where to read values that have no column, such as a locked grant's reason. It is read-only: there is no cluster to write back to.

There is no Events tab for Bitbucket rows.

Permission Actions

All three actions target a single selected row and need a Pro license. Only Grant repository access has a keyboard shortcut, and the action bar shows only actions that have one - so it is the only one that appears there. All three are in the command palette (⌘P / Ctrl+P). Keys are shown macOS-first; on Windows/Linux replace with Ctrl and with Shift.

The actions also need to know which connection the selected row belongs to; every row carries that, so if you see "No Bitbucket connection selected" the row was not one of the extension's own.

Grant Repository Access

Grant repository access (⌘⇧P / Ctrl+Shift+P) runs on a selected repository row and creates or overwrites a grant on it.

FieldNotes
Repository slugFixed to the repository you launched from; it cannot be changed in the dialog.
Principal typeMember (user) or User group.
User uuid / User group slugThe label changes with the principal type. A member is identified by their Bitbucket user uuid (visible in the Members view), a group by its slug (visible in User Groups). Names are not accepted.
PermissionRead, Write or Admin. Defaults to Read.

Press Grant access. On success a "Repository access granted" notification names the principal, repository and permission. Setting a permission for a principal that already has one overwrites it rather than failing.

Change Permission Level

Change permission level runs on a selected repository grant row. It has no shortcut - run it from the command palette. The dialog shows the grant it will change as principal type, principal and repository (for example member:alice → my-repo) and offers the same Read / Write / Admin choice, starting at the current permission. Press Change permission; success shows "Permission changed". A locked grant disables the button, as described in Locked grants.

Revoke Repository Access

Revoke repository access runs on a selected repository grant row and removes it entirely. It has no shortcut and is destructive, so it is confirmed by typing: the dialog explains whose access to which repository is about to go, and you must type the repository slug into Confirm repository slug before Revoke access becomes available. Success shows "Repository access revoked". A locked grant disables the button here too.

What happens when you confirm

All three actions do the same three things in order, and the dialog stays open until every one of them has finished:

  1. Check. For Grant and Change, the requested permission is validated. For all three, the target repository's permission read is checked: if it has not been loaded, or it failed, or it fell back to effective permissions, or it stopped at a page cap, the action is refused rather than guessed at.
  2. Write. The change is sent to Bitbucket.
  3. Re-read. The whole connection is refreshed - the full read, including per-repository permissions for every repository, not the cheap list read a poll does.

Only then does the dialog close and the success notification appear. That means the views behind it are already up to date when you see it: there is no separate wait for a poll to catch up.

Two consequences worth knowing before you press confirm on a large workspace. The re-read in step 3 grows with the number of repositories and spends a real part of Bitbucket's hourly request budget. And the write in step 2 has already happened by the time step 3 runs, so a failure reported after the write - including the 60-second cap the whole call runs under, which a big workspace's re-read can exceed - does not mean the permission change was rejected. If the dialog reports a failure, re-open that repository's grants and check what actually landed before retrying.

Every failure keeps the dialog open with the reason shown in red, alongside a matching "...failed" notification.

Polling Behavior

  • The extension wakes every 60 seconds and polls each connected connection whose own interval has elapsed. Disconnected connections are skipped entirely, and nothing is polled while the extension is Pro-locked.
  • The default cadence is 300 seconds. Bitbucket Cloud meters repository API access at roughly 1,000 requests per hour for a normal authenticated account, which is small enough that the default is deliberately conservative. Override it per connection with Poll interval (s) on the edit form, or extension-wide in the configuration file.
  • Each poll re-fetches only the workspace-wide lists: the workspace, its members and roles, its user groups with their members, its repositories, and any pending invitations. Per-repository permissions and protection rules are not re-fetched, and anything a drilldown already loaded is carried forward rather than dropped - see Grants are loaded on demand.
  • Because every poll retries every one of those reads, a slice that failed earlier - a members read refused by an under-scoped token, say - recovers by itself once the cause is fixed. No manual re-fetch is needed.
  • A failing connection backs off: the interval doubles per consecutive failure, up to 30 minutes (or the configured interval, if that is already longer). One success resets it.
  • A successful poll stamps the connection's Last check.
  • The data is a snapshot, not a history. Each read replaces the previous one; there is no time series and no change log.
  • Listings are capped at 1,000 pages per read. Hitting the cap records a truncated: note in the row's Last error rather than silently returning a short list, and the data that did arrive is still shown. A cap is not treated as a failure - it never blanks a view - but on a repository's permission read it does block permission changes there, because a short list cannot be told apart from a complete one. Pagination also stops if Bitbucket hands back a cursor it has already returned; that one is recorded as a real failure.

Automation over MCP

If you run Kunobi's MCP server, an AI client can call some of the extension's tools directly. The six read tools run immediately: bitbucket__refresh (re-read a connection), bitbucket__get_snapshot, bitbucket__refresh_repo_grants, bitbucket__refresh_group, bitbucket__refresh_repo_rules and bitbucket__get_rules. The three permission mutations are gated: they need a Pro license, the extension mutation tools setting enabled, and a human approval - either a fresh prompt or a temporary approval window you granted earlier that still covers the call - and they are recorded in the audit log. Connection management (add, edit, remove, connect, disconnect) is not exposed over MCP at all. Kunobi's in-app assistant cannot run any of them.

Configuration File

Non-secret connection metadata is persisted in a TOML file the extension reads at startup and rewrites whenever you change something in the app:

  • macOS: ~/Library/Application Support/<app-id>/extensions/bitbucket/config.toml
  • Linux: ~/.local/share/<app-id>/extensions/bitbucket/config.toml
  • Windows: %APPDATA%\<app-id>\extensions\bitbucket\config.toml

where <app-id> matches your Kunobi variant (for example ninja.kunobi.desktop).

# Extension-wide background refresh cadence, in seconds.
default_poll_interval = 300

[[connections]]
# Generated when the connection is added. It is also the keychain key for the
# API token - changing it orphans the stored token. Leave it alone.
id = "3f2b1c8e-5a7d-4e10-9b6f-2c4d8e1a7b03"
# The workspace this connection reads.
workspace = "acme"
# The Atlassian account email - the HTTP Basic username.
email = "admin@acme.io"
# Optional per-connection override; omit to use default_poll_interval.
poll_interval = 600

Two things to know:

  • API tokens are never in this file. They live in the OS keychain (see Security). The account email is not a secret and is stored here in plain text, because it is the Basic username and the connection cannot be rebuilt after a restart without it.
  • An older selected_connection_id key may be present in a file written by an earlier version. It is ignored, not an error - connections are now bound per tab and there is no global selection to persist.

Security

The API token goes to your operating system's credential store, keyed by the connection's generated id and never written to the config file or into any store the UI reads - see Where credentials are stored. The account email is not a secret and is kept in the config file, because it is the Basic username and the connection cannot be rebuilt without it.

One wrinkle worth knowing: the stable and unstable release channels share one application identifier, so they share both the stored tokens and the configuration file. Development, local and E2E builds each get their own.

The token is sent only to the origin the connection was configured for. When the extension follows a pagination cursor returned by Bitbucket, it verifies the cursor points at that same origin before attaching credentials, and refuses to follow it otherwise.

Troubleshooting

Connecting lands in error, or Token state reads invalid - the token was rejected. 401: wrong, expired or revoked. 403: missing scopes, starting with read:workspace:bitbucket and read:repository:bitbucket. 404: the workspace is not visible to that account, so check the slug and the account's membership. To replace a token, mint a new one in Atlassian and paste it into Password via Edit (e); saving reconnects.

Last check keeps ageing but Status still reads connected - failing polls back off instead of flipping status: the interval doubles per failure up to 30 minutes, and one success resets it. Press Test (t) for the current reason.

A view is empty and shows an error pane - the slice that view reads failed; the others are unaffected. The pane names the slice and Bitbucket's message: not permitted is a missing scope, unauthorized a bad token, not found a path this account cannot see. The next poll retries.

Rows are present but some look empty or wrong - check their Last error column. A partial read marks only the rows it affected, so a group with no members means something different depending on whether that column is blank.

Repository Grants is empty - no repository's grants are loaded yet. Select a repository and press Enter. Expect to do this per repository; there is no fan-out.

A grant shows Origin effective and Editable false - the permissions-config read was refused, so Bitbucket's flattened permissions were used instead. That whole repository is read-only until the token can read permissions-config - see Locked grants.

A permission action is refused - the message names the cause. Permissions aren't loaded yet: open that repository's Repository Grants first. Snapshot unavailable or incomplete: that repository's read failed or fell back to effective permissions, so see Locked grants. Hit a page cap: the repository has more permission rows than Kunobi pages through, so make the change in Bitbucket directly.

Group members error, or a group reports no members - group enumeration uses Bitbucket's legacy endpoint, which returns every group with its members inline, so a failure affects all groups at once. A missing group records an error rather than a zero, so an empty list with a blank Last error really is empty.

Protection Rules is empty or errors - branch-restriction reads need admin on that repository. Recorded per repository, so one failing does not blank the others.

Reads slow down or fail in bursts - you are near Bitbucket's request limit. Short throttles are waited out and retried; longer ones surface as errors and the connection backs off. Raise Poll interval (s) and drill into fewer repositories at once.

"This requires a Pro license." - bitbucket is not entitled on your license. In the app an upgrade pane replaces the views first, so this usually arrives from an MCP call or a session whose license lapsed. See Plans & Pro.

Limitations

  • Bitbucket Cloud only. Data Center and Server are not supported, and there is no way to point a connection at a self-hosted instance.
  • API tokens only. App passwords are not accepted, and there is no OAuth flow - the credential is always an email plus an API token.
  • One workspace per connection. Reading several workspaces means adding several connections; there is no cross-workspace view.
  • Repository permissions are the only writable thing. Group membership cannot be changed from Kunobi at all - Bitbucket's REST API does not support it, so the action does not exist rather than existing and failing. Creating, renaming or deleting groups, repositories and workspaces is likewise out of scope.
  • No standing-credential view. Bitbucket Cloud does not expose a listable inventory of app passwords or access tokens, and repository deploy keys are not read, so there is no way to audit standing credentials here.
  • Protection rules are read-only and need repository admin scope to read at all.
  • Repository grants load one repository at a time. They are not part of the workspace-wide read, so seeing the whole picture means drilling into each repository - a deliberate trade against Bitbucket's request budget. They persist once loaded, but they are a point-in-time read that only a re-drill or a permission action refreshes, and nothing on screen marks how old they are.
  • A very large repository cannot be edited from Kunobi. If a repository's permission list exceeds the paging limit, permission changes on it are refused rather than made against a list Kunobi knows is incomplete.
  • A repository whose permission read is not clean cannot be edited at all. The effective-permissions fallback loses provenance entirely (group grants have no representation, direct-versus-group cannot be distinguished), and a refused group read loses it silently - and in either case no permission action on that repository will run, because the extension cannot see the real edges to check against.
  • Bitbucket does not report everything. Per-member 2FA is only known when Bitbucket returns it; repository archival is never reported, so Archived is always false; there is no public pending-invitations endpoint, so Invitation is always blank; nothing records the rate-limit budget, so Rate remaining is always empty; and workspace-level governance settings such as enforced 2FA or a default member permission are not in the public API. None of these are guessed at.
  • Snapshots, not history. Every read replaces the last one. There is no change history, no audit trail of who changed a permission, and no alerting on drift.
Available for:
Apple macOS logomacOSMicrosoft Windows logoWindowsLinux logoLinux
Download Kunobi