feat(orb): trust-gate inbound federated bundles per the #6477 key-trust design - #6649
Conversation
…key-trust design (JSONbored#6480) Add the receiving side of federated fleet intelligence: verify a pulled peer bundle's HMAC against the operator's explicit peerKeys allowlist before it can be folded into local calibration or the peer-median benchmark, and log every rejection with the rule that stopped it. Implements JSONbored#6477's decision exactly: explicit operator-configured allowlist (no auto-discovery, no PKI) plus median-not-mean aggregation, which the existing fleet analytics already provides. No reputation/decay/scoring system, which that design pass considered and deliberately turned down. Closes JSONbored#6480
…ONbored#6480) vitest does not typecheck, so both fixtures passed at runtime while tsc failed on the new required field. Each notes why its own side never reads peerKeys. Also cover defaultRejectionLogger's unknown-handle fallback, the last partial branch in the new module.
|
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 #6649 +/- ##
=======================================
Coverage 93.64% 93.65%
=======================================
Files 680 681 +1
Lines 68026 68075 +49
Branches 18673 18686 +13
=======================================
+ Hits 63704 63753 +49
Misses 3347 3347
Partials 975 975
Flags with carried forward coverage won't be shown. Click here to find out more.
|
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-16 17:24:43 UTC
Review summary Nits — 5 non-blocking
Decision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🧪 Chat with LoopOverAsk LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.
Full command reference: https://loopover.ai/docs/loopover-commands 🧪 Experimental — new and may change. 🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.
|
Summary
The receiving side of federated fleet intelligence.
pullPeerBundles(#6479) already fetches peer bundles, but nothing consumed its return value — there was no code path that decided whether a bundle could be trusted, which is exactly the gap #6477 had to close first. This adds that gate: a pulled bundle is verified and trust-checked before it may be folded into local calibration or the peer-median benchmark (#6481), and every rejection is logged with the rule that stopped it.New
src/orb/federated-import.ts, plus the one config field the design requires.This implements #6477's decision — it does not redesign it
That design pass settled the mechanism deliberately, and this PR follows it literally. Its two poisoning-resistance layers, and where each actually lives:
federatedIntelligence.peerKeysis ever considered. Enforced here. This is what makes Sybil self-limiting by construction: forging peers requires the receiving operator to add the attacker's keys themselves.src/orb/analytics.ts:92), so the layer holds by construction. Re-implementing it would fork the exact definition Dashboard benchmark view: gate precision vs peer median for federated fleet intelligence #6481's comparison depends on, whichfederated-bundle.ts:24-27explicitly warns against.#6477 rejected building a reputation/decay/scoring system, so there is deliberately no per-peer score, no anomaly heuristic, and no retroactive poisoned-bundle detection: an operator who finds a bad peer removes its key. Those absences are the design, not gaps.
Three decisions worth reviewing
instanceId. The HMAC is detached and carries no key hint.instanceIdis attacker-controlled and unauthenticated until a key verifies, so using it to pick a verifier would let a bundle choose who checks it. The loop is timing-safe (timingSafeEqualHex) and deliberately does not early-exit on a match, so total work doesn't reveal which key matched.untrusted_or_tamperedis one reason, not two. With a detached HMAC the receiver genuinely cannot distinguish "peer not allowlisted" from "body tampered" — both are just "no allowlisted key reproduces this signature". Splitting them would report a distinction this scheme cannot make.enabled: truealone never imports. Import requires opt-in and a non-empty allowlist (isFederatedImportEnabled), kept separate from the export'sisFederatedIntelligenceEnabled. An operator who turns on the export does not silently start ingesting peer data; an empty allowlist rejects everything (fail closed), mirroringMCP_READ_REPO_ALLOWLIST's posture.Config
One new field,
federatedIntelligence.peerKeys(list of 64-hex keys, default[]). Validated at parse time to the shapesignFederatedBundle's HMAC key actually has, so an operator can only allowlist something that could really verify a bundle. A malformed entry is dropped with a warning rather than throwing, matching every sibling list field — safe in this direction because dropping a key can only ever remove a peer from consideration, never admit an untrusted one. De-dup is case-insensitive, first occurrence winning (normalizeAutoCloseExemptLogins's shape).No DB/OpenAPI wiring, deliberately — and this is the part worth checking rather than assuming.
federatedIntelligencehas no DB-backed counterpart (focus-manifest.ts:465-466: "MirrorsupstreamDriftIssues:exactly: no DB-backed counterpart, so the parsed value … IS the effective value"), so the config-as-code parity chain forRepositorySettingsfields doesn't apply here.docs:drift-checkagrees: 93 FocusManifest fields all documented.A warning never echoes a rejected key, and the rejection log carries only the reason plus the opaque instance handle — never bundle contents, never key material. Both are pinned by tests.
Validation
federated-import.ts44/44 statements, 41/41 branches; all 43 changed lines infocus-manifest.tscovered with zero partial branches. Clears the 99%codecov/patchwall onsrc/**+packages/loopover-engine/src/**with margin.federated-import,focus-manifest,federated-bundle,federated-collector.node --test— 588/588 pass.npm run typecheck— 0 errors. Two existing fixtures built the config literal without the new field; vitest doesn't typecheck, so they passed at runtime whiletscfailed. Both fixed (the export and the transport each note why they don't readpeerKeys).docs:drift-checkok ·selfhost:config-lintok ·eslint0 errors/0 warnings on the new files ·git diff --checkclean · rebased on latestmain, no base conflict.Tests
Both sides of every branch, including the ones easy to miss:
null(an instance underMIN_DECIDED) still accepted.schemaVersion; wrong-typed signed field; opted-out instance; opted-in with an empty allowlist.canonicalizeFederatedBundleBody, so an export-side canonicalization change breaks these rather than passing against a local re-statement.unknownrather thannull.Scope
src/,packages/,test/).federated-bundle.ts(export) andfederated-collector.ts(transport) are unmodified — only their test fixtures gained the new field.site/,CNAME, orlovablechanges. The example keys in tests/docs are locally-invented fixtures ("a".repeat(64), all-zeros), not real material.Safety
peerKeys⇒ empty ⇒ every inbound bundle rejected, so behavior is unchanged for every existing operator.Closes #6480