Skip to content

Commit bda83ca

Browse files
kai392RealDiligentclaudeloopover-orb[bot]
authored
fix(orb): correct stale "#6480 blocked on #6477" comments — federated import shipped (#8957)
federated-bundle.ts and federated-collector.ts described the federated-import trust mechanism (#6480) as still blocked on #6477, but src/orb/federated-import.ts fully implements it: an operator-supplied `federatedIntelligence.peerKeys` allowlist (fail-closed, no PKI/reputation), exactly per #6477's ratified key-trust design, and federated-benchmark.ts already lists all three stages as shipped. Update the comments to describe #6480's shipped state; the collector's scope boundary (verification lives on the import side, not the transport client) is preserved. Closes #8878 Co-authored-by: RealDiligent <brave.challenge007@gmail.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: loopover-orb[bot] <296761690+loopover-orb[bot]@users.noreply.github.com>
1 parent fcecf0c commit bda83ca

2 files changed

Lines changed: 14 additions & 11 deletions

File tree

src/orb/federated-bundle.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,12 @@ export function canonicalizeFederatedBundleBody(body: FederatedSignalBundleBody)
162162
/**
163163
* HMAC-sign a bundle body so a receiving instance can verify it was not tampered with in transit.
164164
*
165-
* TODO(#6477): the KEY-TRUST scheme (how a peer establishes/rotates the key it verifies against) is #6477's
166-
* design decision and is deliberately NOT invented here. Until it lands, the signing key is this instance's
167-
* existing dedicated anonymization secret (getOrCreateAnonSecret) as a placeholder: it makes the bundle
168-
* tamper-evident to anyone who already holds the key, but it does NOT yet establish peer trust. #6480 (the
169-
* import side) is explicitly blocked on #6477 for exactly that reason.
165+
* KEY-TRUST (#6477, ratified as an allowlist design — NOT a PKI/reputation system): the signing key is this
166+
* instance's dedicated anonymization secret (getOrCreateAnonSecret), which makes the bundle tamper-evident to
167+
* anyone holding that key. Peer trust itself is established on the RECEIVING side, where #6480 has SHIPPED
168+
* (src/orb/federated-import.ts): an importing operator explicitly adds a peer's verification key to
169+
* `federatedIntelligence.peerKeys` (fail-closed when unset), and only an allowlisted key can verify a bundle.
170+
* So this signature is the tamper-evidence layer; the receiver's allowlist is what turns it into peer trust.
170171
*/
171172
export function signFederatedBundle(body: FederatedSignalBundleBody, key: string): string {
172173
return createHmac("sha256", key).update(canonicalizeFederatedBundleBody(body)).digest("hex");

src/orb/federated-collector.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
// pull — GET peer bundles from it.
77
//
88
// SCOPE — deliberately NOT the import side. A pulled bundle is fetched, shape-checked and RETURNED; it is
9-
// never signature-verified, never trust-gated, and never persisted. That is #6480's job, and #6480 is blocked
10-
// on #6477 (the key-trust/anti-poisoning design). Verifying here would not merely be out of scope, it would be
11-
// WRONG: there is no trust anchor to verify against yet, and inventing one is exactly what #6477 exists to
12-
// prevent (see the TODO(#6477) note on signFederatedBundle in ./federated-bundle.ts).
9+
// never signature-verified, never trust-gated, and never persisted. That is #6480's job, which SHIPPED
10+
// separately in ./federated-import.ts: it trust-gates each bundle against the operator's
11+
// `federatedIntelligence.peerKeys` allowlist per #6477's ratified key-trust/anti-poisoning design. Verifying
12+
// HERE would still be wrong — the trust anchor lives with the importer that decides what to fold in, not with
13+
// the transport client that only fetches (see the KEY-TRUST note on signFederatedBundle in ./federated-bundle.ts).
1314
//
1415
// NO DEFAULT COLLECTOR, BY DESIGN. The client only ever talks to an endpoint the operator configured in
1516
// `.loopover.yml`. There is no hardcoded fallback and no auto-discovery — this codebase's self-host posture
@@ -164,8 +165,9 @@ export async function pushFederatedBundle(manifest: ManifestSlice, db: D1Databas
164165
*
165166
* Returns [] — having touched nothing — unless the operator opted in AND configured a pull-armed collector.
166167
* Bundles are shape-checked and returned; unrecognized entries are dropped. They are deliberately NOT
167-
* signature-verified or trust-gated — that is #6480, blocked on #6477. Returns [] rather than throwing on any
168-
* failure, so an unreachable or hostile collector is indistinguishable from "no peers yet" to every caller.
168+
* signature-verified or trust-gated — that is #6480's job, shipped in ./federated-import.ts (the peerKeys
169+
* allowlist). Returns [] rather than throwing on any failure, so an unreachable or hostile collector is
170+
* indistinguishable from "no peers yet" to every caller.
169171
*/
170172
export async function pullPeerBundles(manifest: ManifestSlice, opts: CollectorOpts = {}): Promise<FederatedSignalBundle[]> {
171173
const endpoint = resolveCollectorEndpoint(manifest, "pull");

0 commit comments

Comments
 (0)