From 1d8e8b1822331124f18c849aba62e31a9c47b027 Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Thu, 23 Jul 2026 00:33:27 -0700 Subject: [PATCH] docs(discovery-plane): finalize operator guide now that the plane is shipped and live #7164/#7166/#7167 all closed and the discovery-index server is now deployed at discovery.loopover.ai (Cloudflare Container + Worker). Rewrites the operator guide (packages/loopover-miner + the mirrored docs-site mdx) from "provisional/TBD" to the real, shipped opt-in mechanism: - Documents the real env vars, including LOOPOVER_MINER_DISCOVERY_SHARED_SECRET (used for bearer auth against the hosted index), which wasn't in the original TBD list at all. - Adds the maintainer-run instance's real URL and a verification step (dry-run discover) to the opt-in checklist. - Corrects a real invariant-list drift found while verifying: the doc claimed telemetry uses HMAC-hashed repo/issue correlation identifiers, but the shipped recordDiscoveryTelemetry only ever emits a bare {event, outcome} local log line -- no hosted telemetry collector exists yet, so there's nothing to hash today. - Marks server-side soft-claim dedup shipped (was "server-side dedup is #4250 [open]"). Closes #7169. --- .../content/docs/ams-discovery-plane.mdx | 100 ++++++++++-------- .../docs/discovery-plane-operator-guide.md | 69 ++++++------ 2 files changed, 92 insertions(+), 77 deletions(-) diff --git a/apps/loopover-ui/content/docs/ams-discovery-plane.mdx b/apps/loopover-ui/content/docs/ams-discovery-plane.mdx index 5cc012716c..999feb21cf 100644 --- a/apps/loopover-ui/content/docs/ams-discovery-plane.mdx +++ b/apps/loopover-ui/content/docs/ams-discovery-plane.mdx @@ -7,12 +7,11 @@ Operator-facing guide for the **optional** hosted discovery-index plane. This is half of that roadmap item: how a `loopover-miner` instance opts in, what it may send, and what never leaves the operator's machine. - - The request/response **contract shape**, the telemetry event schema, and the client soft-claim - request builder have now **shipped** as real, tested modules (see below). What remains - **provisional** is only the operator-facing **opt-in wiring** — the env var names further below - are still TBD pending the hosted server and the miner-side opt-in. Do not treat those env var - names as stable API yet. + + The request/response contract, the client + (`packages/loopover-miner/lib/discovery-index-client.ts`), and the hosted server + (`packages/discovery-index`, deployed at `discovery.loopover.ai`) are all real and running. The + walkthrough below reflects the actual opt-in mechanism, not a plan. @@ -64,9 +63,9 @@ defaults**: /> - Do **not** copy Orb's wording for this plane. Orb's header comment is explicit: "Export is - ALWAYS ON… there is no opt-out flag" aside from `ORB_AIR_GAP`. The discovery plane is the - opposite: **no hosted traffic unless the operator turns it on.** + Do **not** copy Orb's wording for this plane. Orb's header comment is explicit: "Export is ALWAYS + ON… there is no opt-out flag" aside from `ORB_AIR_GAP`. The discovery plane is the opposite: **no + hosted traffic unless the operator turns it on.** ## What the plane is for @@ -79,57 +78,67 @@ The plane: - Serves **public GitHub metadata only** (issue titles, labels, counts, timestamps, URLs — the same class of fields Phase 1 already uses locally). -- May coordinate **soft claims** across the fleet (server-side dedup is still open; the client - request shape has shipped). +- May coordinate **soft claims** across the fleet, server-side dedup included. - Never receives source trees, diffs, tokens, or write credentials. Local discovery (`opportunity-fanout` + `opportunity-ranker`) continues to work with **zero** hosted configuration. -## Opt-in mechanism (env var names still TBD) +## Opt-in mechanism -The request/response contract has now shipped (see the scope table above), but the -operator-facing opt-in env vars below are **not implemented yet** — treat them as -**documentation placeholders** for the shape operators should expect once the opt-in wiring -lands: +Real, shipped env vars, read in `discovery-index-client.ts`: on every hosted-index request. The maintainer-run instance at discovery.loopover.ai requires one -- ask the operator running it for a value.", + }, + { + title: "LOOPOVER_MINER_DISCOVERY_TELEMETRY", + description: + "Default unset / false. Separate opt-in for operational telemetry. Plane queries can stay on while telemetry stays off. Currently local-only -- see Telemetry below.", }, ]} /> -**Truthy-string convention** (when implemented): `/^(1|true|yes|on)$/i`, matching other -`LOOPOVER_*` flags in this repo. +**Truthy-string convention:** `/^(1|true|yes|on)$/i`, matching other `LOOPOVER_*` flags in this +repo. **Operator checklist (enabled plane):** -**1.** Set `LOOPOVER_MINER_DISCOVERY_PLANE=true` (exact name may change). +**1.** Set `LOOPOVER_MINER_DISCOVERY_PLANE=true`. + +**2.** Set `LOOPOVER_MINER_DISCOVERY_INDEX_URL=https://discovery.loopover.ai` (or your own +deployment's URL). -**2.** Set `LOOPOVER_MINER_DISCOVERY_INDEX_URL` to the operator-trusted index endpoint. +**3.** Set `LOOPOVER_MINER_DISCOVERY_SHARED_SECRET` if the index you're pointing at requires one. -**3.** Optionally set `LOOPOVER_MINER_DISCOVERY_TELEMETRY=true` if you want anonymized -operational events for the hosted service — not required for index queries. +**4.** Optionally set `LOOPOVER_MINER_DISCOVERY_TELEMETRY=true` for local operational log lines +about plane usage — not required for index queries. -**4.** Keep `GITHUB_TOKEN` (or equivalent) on the instance only; never configure tokens intended +**5.** Keep `GITHUB_TOKEN` (or equivalent) on the instance only; never configure tokens intended for the hosted plane to receive. -With opt-in off (default), behavior is byte-identical to today: local SQLite ledgers, local -fan-out, no hosted calls. +**6.** Run `loopover-miner discover --dry-run --json` and check the response for +hosted-index candidates alongside your local fan-out to confirm the plane is actually being +queried. + +Every call in the client is fail-open: a network error, timeout, or non-2xx response degrades +silently to "no supplement" rather than failing the miner's own discover/attempt work. With +opt-in off (default), behavior is byte-identical to today: local SQLite ledgers, local fan-out, +no hosted calls. ## Contrast with local soft-claims today @@ -158,17 +167,21 @@ operators: telemetry payloads. - **No compensation signals in the plane** — raw reward values, wallet addresses, hotkeys, trust scores, or private rankings never cross this boundary. -- **Telemetry is a second opt-in** — even with the plane enabled, anonymized telemetry remains - separately gated. -- **Anonymized identifiers only** — when telemetry ships, repo/issue correlation uses HMAC-hashed - identifiers keyed by a **per-instance dedicated secret** the collector never holds (same - posture as `getOrCreateAnonSecret` / `hmacField` in `src/selfhost/orb-collector.ts` — key - separation from GitHub App / webhook secrets). -- **Low-cardinality reason buckets** — any free-text-adjacent telemetry fields use bucketed - categories (Orb's `bucketReasonCode` pattern), not raw maintainer or model prose. +- **Telemetry is a second opt-in** — even with the plane enabled, telemetry remains separately + gated behind `LOOPOVER_MINER_DISCOVERY_TELEMETRY`. - **Core miner still works offline** — claims, plans, queues, and local ledgers do not require the hosted plane; `loopover-miner doctor` / `status` remain no-network commands. +### Telemetry: local today, not yet sent to the hosted service + +`recordDiscoveryTelemetry` (`discovery-index-client.ts`) currently emits a structured **local log +line** (`event`, `outcome` — both fixed, low-cardinality strings; no repo/issue identifiers, no +free text) via the miner's own logger. No hosted telemetry-collector endpoint exists yet, so +nothing about telemetry events leaves the operator's machine today regardless of this flag. The +originally planned design called for HMAC-hashed repo/issue correlation identifiers once a real +collector ships — that collector doesn't exist yet, so there is nothing to hash today; this +section will be updated again once it does. + ### Never included (client → hosted plane) Inventory style matches `src/selfhost/orb-collector.ts` ("No diffs, no code…") adapted for @@ -184,8 +197,9 @@ discovery-plane domain: ### Never retained by the hosted service (server-side) -The server operating doc (maintainer-only) will restate the same boundary: **never holds source -or actor-capable credentials.** This client guide does not define server retention policy. +The server (`packages/discovery-index`) never holds source or actor-capable credentials — see +that package's own README and operations docs for its full boundary and incident-response +documentation. This client guide does not define server retention policy in detail. ## Related docs diff --git a/packages/loopover-miner/docs/discovery-plane-operator-guide.md b/packages/loopover-miner/docs/discovery-plane-operator-guide.md index d72662be34..d68338aa4e 100644 --- a/packages/loopover-miner/docs/discovery-plane-operator-guide.md +++ b/packages/loopover-miner/docs/discovery-plane-operator-guide.md @@ -6,17 +6,15 @@ Operator-facing guide for the **optional** Phase 6 hosted discovery-index plane ([#4250](https://github.com/JSONbored/loopover/issues/4250)). This is the client/miner half of that roadmap item: how a `loopover-miner` instance opts in, what it may send, and what never leaves the operator's machine. -> **Scope:** the request/response **contract shape**, the telemetry event schema, and the client soft-claim -> request builder have now **shipped** as real, tested modules (see the table). What remains **provisional** is -> only the operator-facing **opt-in wiring** — the env var names and HTTP paths further below are still TBD -> pending the hosted server and the miner-side opt-in (#4250). Do not treat those env var names as stable API yet. +> **Status: shipped and live.** The request/response contract, the client (`packages/loopover-miner/lib/discovery-index-client.ts`), and the hosted server (`packages/discovery-index`, deployed at `discovery.loopover.ai`) are all real and running. The walkthrough below reflects the actual opt-in mechanism, not a plan. > -> | Defines | Status | -> |---------|--------| +> | Defines | Status | +> | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | > | Public-data-only discovery-index API contract — `DiscoveryIndexQuery` / `DiscoveryIndexResponse` / `DiscoveryIndexCandidate` (request/response shapes) | ✅ **shipped**, stable at `DISCOVERY_INDEX_CONTRACT_VERSION` 1 — [`discovery-index-contract.ts`](../../loopover-engine/src/discovery-index-contract.ts), [`discovery-index-contract.md`](discovery-index-contract.md) ([#4300](https://github.com/JSONbored/loopover/issues/4300)) | -> | Anonymized telemetry event schema for the optional hosted plane | ✅ **shipped** — [`miner-telemetry.ts`](../../loopover-engine/src/miner-telemetry.ts) ([#4301](https://github.com/JSONbored/loopover/issues/4301)) | -> | Client-side soft-claim coordination request builder | ✅ **shipped** — [`discovery-soft-claim.ts`](../../loopover-engine/src/discovery-soft-claim.ts) ([#4302](https://github.com/JSONbored/loopover/issues/4302)) | -> | Hosted discovery-index server + the operator-facing opt-in wiring | ⏳ **still open** — [#4250](https://github.com/JSONbored/loopover/issues/4250) (the actual blocker for the opt-in mechanism below; the env var names remain TBD until it lands) | +> | Anonymized telemetry event schema for the optional hosted plane | ✅ **shipped** — [`miner-telemetry.ts`](../../loopover-engine/src/miner-telemetry.ts) ([#4301](https://github.com/JSONbored/loopover/issues/4301)) — see the Telemetry section below for what's actually wired up today vs. planned | +> | Client-side soft-claim coordination request builder | ✅ **shipped** — [`discovery-soft-claim.ts`](../../loopover-engine/src/discovery-soft-claim.ts) ([#4302](https://github.com/JSONbored/loopover/issues/4302)) | +> | Hosted discovery-index server, deployed at `discovery.loopover.ai` | ✅ **shipped and deployed** — [#7167](https://github.com/JSONbored/loopover/issues/7167) (Cloudflare Container + Worker; see `packages/discovery-index/`) | +> | Miner-side opt-in wiring (the env vars below) | ✅ **shipped** — [`discovery-index-client.ts`](../../loopover-miner/lib/discovery-index-client.ts), gated at the call site in `attempt-cli.ts` ([#7168](https://github.com/JSONbored/loopover/issues/7168)) | Part of the Miner Wave 2 discovery plane ([#2353](https://github.com/JSONbored/loopover/issues/2353) Phase 6). Distinct from Phase 1's **local-only** metadata fan-out documented in [`cross-repo-discovery-phase1.md`](cross-repo-discovery-phase1.md) — that path never phones home today. @@ -24,10 +22,10 @@ Part of the Miner Wave 2 discovery plane ([#2353](https://github.com/JSONbored/l Two telemetry/export surfaces exist in LoopOver, and they intentionally use **opposite defaults**: -| Surface | Default | Operator action | Precedent | -|---------|---------|-------------------|-----------| -| **Orb fleet calibration** (`src/selfhost/orb-collector.ts`) | **ON** once a GitHub App is configured | Opt out only via `ORB_AIR_GAP=true` (air-gapped / send-nothing) | Review-stack self-host contract — export is always on unless air-gapped | -| **Hosted discovery plane** (this guide) | **OFF** | Opt **in** explicitly before any hosted index query or plane telemetry | Hybrid, self-host-first miner deployment — participation in a shared hosted plane is never assumed | +| Surface | Default | Operator action | Precedent | +| ----------------------------------------------------------- | -------------------------------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| **Orb fleet calibration** (`src/selfhost/orb-collector.ts`) | **ON** once a GitHub App is configured | Opt out only via `ORB_AIR_GAP=true` (air-gapped / send-nothing) | Review-stack self-host contract — export is always on unless air-gapped | +| **Hosted discovery plane** (this guide) | **OFF** | Opt **in** explicitly before any hosted index query or plane telemetry | Hybrid, self-host-first miner deployment — participation in a shared hosted plane is never assumed | Do **not** copy Orb's wording for this plane. Orb's header comment is explicit: "Export is ALWAYS ON… there is no opt-out flag" aside from `ORB_AIR_GAP`. The discovery plane is the opposite: **no hosted traffic unless the operator turns it on.** @@ -38,33 +36,34 @@ When enabled, a miner may query a **shared, metadata-only** discovery index inst The plane: - Serves **public GitHub metadata only** (issue titles, labels, counts, timestamps, URLs — the same class of fields Phase 1 already uses locally). -- May coordinate **soft claims** across the fleet (server-side dedup is [#4250](https://github.com/JSONbored/loopover/issues/4250); client request shape is [#4302](https://github.com/JSONbored/loopover/issues/4302)). +- May coordinate **soft claims** across the fleet, server-side dedup included ([#4250](https://github.com/JSONbored/loopover/issues/4250); client request shape is [#4302](https://github.com/JSONbored/loopover/issues/4302)). - Never receives source trees, diffs, tokens, or write credentials. Local discovery (`opportunity-fanout` + `opportunity-ranker`) continues to work with **zero** hosted configuration. -## Opt-in mechanism (env var names still TBD — pending the opt-in wiring, #4250) +## Opt-in mechanism -The request/response contract has now shipped (see the scope table above), but the operator-facing opt-in env -vars below are **not implemented yet** — treat them as **documentation placeholders** for the shape operators -should expect once the opt-in wiring (#4250) lands: +Real, shipped env vars, read in [`discovery-index-client.ts`](../../loopover-miner/lib/discovery-index-client.ts): -| Variable (provisional) | Default | Purpose | -|------------------------|---------|---------| -| `LOOPOVER_MINER_DISCOVERY_PLANE` | unset / `false` | Master opt-in. When not truthy (`1`, `true`, `yes`, `on`), the miner must not call the hosted index or emit discovery-plane telemetry. | -| `LOOPOVER_MINER_DISCOVERY_INDEX_URL` | unset | Hosted index base URL. Required when the plane is enabled; ignored when opt-in is off. | -| `LOOPOVER_MINER_DISCOVERY_TELEMETRY` | unset / `false` | Separate opt-in for anonymized operational telemetry ([#4301](https://github.com/JSONbored/loopover/issues/4301)). Plane queries can stay on while telemetry stays off. | +| Variable | Default | Purpose | +| ---------------------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `LOOPOVER_MINER_DISCOVERY_PLANE` | unset / `false` | Master opt-in. When not truthy (`1`, `true`, `yes`, `on`), the miner never calls the hosted index and never emits discovery-plane telemetry. | +| `LOOPOVER_MINER_DISCOVERY_INDEX_URL` | unset | Hosted index base URL. Set this to `https://discovery.loopover.ai` to use the maintainer-run instance, or point it at your own deployment of `packages/discovery-index`. Required when the plane is enabled; ignored when opt-in is off. | +| `LOOPOVER_MINER_DISCOVERY_SHARED_SECRET` | unset | Bearer token sent as `Authorization: Bearer ` on every request to the hosted index. Optional against a server that doesn't require it; the maintainer-run instance at `discovery.loopover.ai` does — ask the operator running it for a value. | +| `LOOPOVER_MINER_DISCOVERY_TELEMETRY` | unset / `false` | Separate opt-in for operational telemetry. Plane queries can stay on while telemetry stays off. **Currently local-only** — see Telemetry below. | -**Truthy-string convention** (when implemented): `/^(1|true|yes|on)$/i`, matching other `LOOPOVER_*` flags in this repo. +**Truthy-string convention:** `/^(1|true|yes|on)$/i`, matching other `LOOPOVER_*` flags in this repo. **Operator checklist (enabled plane):** -1. Set `LOOPOVER_MINER_DISCOVERY_PLANE=true` (exact name may change — see [#4300](https://github.com/JSONbored/loopover/issues/4300)). -2. Set `LOOPOVER_MINER_DISCOVERY_INDEX_URL` to the operator-trusted index endpoint ([#4250](https://github.com/JSONbored/loopover/issues/4250)). -3. Optionally set `LOOPOVER_MINER_DISCOVERY_TELEMETRY=true` if you want anonymized operational events for the hosted service — not required for index queries. -4. Keep `GITHUB_TOKEN` (or equivalent) on the instance only; never configure tokens intended for the hosted plane to receive. +1. Set `LOOPOVER_MINER_DISCOVERY_PLANE=true`. +2. Set `LOOPOVER_MINER_DISCOVERY_INDEX_URL=https://discovery.loopover.ai` (or your own deployment's URL). +3. Set `LOOPOVER_MINER_DISCOVERY_SHARED_SECRET` if the index you're pointing at requires one. +4. Optionally set `LOOPOVER_MINER_DISCOVERY_TELEMETRY=true` for local operational log lines about plane usage — not required for index queries. +5. Keep `GITHUB_TOKEN` (or equivalent) on the instance only; never configure tokens intended for the hosted plane to receive. +6. Run `loopover-miner discover --dry-run --json` and check the response for hosted-index candidates alongside your local fan-out to confirm the plane is actually being queried. -With opt-in off (default), behavior is byte-identical to today: local SQLite ledgers, local fan-out, no hosted calls. +Every call in the client is fail-open: a network error, timeout, or non-2xx response degrades silently to "no supplement" rather than failing the miner's own discover/attempt work. With opt-in off (default), behavior is byte-identical to today: local SQLite ledgers, local fan-out, no hosted calls. ## Contrast with local soft-claims today @@ -81,11 +80,13 @@ Mirrors [`DEPLOYMENT.md`](../DEPLOYMENT.md) tone — concrete guarantees for ope - **Read-only client posture** — the miner uses GET/list/search semantics toward GitHub directly (Phase 1) and toward the hosted index when enabled; the plane does not grant the miner new GitHub write capability. - **Credentials stay local** — GitHub tokens, PATs, and actor-capable secrets are injected at runtime on the operator's machine or secret store; they are **never** included in index or telemetry payloads. - **No compensation signals in the plane** — raw reward values, wallet addresses, hotkeys, trust scores, or private rankings never cross this boundary (same public boundary as [`cross-repo-discovery-phase1.md`](cross-repo-discovery-phase1.md) Acceptance). -- **Telemetry is a second opt-in** — even with the plane enabled, anonymized telemetry ([#4301](https://github.com/JSONbored/loopover/issues/4301)) remains separately gated. -- **Anonymized identifiers only** — when telemetry ships, repo/issue correlation uses HMAC-hashed identifiers keyed by a **per-instance dedicated secret** the collector never holds (same posture as `getOrCreateAnonSecret` / `hmacField` in `src/selfhost/orb-collector.ts` — key separation from GitHub App / webhook secrets). -- **Low-cardinality reason buckets** — any free-text-adjacent telemetry fields use bucketed categories (Orb's `bucketReasonCode` pattern), not raw maintainer or model prose. +- **Telemetry is a second opt-in** — even with the plane enabled, telemetry remains separately gated behind `LOOPOVER_MINER_DISCOVERY_TELEMETRY`. - **Core miner still works offline** — claims, plans, queues, and local ledgers do not require the hosted plane; `loopover-miner doctor` / `status` remain no-network commands. +### Telemetry: local today, not yet sent to the hosted service + +`recordDiscoveryTelemetry` (`discovery-index-client.ts`) currently emits a structured **local log line** (`event`, `outcome` — both fixed, low-cardinality strings; no repo/issue identifiers, no free text) via the miner's own logger. No hosted telemetry-collector endpoint exists yet, so nothing about telemetry events leaves the operator's machine today regardless of this flag. The originally planned design called for HMAC-hashed repo/issue correlation identifiers once a real collector ships — that collector doesn't exist yet, so there is nothing to hash today; this section will be updated again once it does. + ### Never included (client → hosted plane) Inventory style matches `src/selfhost/orb-collector.ts:15-17` ("No diffs, no code…") adapted for discovery-plane domain ([#4301](https://github.com/JSONbored/loopover/issues/4301)): @@ -98,9 +99,9 @@ Inventory style matches `src/selfhost/orb-collector.ts:15-17` ("No diffs, no cod - Raw gate reasons, model transcripts, or free-text maintainer notes - Reward amounts, wallet addresses, hotkeys, trust scores, or private rankings -### Never retained by the hosted service (server-side — [#4250](https://github.com/JSONbored/loopover/issues/4250)) +### Never retained by the hosted service (server-side) -The server operating doc (maintainer-only) will restate the same boundary: **never holds source or actor-capable credentials.** This client guide does not define server retention policy; see #4250 deliverables. +The server (`packages/discovery-index`) never holds source or actor-capable credentials — see `packages/discovery-index/README.md` and `OPERATIONS.md` for the server's own boundary and incident-response documentation. This client guide does not define server retention policy in detail. ## Related docs