feat(control-plane): route incoming GitHub webhooks to hosted ORB tenants - #8078
Conversation
…ants (#7181) Adds orb-webhook-router.ts: a request-time routing layer for POST /v1/orb/webhook that verifies an incoming GitHub webhook's HMAC-SHA256 signature (a hosted-fleet- specific secret, independent of the main app's own ORB_GITHUB_WEBHOOK_SECRET, since control-plane is a separate Worker with no D1 access), extracts the installation ID, looks the owning tenant up via a new KV secondary index (tenant-registry.ts's getByOrbInstallationId, kept in sync on every upsert), and proxies the still-unmodified request straight through to that tenant's own container. @cloudflare/containers' Container.fetch() already wakes a sleeping container and waits for its HTTP port before resolving, so no manual wake/poll loop is needed the way ams-wake.ts's one-shot CLI orchestration required. The tenant's container re-verifies the same signature independently (it runs the main app's own self-host image, completely unmodified) -- this layer only changes WHERE a verified webhook gets dispatched, not how it's authenticated. POST /v1/tenants gains an optional orbInstallationId field (ORB-only, rejecting a duplicate claim by another active tenant with 409) to link a hosted tenant to the GitHub App installation its container should answer for; GET /v1/tenants surfaces it back for admin visibility, mirroring #7182's amsSchedule field exactly. Closes #7181
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
|
Tip ✅ LoopOver review result - approve/merge recommendedReview updated: 2026-07-22 20:43:29 UTC
Review summary Nits — 7 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.
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8078 +/- ##
==========================================
- Coverage 91.98% 91.46% -0.52%
==========================================
Files 748 749 +1
Lines 76617 76855 +238
Branches 23239 23277 +38
==========================================
- Hits 70474 70297 -177
- Misses 5041 5503 +462
+ Partials 1102 1055 -47
Flags with carried forward coverage won't be shown. Click here to find out more.
|
Summary
Implements #7181, the last unbuilt piece of #7173's ORB side (AMS's own cron-wake orchestration, #7182, already shipped).
orb-webhook-router.ts: a request-time routing layer forPOST /v1/orb/webhook— verifies an incoming GitHub webhook's HMAC-SHA256 signature (a hosted-fleet-specific secret,ORB_WEBHOOK_SECRET, independent of the main app's ownORB_GITHUB_WEBHOOK_SECRET, since control-plane is a separate Worker with no D1 access), extracts the installation ID, looks up the owning tenant, and proxies the still-unmodified request straight through to that tenant's own container.@cloudflare/containers'Container.fetch()already wakes a sleeping container and waits for its HTTP port before resolving — no manual wake/poll loop needed here (unlikeams-wake.ts's one-shot CLI orchestration, which has no persistent HTTP server to wait on).tenant-registry.tsgainsgetByOrbInstallationId, backed by a newinstallation:${id}KV secondary index kept in sync on everyupsert(handles re-linking and unlinking, not just first-link).POST /v1/tenantsgains an optionalorbInstallationIdfield (ORB-only, rejecting a duplicate claim by another active tenant with 409);GET /v1/tenantssurfaces it back, mirroring Cloudflare Cron Trigger wake scheduling for hosted AMS containers #7182'samsSchedulefield exactly.Test plan
npm run build(typecheck) cleannpm run cf:typecheckcleannpm run test:node— 159/159 passingnode ../scripts/control-plane-coverage.mjs— every touched file 100%/100%/100%/100%; only gap is the pre-existing, untouchedsettlement-backend-driver.tsnpx wrangler deploy --dry-runclean, both container images build, all bindings resolvenpm audit --audit-level=moderate— pre-existing findings only, no dependency changes in this PRCloses #7181