feat(discovery-index): deploy as a Cloudflare Container with rate limiting - #7897
Conversation
…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.
Deploying with
|
| 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 didn't find any vulnerabilities or security issues in this PR. |
|
Warning ⏸️ LoopOver review result - manual review recommendedReview updated: 2026-07-21 17:09:27 UTC
Review summary Nits — 7 non-blocking
Concerns raised — review before merging
📋 Copy for AI agents — paste into your coding agentDecision drivers
Context & advisory signals — never blocks the verdict
Linked issue satisfactionPartially addressed 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.
|
…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
Bundle ReportBundle size has no change ✅ |
Codecov Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Summary
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: 1is a correctness requirement for the same reason, not a cost choice.tsconfig.jsoninto its build context, so the image build itself was broken — uncaught until now since nothing had built it end-to-end before.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
type(scope): short summaryConventional Commit format.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally — 100% line/branch on every new/changed source file (rate-limiter.ts);worker.ts/env.d.tsadded to the same Docker-boot-only coverage exclusionserver.tsalready has, matching precedent.npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderateIf any required check was skipped, explain why:
packages/discovery-index/**,vitest.config.ts, andcodecov.yml— no workflows, MCP, workers, or UI code changed, so those checks are out of scope. Additionally rannpx wrangler deploy --dry-runfrompackages/discovery-index/— builds the real Docker image, resolves both Durable Object bindings (container + rate limiter), registers the container. This isn't part ofnpm run test:cibut is the real functional validation for this change.Safety
wrangler.jsoncdocumentswrangler secret putfor both real secrets; neither is committed.UI Evidencesection below with JPG/JPEG or PNG screenshots.UI Evidence
N/A — backend/infra-only change, no UI surface touched.
Notes
wrangler secret put DISCOVERY_INDEX_SHARED_SECRET/DISCOVERY_INDEX_GITHUB_TOKENandwrangler deploy(or connecting the repo via Cloudflare's dashboard git integration), perREADME.md's updated Deployment section — real Cloudflare account access this PR intentionally doesn't (and can't) include.Closes #7167
Closes #4250