feat(orb): add the opt-in federated collector transport client (push/pull) - #6537
Merged
loopover-orb[bot] merged 1 commit intoJul 16, 2026
Conversation
…pull) Adds the transport side of federated fleet intelligence (JSONbored#1970): pushFederatedBundle POSTs this instance's own anonymized bundle to the collector an operator configures, and pullPeerBundles GETs peer bundles from it. Extends the existing .loopover.yml federatedIntelligence: block (JSONbored#6478) with collectorUrl + collectorMode rather than adding a second toggle. There is deliberately NO default collector and no auto-discovery: with no collectorUrl configured, enabled: true still sends nothing, because this codebase's self-host posture assumes no central service exists. collectorUrl is validated at config-read time against the same isSafeHttpUrl SSRF guard every other URL-valued manifest field uses, and re-checked at call time (the src/orb/relay.ts idiom) so a snapshot round-tripped through KV cannot smuggle an unsafe endpoint past it. Export side only for the payload: pulled bundles are shape-checked and returned, never signature-verified, never trust-gated and never persisted. That is JSONbored#6480, which is blocked on JSONbored#6477 -- verifying here would not just be out of scope, it would be wrong, because there is no trust anchor to verify against yet. Fail-safe throughout: every entry point resolves the opt-in before touching the DB or the network, and the whole body is wrapped so nothing here can throw into the gate. 5xx/network retries use jittered exponential backoff; a 4xx is not retried. Rate limiting and backoff reuse the engine's existing pure helpers (evaluateLocalRateLimit, jitteredBackoffMs) rather than inventing a scheme. Closes JSONbored#6479
Contributor
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #6537 +/- ##
=======================================
Coverage 95.57% 95.57%
=======================================
Files 590 591 +1
Lines 47170 47235 +65
Branches 15007 15034 +27
=======================================
+ Hits 45081 45146 +65
Misses 1297 1297
Partials 792 792
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Contributor
|
Important 🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪🟪 🔍 LoopOver is reviewing…AI analysis is in progress. This comment will update when the review is complete. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed · 🟪 Reviewing |
Closed
4 tasks
This was referenced Jul 16, 2026
Closed
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.
Closes #6479. Transport side of federated fleet intelligence (#1970), following the export path merged in #6505 (#6478).
pushFederatedBundlePOSTs this instance's own anonymized bundle to the collector an operator configures;pullPeerBundlesGETs peer bundles from it. Both are best-effort and off by default.Scope: transport only — and the import side is deliberately absent
A pulled bundle is fetched, shape-checked, and returned. It is never signature-verified, never trust-gated, never persisted. That is #6480, which is blocked on #6477 (the key-trust/anti-poisoning design).
Verifying here would not merely be out of scope — it would be wrong: there is no trust anchor to verify against yet, and inventing one is exactly what #6477 exists to decide (
src/orb/federated-bundle.ts:170-174). SopullPeerBundlesreturnsFederatedSignalBundle[]for a future consumer, mirroring the pure-library shape #6505 already landed.No default collector, by design
The client only ever talks to an endpoint the operator configured. No hardcoded fallback, no auto-discovery — this codebase's self-host posture assumes no central/managed collector exists. With
enabled: truebut nocollectorUrl, nothing is sent, because there is nowhere to send it. (Contrast the #1255 orb path, which POSTs to a hosted default atorb-collector.ts:168— a different feature with a different contract.)Config: extends the existing block, does not add a second one
The issue asks for the
inherit | off | enabledconvention — the existing{present, enabled}shape already encodes exactly that (present: false= inherit), so this extendsfederatedIntelligence:rather than duplicating the toggle.collectorUrlis SSRF-guarded twice:parseVisualProductionUrl, so anhttp://,localhost,127.0.0.1,10.x,192.168.x, or*.internalendpoint is dropped with a warning and can never reach the client.resolveCollectorEndpoint(thesrc/orb/relay.ts:230idiom), so a snapshot round-tripped through the KV cache cannot smuggle an unsafe URL past a guard it was never parsed by.Reuse over invention
evaluateLocalRateLimitandjitteredBackoffMs(packages/loopover-engine/src/governor/rate-limit.ts, exported atindex.ts:156). Both take injected randomness, so retries are deterministic in tests. No new scheme, and the bucket stays caller-owned so this module remains state-free likefederated-bundle.ts.FederatedSignalBundleis imported, never redefined — per the issue's "do not invent a second bundle format".fetchFninjection +AbortSignal.timeoutfolloworb-collector.ts:155/215.Fail-safe
Every entry point resolves the opt-in before touching the DB or the network, and the whole body is wrapped so nothing here can throw into the gate. The gate never awaits this; it is background best-effort sync. A collector that is unreachable, slow, rate-limited, or returning garbage is indistinguishable from "no peers yet" to every caller.
Verification
typecheck,docs:drift-check,engine-parity:drift-check,manifest:drift-check,command-reference:check,selfhost:env-reference:check,ui:openapi:check,db:schema-drift:check— all pass. Example configs byte-identical across both templates (config-templates 24/24).federated-collector.test.ts).src/orb/federated-collector.ts72/72 statements, 64/64 branches (100%); every added line in the enginefocus-manifest.tscovered. Covered arms include: opted-out (null/absent/disabled/no-URL/wrong-mode) proving zero network calls via a throwing Proxyfetch; push/pull happy paths; 5xx retried to exhaustion with backoff asserted; 4xx not retried; timeout; non-JSON body; non-array payload; unknown schemaVersion / missing signature dropped; rate-limit exhausted ⇒ zero fetches; every SSRF-guard rejection arm; and a throwing injected dependency still degrading rather than throwing.