feat(konnect): move Konnect projects into a global Control Planes organization [INS-3148] - #10315
Open
yaoweiprc wants to merge 1 commit into
Open
feat(konnect): move Konnect projects into a global Control Planes organization [INS-3148]#10315yaoweiprc wants to merge 1 commit into
yaoweiprc wants to merge 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR makes Konnect projects account-global by moving them under a dedicated local-only “Control Planes” organization (org_konnect_<accountId>), and removes the sidebar’s Projects/Konnect tab split in favor of organization-driven UI/behavior.
Changes:
- Add a local-only Konnect organization model and widen “local organization” API guards to cover both Scratchpad and Control Planes.
- Aggregate the
konnectSyncfeature flag across all user organizations with caching + TTL, and surface Control Planes based on the aggregated flag or existing Konnect data. - Add an idempotent migration that re-parents Konnect projects to the Control Planes org (with a conflict-resolution modal) plus updated unit + smoke tests.
Reviewed changes
Copilot reviewed 40 out of 40 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/insomnia/src/ui/utils/router.ts | Adds startup async task to sync the aggregated Konnect feature flag and restores last visited Control Planes org id. |
| packages/insomnia/src/ui/organization-utils.ts | Implements aggregated konnectSync caching (TTL) and guards syncProjects against local-only orgs. |
| packages/insomnia/src/ui/hooks/use-organization-features.tsx | Prevents org-scoped permission/feature loads for any local-only organization. |
| packages/insomnia/src/ui/hooks/konnect-sync-trigger.ts | Adds a small registry to trigger Konnect sync without prop-drilling callbacks. |
| packages/insomnia/src/ui/context/app/insomnia-event-stream-context.tsx | Guards real-time collaborator presence and SSE subscription for local-only orgs. |
| packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/use-sidebar-drag-and-drop.tsx | Blocks dragging collection workspaces across Konnect projects to avoid orphaned Konnect keys. |
| packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/use-project-navigation-sidebar-navigation.ts | Removes tab-driven navigation logic; navigation is now organization-driven. |
| packages/insomnia/src/ui/components/sidebar/project-navigation-sidebar/project-navigation-sidebar.tsx | Removes Projects/Konnect tabs and switches sidebar controls based on whether org is Control Planes. |
| packages/insomnia/src/ui/components/settings/import-export.tsx | Avoids feature/workspace fetches for local-only orgs. |
| packages/insomnia/src/ui/components/project/organization-select.tsx | Renders Kong logo for Control Planes in the org dropdown and selected value. |
| packages/insomnia/src/ui/components/modals/request-settings-modal.tsx | Avoids fetching org workspaces for local-only orgs. |
| packages/insomnia/src/ui/components/modals/konnect-settings-modal.tsx | Uses the new sync-trigger registry instead of a ref passed from the sidebar. |
| packages/insomnia/src/ui/components/modals/konnect-org-migration-modal.tsx | Adds conflict-resolution modal for choosing which org’s Konnect data to keep. |
| packages/insomnia/src/ui/components/header-invite-button.tsx | Skips org-permissions request for local-only orgs. |
| packages/insomnia/src/ui/components/dropdowns/git-project-sync-dropdown.tsx | Avoids storage-rule loads for local-only orgs. |
| packages/insomnia/src/routes/untracked-projects.tsx | Treats Control Planes org id as “tracked” to avoid false untracked banners. |
| packages/insomnia/src/routes/organization.tsx | Injects Control Planes into the org list, hides collaboration UI for local orgs, and mounts the migration modal. |
| packages/insomnia/src/routes/organization.sync-organizations-and-projects.tsx | Chains org sync → aggregated Konnect feature sync, and triggers Konnect migration after project migration. |
| packages/insomnia/src/routes/organization.$organizationId.project.new.tsx | Guards org-scoped reporting and disallows non-local storage types in local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.workspace.$workspaceId.spec.tsx | Updates storage-rule fetch guard to local-only org predicate. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.update.tsx | Disallows non-local storage types in local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId.tsx | Removes sidebar-tab state and switches outlet context to isKonnectOrganization. |
| packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx | Uses isKonnectOrganization to gate first-request creation UI and guards storage rules fetch. |
| packages/insomnia/src/routes/organization.$organizationId.project._index.tsx | Guards storage-rule loads for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.permissions.tsx | Short-circuits feature/billing loads for local-only orgs and merges konnectSync into cache. |
| packages/insomnia/src/routes/organization.$organizationId.members.$userId.roles.tsx | Guards member-role mutation for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.collaborators.tsx | Returns empty collaborator results for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.collaborators.invites.$invitationId.tsx | Guards invitation-role mutation for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.collaborators.invites.$invitationId.reinvite.tsx | Guards reinvite action for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.collaborators-search.tsx | Returns empty collaborator search results for local-only orgs. |
| packages/insomnia/src/routes/organization.$organizationId.collaborators-check-seats.tsx | Guards seat-check calls for local-only orgs. |
| packages/insomnia/src/routes/organization._index.tsx | Ensures aggregated Konnect feature sync + migration run in the login redirect path. |
| packages/insomnia/src/routes/git-credentials.$id.related-projects.tsx | Treats Control Planes org id as “owned” to avoid cross-account leakage in related projects. |
| packages/insomnia/src/konnect/migrate-konnect-organization.ts | Adds detection and execution of Konnect project migration into Control Planes. |
| packages/insomnia/src/konnect/tests/migrate-konnect-organization.test.ts | Adds unit tests covering auto/conflict/orphan-ignore/idempotency and cascading delete expectations. |
| packages/insomnia/src/common/organization-storage-rules.ts | Uses local-only organization predicate to force local-only storage rules. |
| packages/insomnia-smoke-test/tests/smoke/konnect.test.ts | Updates smoke tests to enter Konnect via org dropdown and validate hiding when flag disabled. |
| packages/insomnia-smoke-test/playwright/pages/components/navigation-sidebar.ts | Updates test page object from “tabs” to organization selection helpers. |
| packages/insomnia-data/src/models/organization.ts | Adds Konnect org constants/helpers and the unified isLocalOrganizationId predicate. |
| docs/konnect-control-planes-organization-plan.md | Adds design/plan doc describing the approach, risks, and verification checklist. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+102
to
+105
| const organizations = JSON.parse(localStorage.getItem(`${accountId}:spaces`) || '[]') as Organization[]; | ||
| if (organizations.length === 0) { | ||
| return; | ||
| } |
Comment on lines
+73
to
+74
| const parsed = JSON.parse(raw) as KonnectSyncEnabledCache; | ||
| return typeof parsed?.enabled === 'boolean' ? parsed : null; |
| }; | ||
| syncKonnectProjectsAndNotifyRef.current = syncKonnectProjectsAndNotify; | ||
|
|
||
| registerKonnectSyncTrigger(syncKonnectProjectsAndNotify); |
Comment on lines
+33
to
+36
| <ModalOverlay | ||
| isOpen | ||
| className="fixed top-0 left-0 z-10 flex h-(--visual-viewport-height) w-full items-center justify-center bg-black/30" | ||
| > |
✅ Circular References ReportGenerated at: 2026-08-06T10:15:54.582Z Summary
Click to view all circular references in PR (9)Click to view all circular references in base branch (9)Analysis✅ No Change: This PR does not introduce or remove any circular references. This report was generated automatically by comparing against the |
…anization
Konnect projects move from `parentId: <real org>` to one account-wide,
local-only organization `org_konnect_<accountId>` ("Control Planes"), and the
Projects/Konnect tab split is removed from the project navigation sidebar.
- Add the fake organization helpers and the `isLocalOrganizationId` predicate
every "skip all remote APIs" branch now uses.
- Aggregate the per-organization `konnectSync` flag into one account-wide,
TTL-cached value and inject the fake organization from the layout loader.
- Guard the organization-scoped remote calls that were never guarded for
Scratchpad: collaborators, invites, member roles, check-seats, presence, the
SSE stream, and the team-project/git-project-count mutations.
- One-time migration in `entry.client.tsx` before hydration, with a modal for
the ambiguous case where several organizations hold Konnect data.
- Hide project creation inside Control Planes, including the `NoProjectView`
create form.
Design notes, audit findings and verification steps live in
docs/konnect-control-planes-organization-plan.md.
INS-3148
yaoweiprc
force-pushed
the
feat/migrate-konnect
branch
from
August 6, 2026 10:09
88c84ee to
b623f1f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Konnect PAT is stored once per account (Electron
safeStorage, keykonnectPat), but the projects it syncs were created withparentId: organizationId— so they belonged to whichever organization happened to be active. The same PAT could therefore be synced independently under several organizations, producing duplicated Konnect data with no single source of truth.Solution
Konnect projects now live in a hardcoded, local-only organization
org_konnect_<accountId>, displayed as Control Planes in the organization dropdown (same pattern as the existing Scratchpad organization). The account id is baked into the id because the NeDB database is not partitioned per user.The Projects/Konnect tab split in the navigation sidebar is removed — the sidebar now switches its header controls (Sync button, gear, sync progress, sync result panel, intro card) based on the active organization instead of a persisted tab.
Feature flag
konnectSyncis a per-organization feature flag, but the Konnect organization is account-wide, so it is now the OR across every organization the user belongs to. It is fetched concurrently once per app start via a newAsyncTask.SyncKonnectFeatureand cached in localStorage with a 6h TTL. A failed lookup never downgrades a cachedtrue, so the flag survives being offline.The organization is visible when the flag is on, or when the account already has Konnect data from a previous version — in which case it stays reachable but the Sync button is disabled.
Migration
On startup, existing Konnect projects are re-parented onto the Konnect organization:
The migration is idempotent by predicate (no "already migrated" flag), so dismissing the modal simply re-prompts on the next start.
API guards
Roughly half of the organization-scoped API calls were never guarded for Scratchpad — Scratchpad gets away with it because its entry point is hidden. Control Planes is reachable from the dropdown, so each one now short-circuits on a shared
isLocalOrganizationIdpredicate: features, storage rules, team projects, collaborators, collaborator search, check-seats, invitations, reinvites, member roles, git project count, real-time presence and the SSE stream.HeaderPlanIndicatoris deliberately kept — perdocs/header-plan-indicator-data-scope.mdeverything it displays is account-scoped and fetched withsessionIdonly, so hiding it would just make billing info vanish for no reason.Notable details
buildKonnectOrganizationcopiesis_ownerfrom the user's real organizations rather than hardcodingtrue;usePlanDataderivesisOwnerfrom the organization list, and hardcoding it would show an enterprise member the wrong "upgrade yourself" CTA.canDroplogic (which only blocks cloud projects) allowed it — the workspace would keep akonnectServiceIdunder the wrong parent and the next sync would delete or duplicate it.KonnectSettingsModalno longer takes a ref prop and works regardless of what renders it.parentIdrewrites but not the deletions:database.bufferChanges()is not re-entrant anddb.remove()buffers and flushes internally, which defeats an outer buffer. This is documented in the plan; the pre-existing instance of the same pattern in the disconnect flow is left alone.Drive-by fix
syncProjectsissuedfetchTeamProjectsbefore checking whether the organization was local, so the Scratchpad guard never actually prevented the request. The guard now runs first.Test plan
npm run type-check— cleannpm run lint— 0 errorsnpm test— 2169 passing; the 6 failures are pre-existing ondevelop(verified viagit stash)migrate-konnect-organization.test.ts— 10 cases covering auto / conflict / orphan-ignored / idempotency / cascade delete / timestamp carry-overManual verification still to do (24 checks listed in the plan doc), most importantly: the DevTools Network audit inside Control Planes, the two-organization conflict flow, and confirming account B cannot see account A's data.
Docs
docs/konnect-control-planes-organization-plan.mdrecords the design, the audit findings (unguarded API surface, non-re-entrant buffering, the two classes of${accountId}:spacesreaders), the decisions taken, and the list of scratchpad special-cases deliberately not extended — notably the organization dropdown itself, which must stay visible or the user would be trapped inside Control Planes.