Skip to content

feat(discovery-index): deploy as a Cloudflare Container with rate limiting - #7897

Merged
JSONbored merged 7 commits into
mainfrom
claude/discovery-index-cloudflare-container
Jul 21, 2026
Merged

feat(discovery-index): deploy as a Cloudflare Container with rate limiting#7897
JSONbored merged 7 commits into
mainfrom
claude/discovery-index-cloudflare-container

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Deploys the existing, unmodified discovery-index Docker image as a Cloudflare Container (wrangler.jsonc + src/worker.ts), rather than a dedicated VPS or third-party PaaS. Chosen over native (non-Container) Workers because the service's result cache and soft-claim dedup store are both in-process memory — a Container is one real, persistent process, so that state stays correct exactly as already tested, where Workers' distributed isolates would not reliably share it (two concurrent soft-claims for the same issue could otherwise both succeed). max_instances: 1 is a correctness requirement for the same reason, not a cost choice.
  • Fixes a real, pre-existing bug this surfaced: the Dockerfile never copied the root tsconfig.json into its build context, so the image build itself was broken — uncaught until now since nothing had built it end-to-end before.
  • Adds the two gaps flagged when the deploy config landed: a blanket, IP-keyed Durable Object rate limiter (src/rate-limiter.ts, 60 req/min, enforced ahead of the Container), and a maintainer-facing operating doc (OPERATIONS.md) covering retention boundaries and incident response.

Scope

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally — 100% line/branch on every new/changed source file (rate-limiter.ts); worker.ts/env.d.ts added to the same Docker-boot-only coverage exclusion server.ts already has, matching precedent.
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries

If any required check was skipped, explain why:

  • This PR only touches packages/discovery-index/**, vitest.config.ts, and codecov.yml — no workflows, MCP, workers, or UI code changed, so those checks are out of scope. Additionally ran npx wrangler deploy --dry-run from packages/discovery-index/ — builds the real Docker image, resolves both Durable Object bindings (container + rate limiter), registers the container. This isn't part of npm run test:ci but is the real functional validation for this change.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed. wrangler.jsonc documents wrangler secret put for both real secrets; neither is committed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests.
  • API/OpenAPI/MCP behavior is updated and tested where needed.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks.
  • Visible UI changes include a UI Evidence section below with JPG/JPEG or PNG screenshots.
  • Public docs/changelogs are updated where needed (README.md, OPERATIONS.md); changelog itself untouched (not a release-prep PR).

UI Evidence

N/A — backend/infra-only change, no UI surface touched.

Notes

  • This does not go live on its own: deploying for real still requires the maintainer's own wrangler secret put DISCOVERY_INDEX_SHARED_SECRET/DISCOVERY_INDEX_GITHUB_TOKEN and wrangler deploy (or connecting the repo via Cloudflare's dashboard git integration), per README.md's updated Deployment section — real Cloudflare account access this PR intentionally doesn't (and can't) include.

Closes #7167
Closes #4250

…7777)

ipv6IsPrivateOrLocal in safe-url.ts recognized the IPv4-mapped IPv6 form
(::ffff:a.b.c.d, normalized by new URL() to ::ffff:7f00:1) but not the older,
ffff:-less IPv4-compatible form (::a.b.c.d, normalized the same bracket-free
way to ::7f00:1). A URL like https://[::169.254.169.254] (cloud metadata) or
https://[::127.0.0.1] (loopback) passed the guard as a public host.

Generalize the existing hex-pair-to-IPv4 conversion to treat "ffff:" as
optional, so both encodings are checked the same way. Applied identically to
the byte-identical engine twin (packages/loopover-engine/src/review/safe-url.ts)
to keep engine-parity:drift-check passing.

Closes #7777
#7778)

scanPackageLockPatch tracked which package-lock entry a line belonged to only
by watching for that entry's own opening "node_modules/<pkg>": { line in the
diff. git's default 3-line context doesn't guarantee that line survives when
a changed resolved/integrity/version field sits deeper into the entry -- when
it doesn't, currentEntryKey stayed null for the whole hunk and the change was
silently dropped instead of flagged.

Add a fallback "unattributed entry" bucket for a tracked-field change with no
known active entry, gated by a new insideRejectedBlock flag so the existing
deliberate-skip case (a malformed "node_modules/" key with nothing after the
marker) still behaves exactly as before.

Closes #7778
…lare Container (#7167)

Adds wrangler.jsonc + a Worker/Durable Object routing entry point (src/worker.ts) to run the
existing, unmodified discovery-index Docker image as a Cloudflare Container rather than a
dedicated VPS or third-party PaaS. Reuses the platform already ratified for the ORB+AMS hosted
control-plane (#7173) and gets TLS/DNS for free via a Workers custom domain.

Chosen over native (non-Container) Workers because the service's result cache and soft-claim
dedup store are both in-process memory: a Container is one real, persistent process, so that
state stays correct exactly as already tested, where Workers' distributed isolates would not
reliably share it and could let two concurrent soft-claims for the same issue both succeed.
max_instances is pinned to 1 for the same reason -- a correctness requirement, not a cost choice.

Also fixes a real, pre-existing bug this surfaced: the Dockerfile never copied the root
tsconfig.json into its build context, so the image build itself was broken (uncaught until now
since nothing had built it end-to-end before).

worker.ts/env.d.ts are added to the existing coverage-exclusion precedent already applied to
server.ts (Docker-boot-only, not unit-coverable) -- no regression to existing discovery-index
test coverage, verified at 100% line/branch on every existing source file.

Closes #7167
…#4250)

Fixes the two gaps flagged when the Cloudflare Container deployment landed:

- rate-limiter.ts: a Durable-Object-backed, IP-keyed fixed-window rate limiter (60 req/min),
  enforced in worker.ts ahead of the container for /v1/discovery-index/* only. IP-keyed rather
  than per-caller because every opted-in miner shares one DISCOVERY_INDEX_SHARED_SECRET with no
  caller identity on the wire (soft-claim.ts's own design). Fails open on a Durable Object error,
  mirroring the main app's own RateLimiter (src/auth/rate-limit.ts). Unit-tested directly with a
  fake DurableObjectState/namespace, the same pattern the main app's RateLimiter already uses --
  100% line/branch coverage, not excluded from Codecov.

- OPERATIONS.md: the maintainer-facing operating doc the client operator guide already referenced
  but didn't exist yet -- retention boundaries (all in-process memory, nothing persisted beyond
  short TTLs), the abuse posture above, and incident-response steps for a leaked shared secret or
  GitHub token.

Verified: wrangler deploy --dry-run resolves both Durable Object bindings; full existing
discovery-index suite (124 tests) + typecheck green.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 21, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
loopover-ui 17aa709 Commit Preview URL

Branch Preview URL
Jul 21 2026, 05:01 PM

@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 21, 2026
@loopover-orb

loopover-orb Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Warning

⏸️ LoopOver review result - manual review recommended

Review updated: 2026-07-21 17:09:27 UTC

16 files · 1 AI reviewer · 2 blockers · CI green · clean

⏸️ Suggested Action - Manual Review

Review summary
This deploys the existing discovery-index Docker image as a Cloudflare Container fronted by a Worker with a Durable Object rate limiter, plus an operations doc. The core design (single fixed-instance name for in-process cache/soft-claim correctness, IP-keyed fail-open rate limiting, unmodified Docker image) is sound and well-reasoned, and the rate-limiter itself has solid dedicated unit tests. The main gap is that the new worker.ts/env.d.ts/wrangler.jsonc wiring — the actual deploy mechanism this PR is about — is entirely untested except by inspection, relying on codecov/vitest exclusions justified as 'infra glue, only exercised by real Cloudflare infrastructure.'

Nits — 7 non-blocking
  • packages/discovery-index/src/worker.ts:32 hardcodes envVars at class-field-initializer time via `import { env } from "cloudflare:workers"` — worth a comment/test note confirming this resolves per-request in the Workers runtime rather than being frozen at module load, since Container class fields are unusual for this.
  • packages/discovery-index/src/rate-limiter.ts uses magic numbers (400/429/200 status codes, retry-after floor of 1) without named constants — minor readability nit given the file otherwise documents its numeric choices (RATE_LIMIT, RATE_LIMIT_WINDOW_SECONDS) well.
  • OPERATIONS.md's incident-response section for a leaked shared secret has no automated revocation/rotation notification path yet ('not built yet as of chore(discovery-plane): decide and stand up hosting/deployment for the discovery-index server #7167') — worth tracking as a fast-follow rather than leaving purely as prose.
  • package-lock.json's ~21k line diff is expected for new deps (wrangler + transitive) and not itself a concern, but worth calling out in the PR description for reviewer context.
  • Consider adding a lightweight smoke test (even just type-level, via cf:typecheck in CI) specifically asserting worker.ts's fetch handler routes rate-limited paths correctly, since worker.ts itself is excluded from coverage and only the rate-limiter helper is unit-tested.
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.
  • Diff is mostly generated, vendored, or minified output — Exclude generated, vendored, and minified output and keep the diff focused on substantive changes.

Concerns raised — review before merging

  • No linked issue detected: The PR cites an issue number, but it could not be verified as a currently open issue. — If this PR is intended to solve an issue, link it explicitly in the PR body.
  • Patch-less file(s) could not be fully scanned for secrets (1): GitHub omitted inline diff for: packages/discovery-index/worker-configuration.d.ts. Fetched content exceeded the 512000-char scan cap or could not be retrieved completely, so leaked-secret verification is incomplete. Shrink the change, split the file, or ensure the diff is reviewable before merge. — Ensure patch-less files are within scan limits or split the change so secrets can be verified.
📋 Copy for AI agents — paste into your coding agent
Fix the following blocker(s) from this PR review:

1. No linked issue detected: The PR cites an issue number, but it could not be verified as a currently open issue. — If this PR is intended to solve an issue, link it explicitly in the PR body.

2. Patch-less file(s) could not be fully scanned for secrets (1): GitHub omitted inline diff for: packages/discovery-index/worker-configuration.d.ts. Fetched content exceeded the 512000-char scan cap or could not be retrieved completely, so leaked-secret verification is incomplete. Shrink the change, split the file, or ensure the diff is reviewable before merge. — Ensure patch-less files are within scan limits or split the change so secrets can be verified.

Decision drivers

  • ❌ Code review — 2 blockers (1 reviewer)
  • ❌ Gate result — Blocking (Repo-configured hard blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #7167, #4250
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (2 linked issues).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 21 registered-repo PR(s), 14 merged, 325 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 21 PR(s), 325 issue(s).
Improvement ✅ Minor risk: low · value: minor
Linked issue satisfaction

Partially addressed
The PR covers hosting decision, DNS/TLS via Cloudflare custom domain, and edge rate-limiting well, but explicitly opts out of the issue's observability requirement — OPERATIONS.md states the service is 'not wired into the self-host fleet's own Grafana/Alloy stack,' relying instead on separate Cloudflare-native observability, which contradicts the issue's explicit instruction to 'wire the new servi

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: not available
  • Official Gittensor activity: 21 PR(s), 325 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask 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.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

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.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 21, 2026
…ated types file

Raw \`wrangler types\` output has trailing whitespace on several lines, which fails this repo's
own git diff --check whitespace gate the moment it's committed (caught by CI on the previous
commit). Adds a small wrapper script (packages/discovery-index/scripts/gen-cf-typegen.mjs,
simpler than the root repo's own scripts/gen-cf-typegen.mjs since this package's Env has no
Pick<Cloudflare.Env, ...> union to reformat) so regenerating after a wrangler.jsonc change
doesn't reintroduce the same failure.
…e-container' into claude/discovery-index-cloudflare-container
@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.40%. Comparing base (2364ad4) to head (17aa709).
⚠️ Report is 3 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7897   +/-   ##
=======================================
  Coverage   91.40%   91.40%           
=======================================
  Files         730      731    +1     
  Lines       74789    74812   +23     
  Branches    22822    22828    +6     
=======================================
+ Hits        68358    68381   +23     
  Misses       5389     5389           
  Partials     1042     1042           
Flag Coverage Δ
shard-1 56.52% <0.00%> (-1.46%) ⬇️
shard-2 52.24% <0.00%> (+1.01%) ⬆️
shard-3 51.95% <100.00%> (+0.80%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/discovery-index/src/rate-limiter.ts 100.00% <100.00%> (ø)

@JSONbored
JSONbored merged commit d95350b into main Jul 21, 2026
16 checks passed
@JSONbored
JSONbored deleted the claude/discovery-index-cloudflare-container branch July 21, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

1 participant