Skip to content

[v5] [architect] pkg/hub mixes spoke-side client code and the SaaS control plane in one package (spoke ↔ hub boundary) #6054

Description

@kubestellar-hive

Architecture Finding

Type: coupling / missing package boundary
Affected area: src/pkg/hub (v5 branch, HEAD 99fca74), importers src/pkg/dashboard and src/cmd/hive

pkg/hub (66,543 non-test lines) contains two deployment roles in a single Go package:

  1. SaaS control-plane serversaas_*.go (~27,231 lines across 20 files), server.go (4,460), saas_provision.go, cluster_app_key.go, hub_generations*.go, oauth.go, journey/enrollment/clusters registry, etc.
  2. Spoke-side client helpers — heartbeat push (StartHeartbeat, LastHeartbeatAttempt/Success, HeartbeatEnabled as package-level atomics), SSO verification (SpokeSSOPublicKeys, VerifySSOTokenAcrossKeys), terminal assertions (MintTerminalAssertion, TerminalSigningKey), SpokeInviteKey, self-upgrade (UpgradeSelfToSHA, RolloutRestartSelf), fd gauge, cluster-health collection.

The spoke consumers (pkg/dashboard/{api,api_contribute,server,session}.go, cmd/hive/{hubwire,configwire,hookwire,main*}.go) use ~60 spoke-side symbols, yet importing them compiles the entire control plane into every spoke build and couples both roles' internal state.

Much of the shared state is package-level mutable globals (heartbeat atomics, contribute queue/fleet snapshot, signing keys), so the boundary between "what the spoke may call" and "what only the hub process may touch" exists only by convention — nothing structural stops spoke code from reaching into control-plane state (e.g. pkg/dashboard tests already drive hub.ReadyQueue, hub.FleetSnapshot, hub.SetHeartbeatStateForTest).

Impact

Recommendation

Phase the split along the deployment-role seam, keeping cmd/hive's dual-mode binary intact:

  1. Extract spoke-facing client code into pkg/hub/spoke (or pkg/spokeclient): heartbeat push + status, task-status push, SSO verify, terminal assertion mint/verify, invite key, self-upgrade. This is mostly mechanical (move + import rewrite) since these files barely reference saas internals.
  2. Move wire/DTO types shared by both sides (HeartbeatPayload, TaskStatusPayload, AgentSummary, LeaderboardEntry, *Wire types) into pkg/hub/wire with no behavior.
  3. Leave the control plane in pkg/hub (or pkg/hub/saas); its only inward dependency becomes the wire package.

After the split, go list makes the spoke↔hub contract explicit and reviewable, and the compiler enforces it.

Coordination: open PRs #6046 (v4→v5 sync: cmd/hive/hubwire.go, main_helpers.go) and #6047#6049 (v4: pkg/hub heartbeat/health/saas) occupy these files — this refactor should wait until they land.


Filed by architect agent (ACMM L5 — hold-gated mode)

🐝 Hive Agent: architect | Instance: hosted-available-oke-11-placeholder-r05x | SHA: unknown

— hive: agent=architect backend=copilot model=claude-opus-4-6

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/architectCreated or modified by the architect agentarchitectureApproved by a Hive merger/owner for auto-merge on green CIhelp wantedDenotes an issue that needs help from a contributor. Must meet "help wanted" guidelines.hive/hosted-available-oke-11-placeholder-r05xApproved by a Hive merger/owner for auto-merge on green CItech-debtApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions