feat(#3255): hives stop owning the knowledge webhook, and clean up their own
A webhook has exactly one target URL, so every hive registering one against the shared internal/knowledge repository was last-writer-wins rather than idempotent: all but the most recent silently stopped receiving deliveries. The swarm controller holds the single registration and now addresses an event to each hive over the queue instead. This is a migration, not a deletion. Not registering any more fixes nothing on a hive that has already run — the hook it created persists on the forge, so the contention would survive on exactly the deployments that have it while fresh installs looked fixed. The hive that created a hook removes it. It removes only its OWN, matched on the full URL rather than the /webhook/knowledge suffix. A hook with that suffix and a different base belongs to another hive, possibly one not yet upgraded, and deleting it would break that hive's knowledge sync until it caught up. Reaping a neighbour's registration is the behaviour being removed here; doing it while fixing it would only invert the direction. The predecessor did reap by suffix, to clear loopback hooks left by an older single-hive layout. That was safe when a hive was alone on its forge and is not safe now. The hive-side registrars also acted as reapers of hooks under their own path, which is why the swarm hook lives under /webhook/forge/; removing this registrar removes that reaper too. Intended, and stated because no reviewer would infer it from the diff. The receive endpoint goes with it. A live HMAC-verified /webhook/knowledge that nothing can legitimately reach would tell the next reader that this is how a hive learns about knowledge changes. Docs move in the same commit: docs/swarm/README.md said two hooks exist per swarm-wide repo and neither should be deleted, which is now true for agent-configs and wrong for internal/knowledge — a half-correct description being worse than an uncorrected one.
This commit is contained in:
parent
89050ef34b
commit
d2a550e685
6 changed files with 111 additions and 218 deletions
|
|
@ -38,18 +38,23 @@ create a new document.
|
|||
hive-c0re maintains the local clone at
|
||||
`/var/lib/hyperhive/knowledge` via two paths:
|
||||
|
||||
1. **Forgejo push webhook** — `ensure_webhook` registers a push
|
||||
hook on `internal/knowledge` at startup pointing at
|
||||
`https://<hive_domain>/webhook/knowledge` (routed through the
|
||||
gateway, avoiding the Forgejo SSRF guard that blocks loopback
|
||||
delivery). On any push to main (including merge commits) hive-c0re
|
||||
runs `git pull` so agents see the new content on their next turn.
|
||||
The endpoint is protected by an auto-generated HMAC secret that
|
||||
hive-c0re verifies on every delivery.
|
||||
1. **Swarm event** — the swarm controller holds the single push hook on
|
||||
`internal/knowledge` (see `docs/swarm/README.md` § Swarm-wide forge
|
||||
webhooks). On any push to main, including merge commits, it sends an
|
||||
event to every hive over the swarm queue and each hive runs `git
|
||||
pull`, so agents see the new content on their next turn.
|
||||
|
||||
On a swarm, a **second** hook on the same repo points at the
|
||||
swarm controller — see `docs/swarm/README.md` § Swarm-wide forge
|
||||
webhooks. Both are expected; neither should be deleted.
|
||||
A hive that is offline when the event is sent does not get it on
|
||||
reconnect — the periodic pull below is what closes that gap. So one
|
||||
hive briefly showing older `/knowledge` content than another is
|
||||
expected, and resolves by itself within the fallback interval.
|
||||
|
||||
**Do not add a per-hive hook.** A webhook has exactly one target
|
||||
URL, so a second registration against the same repo does not add a
|
||||
recipient — it takes delivery away from whoever registered first.
|
||||
Earlier versions had each hive register its own; hive-c0re now
|
||||
removes its own leftover at startup, so no operator step is needed
|
||||
to migrate.
|
||||
|
||||
2. **Periodic pull** — a background task in `hive-c0re::main`
|
||||
pulls on a fixed cadence as a fallback (webhook missed, c0re
|
||||
|
|
|
|||
|
|
@ -356,18 +356,23 @@ itself — a `push` hook on `internal/knowledge` and a `pull_request` hook
|
|||
on the `agent-configs` org, both under
|
||||
`https://<swarm.ui.domain>/webhook/forge/`.
|
||||
|
||||
**Two hooks exist per swarm-wide repo: each hive's own, plus the
|
||||
controller's.** Both are expected — **do not delete either.** Removing a
|
||||
hive's hook stops that hive acting on knowledge pushes and config PRs;
|
||||
removing the controller's just gets recreated on its next start.
|
||||
|
||||
The controller **interprets** a delivery and sends hives a specific
|
||||
message — *the knowledge repo changed*, *deploy agent `foo` at rev
|
||||
`abc123`* — rather than forwarding forge payloads for each hive to
|
||||
re-derive. Approval happens once, at the swarm level: a hive receives a
|
||||
decision, not an event to adjudicate. Today the controller logs each
|
||||
verified delivery and sends nothing, because the swarm→hive channel does
|
||||
not exist yet; the hive-side hooks are what act in the meantime.
|
||||
decision, not an event to adjudicate.
|
||||
|
||||
**`internal/knowledge` is on that path.** The controller's is the only
|
||||
hook on it: hives no longer register their own, and each removes its
|
||||
leftover at startup. A webhook has exactly one target URL, so per-hive
|
||||
registration never added a recipient — it took delivery away from
|
||||
whichever hive registered before it.
|
||||
|
||||
**The `agent-configs` org is not yet.** Each hive still registers its own
|
||||
`pull_request` hook there, so that repo has two — the hive's and the
|
||||
controller's — and **both are expected; do not delete either.** Removing
|
||||
a hive's stops it acting on config PRs; removing the controller's just
|
||||
gets recreated on its next start.
|
||||
|
||||
Nothing to configure. The hooks are registered only when this host also
|
||||
serves the swarm UI vhost — that is what publishes the endpoint, and a
|
||||
|
|
|
|||
Loading…
Reference in a new issue