[Bug] UpgradeBadge and UpgradeButton render to non-owner users in the sidebar
Problem
The UpgradeBadge (shown next to feature-gated nav items in the sidebar) and the UpgradeButton (shown in the sidebar footer) render to any signed-in user, not just to the org's OWNER. A MEMBER (or even a former owner who lost the role) sees the same "Upgrade to Pro" prompts that are intended for the billing decision-maker.
The settings pages already do the right thing (they're OWNER-gated via authenticatedPage(..., { minRole: OrgRole.OWNER, ... })), so the upsell cards inside /settings/security and /settings/audit are only seen by owners. But the sidebar is shared across all users and has no such check.
The defaultSidebar/index.tsx already computes isOwner for the connection-stats notification dot, so the auth context is available — it just isn't being threaded down to the upgrade UI.
Background
Files affected:
packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx:117 — <UpgradeButton /> shown to all users when !isValidLicenseActive.
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx:123-134 — <UpgradeBadge /> shown to all users missing the required entitlement.
packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx:87-97 — same pattern in the settings sidebar.
packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx:43 — already computes isOwner (just not threaded down).
packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx — does not compute isOwner at all yet.
Current behavior
A MEMBER signed into a Sourcebot instance without a Pro license sees:
- An "Upgrade to Pro" button at the bottom of every page (in the sidebar footer).
- A small "Upgrade" badge next to the API Keys, SSO, SCIM, Audit, and MCP nav items in the settings sidebar.
A MEMBER cannot act on either of these — both link to the upgrade flow which is only meaningful for owners.
Expected behavior
Both the UpgradeButton and the UpgradeBadge should be hidden for any user whose role is not OrgRole.OWNER. The settings pages already do this correctly; the sidebar should match.
Proposed solution
- Add an
isOwner?: boolean prop to SidebarBaseProps and NavProps (in defaultSidebar and settingsSidebar).
- In
SidebarBase, change !isValidLicenseActive && <UpgradeButton /> to isOwner && !isValidLicenseActive && <UpgradeButton />.
- In each
Nav component, change showUpgradeBadge to require isOwner === true in addition to the entitlement check.
- In
defaultSidebar/index.tsx, pass isOwner={isOwner} to <Nav> and <SidebarBase> (the value is already computed at line 43).
- In
settingsSidebar/index.tsx, compute isOwner (using the same getAuthContext + OrgRole.OWNER pattern as defaultSidebar) and pass it to <Nav> and <SidebarBase>.
- Add a vitest case for the
Nav component to lock in the behaviour: badge is rendered for isOwner={true} + missing entitlement, and not rendered for isOwner={false}.
Alternatives considered
- Hide the entire sidebar for non-owners. Rejected: the search nav items are useful for non-owners, and the rest of the sidebar is a perfectly normal product surface. The upgrade UI is the only thing that should be gated.
- Move the upgrade prompt to a banner shown only on the owner's dashboard. Larger change; the issue is specifically about the sidebar pop-up.
- Make
UpgradeBadge itself check the role internally. The badge component is a tiny presentational element; gating belongs in the parent Nav so all callers (default and settings) get the same behaviour without each importing the auth context.
Scope
- 4 files modified (
sidebarBase.tsx, defaultSidebar/nav.tsx, defaultSidebar/index.tsx, settingsSidebar/nav.tsx, settingsSidebar/index.tsx).
- 1 new test file (
defaultSidebar/nav.test.tsx) with 2 vitest cases.
- No schema, no dependency, no migration. Pure frontend fix.
Acceptance criteria
Backward compatibility
Pure UI behaviour change. No API, no schema. Existing users on the OWNER role see no change. Non-owner users stop seeing the prompts they couldn't act on.
Risks
Minimal. The settings pages already gate on OWNER, so the upsell cards inside /settings/security and /settings/audit are unaffected. The only behaviour change is in the sidebar, which is shared UI.
[Bug] UpgradeBadge and UpgradeButton render to non-owner users in the sidebar
Problem
The
UpgradeBadge(shown next to feature-gated nav items in the sidebar) and theUpgradeButton(shown in the sidebar footer) render to any signed-in user, not just to the org's OWNER. A MEMBER (or even a former owner who lost the role) sees the same "Upgrade to Pro" prompts that are intended for the billing decision-maker.The settings pages already do the right thing (they're OWNER-gated via
authenticatedPage(..., { minRole: OrgRole.OWNER, ... })), so the upsell cards inside/settings/securityand/settings/auditare only seen by owners. But the sidebar is shared across all users and has no such check.The
defaultSidebar/index.tsxalready computesisOwnerfor the connection-stats notification dot, so the auth context is available — it just isn't being threaded down to the upgrade UI.Background
Files affected:
packages/web/src/app/(app)/@sidebar/components/sidebarBase.tsx:117—<UpgradeButton />shown to all users when!isValidLicenseActive.packages/web/src/app/(app)/@sidebar/components/defaultSidebar/nav.tsx:123-134—<UpgradeBadge />shown to all users missing the required entitlement.packages/web/src/app/(app)/@sidebar/components/settingsSidebar/nav.tsx:87-97— same pattern in the settings sidebar.packages/web/src/app/(app)/@sidebar/components/defaultSidebar/index.tsx:43— already computesisOwner(just not threaded down).packages/web/src/app/(app)/@sidebar/components/settingsSidebar/index.tsx— does not computeisOwnerat all yet.Current behavior
A MEMBER signed into a Sourcebot instance without a Pro license sees:
A MEMBER cannot act on either of these — both link to the upgrade flow which is only meaningful for owners.
Expected behavior
Both the
UpgradeButtonand theUpgradeBadgeshould be hidden for any user whose role is notOrgRole.OWNER. The settings pages already do this correctly; the sidebar should match.Proposed solution
isOwner?: booleanprop toSidebarBasePropsandNavProps(indefaultSidebarandsettingsSidebar).SidebarBase, change!isValidLicenseActive && <UpgradeButton />toisOwner && !isValidLicenseActive && <UpgradeButton />.Navcomponent, changeshowUpgradeBadgeto requireisOwner === truein addition to the entitlement check.defaultSidebar/index.tsx, passisOwner={isOwner}to<Nav>and<SidebarBase>(the value is already computed at line 43).settingsSidebar/index.tsx, computeisOwner(using the samegetAuthContext+OrgRole.OWNERpattern asdefaultSidebar) and pass it to<Nav>and<SidebarBase>.Navcomponent to lock in the behaviour: badge is rendered forisOwner={true}+ missing entitlement, and not rendered forisOwner={false}.Alternatives considered
UpgradeBadgeitself check the role internally. The badge component is a tiny presentational element; gating belongs in the parentNavso all callers (default and settings) get the same behaviour without each importing the auth context.Scope
sidebarBase.tsx,defaultSidebar/nav.tsx,defaultSidebar/index.tsx,settingsSidebar/nav.tsx,settingsSidebar/index.tsx).defaultSidebar/nav.test.tsx) with 2 vitest cases.Acceptance criteria
UpgradeButtonis not rendered for a signed-in user whose role isMEMBER(or who is not signed in).UpgradeBadgeis not rendered for a signed-in user whose role isMEMBER(or who is not signed in), in either the default or settings sidebar.Backward compatibility
Pure UI behaviour change. No API, no schema. Existing users on the OWNER role see no change. Non-owner users stop seeing the prompts they couldn't act on.
Risks
Minimal. The settings pages already gate on OWNER, so the upsell cards inside
/settings/securityand/settings/auditare unaffected. The only behaviour change is in the sidebar, which is shared UI.