Skip to content

feat(orb): relay registration — a brokered self-host registers its event target - #1349

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

feat(orb): relay registration — a brokered self-host registers its event target#1349
JSONbored merged 1 commit into
mainfrom
feat/orb-relay-registration

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Event-relay foundation for brokered self-host (#1255). A brokered self-host installs the central Orb App and holds only its enrollment secret; the central Orb receives its repos' webhooks. For the container to actually review, the Orb must forward those events to it — this PR lets the container register where, and establishes the per-tenant signing material.

POST /v1/orb/relay/register (Bearer = the container's own enrollment secret, flag-gated):

  • validates the secret → the bound, registered, non-suspended install (same gate as the token broker);
  • SSRF-validates the relay URL via the shared isSafeHttpUrl (https + public host — a registered URL can never coerce the Orb into hitting an internal service);
  • stores the URL + the enrollment secret encrypted at rest (AES-256-GCM via TOKEN_ENCRYPTION_SECRET). The Orb HMAC-signs each forwarded event with that secret; the container verifies with its own ORB_ENROLLMENT_SECRETper-enrollment isolation, and a DB-only leak can't forge (the encryption key is a separate secret).

migration 0069 adds relay_url + the encrypted-secret columns to orb_enrollments. The forward (Orb → container) + the container's /v1/orb/relay receiver land in the next PRs.

Validation

  • npm run test:ci green; 100% branch coverage on relay.ts (every eligibility arm — unregistered/suspended/removed/deleted; SSRF loopback+localhost; no-encryption; success) + the endpoint status mapping (401/403/400/500/404, unparseable body) + the exemption + rate class.

Safety

  • SSRF guard on the registered URL; secret stored encrypted (never plaintext); flag-gated (404 until enabled) → byte-identical.

Advances #1255.

…ent target

Event-relay foundation (#1255). A brokered self-host installs the central Orb App + holds only its enrollment
secret; the central Orb receives its repos' webhooks. For the container to actually review, the Orb must FORWARD
those events to it — this PR lets the container register WHERE + establishes the per-tenant signing material.

POST /v1/orb/relay/register (Bearer = the container's own enrollment secret, flag-gated): validates the secret →
the bound, registered, non-suspended install (same gate as the token broker), SSRF-validates the relay URL via
the shared isSafeHttpUrl (https + public host — so a registered URL can never coerce the Orb into hitting an
internal service), then stores the URL + the enrollment secret ENCRYPTED at rest (AES-256-GCM via
TOKEN_ENCRYPTION_SECRET). The Orb HMAC-signs each forwarded event with that secret; the container verifies with
its own ORB_ENROLLMENT_SECRET — per-enrollment isolation, and a DB-only leak can't forge (the encryption key is
a separate secret). migration 0069 adds the relay_url + encrypted-secret columns to orb_enrollments.

The forward (Orb → container) + the container's /v1/orb/relay receiver land in the next PRs.

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.28%. Comparing base (1b3af12) to head (07370c7).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1349   +/-   ##
=======================================
  Coverage   95.28%   95.28%           
=======================================
  Files         188      189    +1     
  Lines       20371    20395   +24     
  Branches     7343     7357   +14     
=======================================
+ Hits        19410    19434   +24     
  Misses        378      378           
  Partials      583      583           
Files with missing lines Coverage Δ
src/api/routes.ts 94.58% <100.00%> (+0.04%) ⬆️
src/auth/rate-limit.ts 98.80% <100.00%> (+0.01%) ⬆️
src/orb/relay.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 fafdcf9 into main Jun 25, 2026
19 checks passed
@JSONbored
JSONbored deleted the feat/orb-relay-registration branch June 25, 2026 10:10
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.
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.

GitHub App identity unification + atomic webhook-secret swap (retire reviewwed[bot])

1 participant