Skip to content

feat(orb): self-host broker client — brokered installation tokens - #1341

Merged
JSONbored merged 1 commit into
mainfrom
feat/orb-broker-client
Jun 25, 2026
Merged

feat(orb): self-host broker client — brokered installation tokens#1341
JSONbored merged 1 commit into
mainfrom
feat/orb-broker-client

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

Completes the Orb token broker end-to-end (the server side shipped in #1330/#1332). A brokered self-host holds no GitHub App private key — it installs the central Orb App and sets the operator-issued ORB_ENROLLMENT_SECRET. createInstallationToken now sources tokens from the central Orb (POST /v1/orb/token) when that secret is present, caching them in the same in-isolate token cache as the App-key path (~1 mint/hour/install). Cloud never sets the secret, so the branch is inert there → byte-identical.

  • src/orb/broker-client.tsisOrbBrokerMode (the secret-presence gate) + fetchBrokeredInstallationToken (exchange secret → {token, installationId, expiresAt}; injectable fetch + 10s timeout; throws on non-OK / tokenless body).
  • src/github/app.ts — the broker branch slots in at the single token chokepoint, right after the cache check.
  • No App-key fallback by design (a brokered self-host has none): a broker outage fails the request exactly like an App-key mint failure, and the queue's existing retry/dead-letter handling covers a transient blip. The secret is sent as a Bearer over the https default and never logged (errors carry only the status).

Validation

  • npm run test:ci green.
  • 32 tests (6 broker-client + the existing github-app suite incl. a broker-mode integration test that asserts the token comes from /v1/orb/token and is cached); 100% branch coverage on the diff (default/custom broker URL, trailing-slash strip, present/absent expiry + installationId, empty-secret defensive branch, non-OK + tokenless throws, and the cloud byte-identical path).

Safety

  • Gated on ORB_ENROLLMENT_SECRET presence; cloud sets none → byte-identical. No secret in logs/code. No new wrangler var (self-host secret).

Advances #1255. (Maintainer-OAuth self-enrollment remains a clean follow-up — today enrollments are operator-issued.)

Completes the Orb token broker end-to-end (server: #1330/#1332). A brokered self-host holds no GitHub App private
key — it installs the central Orb App and sets the operator-issued ORB_ENROLLMENT_SECRET. createInstallationToken
now sources tokens from the central Orb (POST /v1/orb/token) when that secret is present, caching them in the
same in-isolate token cache as the App-key path (~1 mint/hour/install). Cloud never sets the secret, so the
branch is inert there → byte-identical.

- src/orb/broker-client.ts: isOrbBrokerMode (secret-presence gate) + fetchBrokeredInstallationToken (exchange
  secret → {token, installationId, expiresAt}; injectable fetch + 10s timeout; throws on non-OK / tokenless body).
- src/github/app.ts: the broker branch slots in at the single token chokepoint, right after the cache check.
- No App-key fallback by design (a brokered self-host has none) — a broker outage fails the request exactly like
  an App-key mint failure, and the queue's retry/dead-letter handling covers a transient blip. The secret is sent
  as a Bearer over the https default and never logged (errors carry only the status).

Advances #1255. (Maintainer-OAuth self-enrollment remains a follow-up; today enrollments are operator-issued.)
@dosubot dosubot Bot added the size:M label Jun 25, 2026
@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.26%. Comparing base (3d8ad8d) to head (61508fb).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1341   +/-   ##
=======================================
  Coverage   95.26%   95.26%           
=======================================
  Files         187      188    +1     
  Lines       20359    20377   +18     
  Branches     7339     7347    +8     
=======================================
+ Hits        19395    19413   +18     
  Misses        378      378           
  Partials      586      586           
Files with missing lines Coverage Δ
src/github/app.ts 97.11% <100.00%> (+0.11%) ⬆️
src/orb/broker-client.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 self-assigned this Jun 25, 2026
@superagent-security

Copy link
Copy Markdown
Contributor

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

@JSONbored
JSONbored merged commit d6748c4 into main Jun 25, 2026
18 checks passed
@JSONbored
JSONbored deleted the feat/orb-broker-client branch June 25, 2026 08:49
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