Skip to content

feat(orb): forward registered installs' events to the brokered self-host - #1352

Merged
JSONbored merged 1 commit into
mainfrom
feat/orb-relay-forward
Jun 25, 2026
Merged

feat(orb): forward registered installs' events to the brokered self-host#1352
JSONbored merged 1 commit into
mainfrom
feat/orb-relay-forward

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

The Orb side of the event relay (#1255) — the missing event half. When the central Orb records a PR/check/issue webhook for an installation that registered a relay target (#1349), it now forwards the raw event to the container, HMAC-signed with the container's enrollment secret (decrypted from the at-rest ciphertext). The container verifies with its own ORB_ENROLLMENT_SECRET, so only the genuine Orb can drive it. A brokered container can now actually receive its repos' events to review (it already mints tokens via the broker, #1341).

  • forwardOrbEvent (src/orb/relay.ts): only review-relevant events (pull_request, reviews, check_run/check_suite, issue_comment, issues) are forwarded — installation-lifecycle + Orb-internal events are not (the container runs under the central App, not its own). Best-effort + fail-safe: a non-forwardable event, no registered relay, or any error (down container, decrypt/sign failure) returns without throwing, so the Orb's webhook 202 always stands. 10s timeout. relaySignature is the shared HMAC both sides recompute.
  • Wired into the orb/webhook receiver after the event is recorded (the duplicate path returns earlier → each delivery forwards once).

Validation

  • npm run test:ci green; 100% branch coverage on relay.ts + webhook.ts diff — skip arms (non-forwardable / no install / no relay / no encryption key), the signed forward (asserts the URL + x-github-* + x-orb-signature-256 match relaySignature), failed (non-ok + thrown), and relaySignature determinism.

Safety

  • HMAC-signed per-tenant (the container verifies the Orb's signature); fail-safe (never fails the 202). Reliability hardening (a retry queue for a transiently-down container) is a noted follow-up; the container /v1/orb/relay receiver + boot self-register land next.

Advances #1255.

The Orb side of the event relay (#1255). When the central Orb records a PR/check/issue webhook for an
installation that registered a relay target, it now FORWARDS the raw event to the container, HMAC-signed with the
container's enrollment secret (decrypted from the at-rest ciphertext); the container verifies with its own
ORB_ENROLLMENT_SECRET, so only the genuine Orb can drive it. This is the missing event half — a brokered
container can now actually receive its repos' events to review (it already mints tokens via the broker).

- forwardOrbEvent (in src/orb/relay.ts): only the review-relevant events (pull_request, reviews, check_run/suite,
  issue_comment, issues) are forwarded — installation-lifecycle + Orb-internal events are NOT (the container runs
  under the CENTRAL App, not its own). BEST-EFFORT + fail-safe: a non-forwardable event, no registered relay, or
  ANY error (down container, decrypt/sign failure) returns without throwing, so the Orb's webhook 202 always
  stands. 10s timeout. relaySignature is the shared HMAC both sides recompute.
- Wired into the orb/webhook receiver after the event is recorded (the duplicate path returns earlier, so each
  delivery forwards once).

Reliability hardening (a retry queue for a transiently-down container) is a noted follow-up. The container-side
/v1/orb/relay receiver + the boot self-register land next. Advances #1255.
@dosubot dosubot Bot added the size:M label Jun 25, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

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

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.30%. Comparing base (2bdef57) to head (4a72032).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1352   +/-   ##
=======================================
  Coverage   95.29%   95.30%           
=======================================
  Files         189      189           
  Lines       20432    20447   +15     
  Branches     7370     7374    +4     
=======================================
+ Hits        19471    19486   +15     
  Misses        378      378           
  Partials      583      583           
Files with missing lines Coverage Δ
src/orb/relay.ts 100.00% <100.00%> (ø)
src/orb/webhook.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@JSONbored
JSONbored merged commit 0ece080 into main Jun 25, 2026
18 checks passed
@JSONbored
JSONbored deleted the feat/orb-relay-forward branch June 25, 2026 10:19
JSONbored added a commit that referenced this pull request Jun 25, 2026
…#1354)

The container side of the event relay (#1255), completing brokered self-host end-to-end: the central Orb now
forwards a registered install's events (#1352), and this is where the container RECEIVES them. POST /v1/orb/relay
verifies the Orb's HMAC signature (x-orb-signature-256) against the container's own ORB_ENROLLMENT_SECRET — in
constant time (crypto.subtle.verify) — then enqueues the event exactly like a GitHub webhook. So a brokered
container (central Orb App + an enrollment secret, no own App key) now both RECEIVES its repos' events and ACTS
on them via brokered tokens.

- relayVerify (src/orb/relay.ts): constant-time HMAC verify of the sha256= signature; rejects wrong-secret,
  malformed/odd-length hex, and missing secret/header.
- handleOrbRelay (src/github/webhook.ts): 400 (no headers) / 404 (not a brokered self-host) / 413 (oversized) /
  401 (bad signature) / 202 (verified → enqueued). The GitHub webhook receiver's post-verify path is extracted
  into the shared enqueueVerifiedWebhook (parse → dedup → record → WEBHOOKS lane → 202); both receivers reuse it.
- Route + token-exemption (auth IS the relay signature) + strict rate class.

Auto-registering the container's relay URL on boot is the remaining convenience follow-up (the registration
endpoint #1349 already exists). Advances #1255.
JSONbored added a commit that referenced this pull request Jun 25, 2026
…1355)

Closes the brokered self-host loop (#1255): the container now self-registers its public relay URL with the
central Orb on startup, so the Orb forwards this install's events to it — no manual curl. The container computes
its relay URL from PUBLIC_API_ORIGIN + /v1/orb/relay and POSTs it to the broker with its enrollment secret.

registerOrbRelayTarget (src/orb/broker-client.ts) is BEST-EFFORT + fire-and-forget: skipped unless broker mode +
PUBLIC_API_ORIGIN are set, and any failure (Orb down, install not registered yet, non-public origin rejected by
the Orb's SSRF check) just means no relay until the next boot — it never throws or blocks startup. Wired into the
selfhost boot alongside the orb-export hook (server.ts, the codecov-ignored process entry).

End-to-end now: install Orb App → self-enroll (admin-verified, #1348) → broker tokens (#1341) → boot
auto-registers relay (this) → Orb forwards events (#1352) → relay receiver verifies + enqueues (#1354) → review +
act. Advances #1255.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant