Skip to content

[v5] RFC: constellation — what a hub needs to coordinate several spokes as one fleet #5691

Description

@hanthor

Summary

A single hive serving a diverse org makes its agents compete over unrelated
work. Splitting into several subject-scoped hives fixes that, but the moment
you do, you discover the hub is a directory, not a coordinator — and
several things that ought to be fleet-wide are per-hive by accident rather than
by design.

This RFC reports what actually broke splitting one 43-repo hive into two, and
proposes what a constellation — several spokes under one hub, acting as one
system — would need.

We are running this now: a self-hosted hub, two spokes, shared credentials.
Everything below is measured, not speculative.

What we built

                    hub.tunaos.org        (HIVE_MODE=hub, registry)
                          |
        +-----------------+-----------------+
        |                                   |
  hive.tunaos.org                     reef.tunaos.org
  25 repos · ACMM L6                  18 repos · ACMM L5
  OS, images, installers,             desktop apps, app
  packaging                           distribution, docs
        |                                   |
        +--------- shared -------------------+
          GitHub App installation
          agent OAuth credentials

Spokes register themselves by heartbeat once they have HIVE_HUB_URL and the
hub's HIVE_HUB_SECRET. That part works exactly as documented.

What the hub does NOT coordinate

Each of these is fleet-wide in reality but per-hive in the implementation.

1. Provider quota

Every spoke probes and consumes provider quota independently, with no shared
view. Two spokes on one Anthropic subscription each believe they have the whole
weekly cap; the first to notice exhaustion is the one that hits it.

This is the single biggest constraint on splitting. A split creates zero
additional quota
— it divides the same pool among more agents. Ours:
DeepSeek dry, OpenAI capped for four days, Anthropic near its weekly cap,
Google carrying the fleet and hitting its own 5-hour window.

Constellation need: headroom as hub state, so a spoke can ask "what is left
for the fleet" rather than "what is left as far as I can see".

2. GitHub App rate limit

Both spokes use one App installation and therefore share its 7100 req/hr.
Bringing up the second spoke exhausted it during initial enumeration:

403 API rate limit exceeded for installation ID 137498420 [rate reset in 19m22s]
... all 18 repos failed to enumerate

Nothing coordinates this. Each spoke polls its own repos on its own schedule
and discovers the shared ceiling by hitting it.

Constellation need: either a documented per-spoke budget, or hub-level
awareness so spokes back off cooperatively instead of racing.

3. Agent credentials

Each hive pod has its own /data/home, so by default every spoke needs its
own interactive claude /login
— and those sessions rotate refresh tokens
against each other. We saw exactly that failure earlier: a copied credential
was zeroed when the other session refreshed, and Claude Code silently fell back
to metered API billing.

We solved it by mounting one credential directory into both spokes, which works
because a single hive already shares ~/.claude across its 11 agents by
symlink. Extending that across hives is the same model.

But it needs both spokes on one node (RWO binds to a node), and PVCs are
namespaced — we needed a second PV over the same hostPath to cross the
namespace boundary. That is a workaround, not a design.

Constellation need: a first-class notion of fleet-shared credentials.

4. Repo ownership

Nothing prevents two spokes managing the same repo, which would put two fleets
on the same backlog opening competing PRs — the exact thing a split exists to
prevent.

Our org watcher nearly caused this: it read one spoke's repo list, could not
read the other's (no owner session), concluded 18 repos were unmanaged, and was
one step from adding them to the first hive.

Constellation need: the hub should know which spoke owns a repo, and refuse
or warn on overlap. The registry already receives each spoke's repo list on
every heartbeat — the data is there, unused.

5. Routing new work

When a repo appears in the org, something must decide which spoke gets it. We
do this by asking an AI with each hive's charter in the prompt, falling back
across backends when one is rate-limited, and leaving a repo unassigned
rather than guessing — a repo in the wrong hive is worse than one nobody has
picked up.

That is spoke-side today and duplicated per operator. It is a natural hub
responsibility: the hub already knows every spoke and its charter.

Proposal

Sequenced smallest-first; each step is useful alone.

1. Overlap detection (cheap, high value). The hub already receives repo
lists via heartbeat. Flag a repo claimed by more than one spoke in
/api/registry and the hub UI. Pure read-side; no new protocol.

2. Spoke charters in the registry. Let a spoke advertise a one-line scope
alongside its repo list. Makes the registry self-describing and gives any
router — AI or human — the data it needs.

3. Fleet headroom. Let spokes report provider headroom on the heartbeat
they already send, and expose the aggregate. Read-only first; a spoke can
decide what to do with the number.

4. Shared-credential guidance. Document the one-directory-many-readers
model, with the node/namespace constraints spelled out. Ideally a supported
config field rather than a hostPath trick.

5. Hub-side routing (optional). Only once 1–3 exist. POST /api/route
taking a repo and returning a spoke, so every operator does not reimplement it.

Deliberately not proposed: the hub scheduling work onto spokes, or holding
credentials. It is a directory; making it a control plane changes its failure
model from "registry is stale" to "fleet is down".

Open questions

  1. Is per-spoke GitHub App installation the intended answer to the rate-limit
    ceiling? It solves it cleanly but multiplies App management.
  2. Should overlap be an error or a warning? Legitimate cases may exist (a docs
    repo two subject areas both touch).
  3. Does hub-side routing belong here at all, or in a separate tool that reads
    the registry?
  4. Is there appetite for spokes reporting quota to the hub, given it is
    operator-sensitive data?

Environment

ghcr.io/kubestellar/hive-hub:stable + two hive:stable spokes on a 2-node
Talos cluster; ACMM L6 and L5; one GitHub App installation; shared Claude Max
and Antigravity credentials. Related: #5632, #5633, #5661, #5690.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions