Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import { handleGitHubWebhook } from "../github/webhook";
import { handleOrbIngest, readOrbIngestBody } from "../orb/ingest";
import { handleOrbWebhook } from "../orb/webhook";
import { handleOrbOAuthCallback } from "../orb/oauth";
import { brokerOrbToken, isOrbBrokerEnabled, issueOrbEnrollment } from "../orb/broker";
import { computeFleetAnalytics } from "../orb/analytics";
import { handleMcpRequest } from "../mcp/server";
import { buildOpenApiSpec } from "../openapi/spec";
Expand Down Expand Up @@ -2873,6 +2874,18 @@ export function createApp() {
// Post-install / OAuth landing — the App's Callback URL. Token-exempt; GitHub drives the redirect after a
// maintainer installs or updates the Orb App. Lands on a real page instead of a 401.
app.get("/v1/orb/oauth/callback", handleOrbOAuthCallback);
// Token-broker exchange: a self-hosted container presents its enrollment secret (Bearer) → a short-lived
// GitHub installation token for the BOUND install. Token-exempt (the enrollment secret IS the auth); flag-gated
// (404 until ORB_BROKER_ENABLED); the installation_id is read server-side from the enrollment, never the request.
app.post("/v1/orb/token", async (c) => {
if (!isOrbBrokerEnabled(c.env)) return c.json({ error: "not_found" }, 404);
const auth = c.req.header("authorization") ?? "";
const secret = auth.startsWith("Bearer ") ? auth.slice(7).trim() : "";
if (!secret) return c.json({ error: "missing_enrollment_secret" }, 401);
const result = await brokerOrbToken(c.env, secret);
if ("error" in result) return c.json(result, result.error === "invalid_enrollment" ? 401 : 403);
return c.json(result);
});

// Gittensory Orb (#1255) — central fleet-calibration collector. Receives anonymized, reversal-aware
// outcome batches from self-hosted instances. No auth required: all data is HMAC-anonymized by the sender;
Expand Down Expand Up @@ -2960,6 +2973,19 @@ export function createApp() {
return c.json({ installationId, registered: registered === 1 });
});

// Operator-only: issue a one-time token-broker enrollment secret for a REGISTERED install, to hand to that
// maintainer's self-hosted container. The secret is returned ONCE (stored only hashed). Bearer-gated by the
// /v1/internal/* middleware (INTERNAL_JOB_TOKEN); flag-gated (404 until ORB_BROKER_ENABLED).
app.post("/v1/internal/orb/enrollments", async (c) => {
if (!isOrbBrokerEnabled(c.env)) return c.json({ error: "not_found" }, 404);
const payload = (await c.req.json().catch(() => null)) as { installationId?: unknown } | null;
const installationId = Number(payload?.installationId);
if (!Number.isInteger(installationId) || installationId <= 0) return c.json({ error: "installationId required" }, 400);
const result = await issueOrbEnrollment(c.env, installationId);
if ("error" in result) return c.json(result, result.error === "installation_not_found" ? 404 : 409);
return c.json(result); // { enrollId, secret } — secret shown exactly once
});

// Convergence (ops / observability, flag GITTENSORY_REVIEW_OPS). Cross-repo review-OUTCOME aggregate (gate-block
// ledger + recommendation/slop calibration) for an operator dashboard. Bearer-gated by the `/v1/internal/*`
// middleware above (INTERNAL_JOB_TOKEN). Flag-OFF (default) → 404, so the endpoint does not exist and the
Expand Down Expand Up @@ -4908,6 +4934,7 @@ function requiresApiToken(path: string): boolean {
if (path === "/v1/github/webhook") return false;
if (path === "/v1/orb/webhook") return false;
if (path === "/v1/orb/oauth/callback") return false;
if (path === "/v1/orb/token") return false;
if (path === "/v1/orb/ingest") return false;
if (path.startsWith("/v1/internal/")) return false;
return path.startsWith("/v1/");
Expand Down
1 change: 1 addition & 0 deletions src/auth/rate-limit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export function routeClassForPath(path: string): RateLimitClass {
// narrow IP range; the per-IP strict cap is proven for /v1/github/webhook and #1292 reserves headroom).
if (path === "/v1/orb/webhook") return "strict";
if (path === "/v1/orb/oauth/callback") return "strict";
if (path === "/v1/orb/token") return "strict";
// Orb telemetry ingest: unauthenticated + write, accepting anonymized batches from untrusted
// self-host instances. Strict (10/min per IP) caps abuse — legitimate instances export hourly.
if (path === "/v1/orb/ingest") return "strict";
Expand Down
57 changes: 57 additions & 0 deletions src/orb/broker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Gittensory Orb central GitHub App (#1255) — the token-broker. A maintainer's self-hosted container exchanges a
// one-time enrollment secret for short-lived GitHub installation tokens, so it can act on its own repos WITHOUT
// ever holding the Orb App private key (gittensory holds it centrally and mints on demand).
//
// Trust model (das-github-mirror): the OPERATOR is the authority. An enrollment is issued only for an install the
// operator has already opted in (registered=1) via the internal-token-gated POST /v1/internal/orb/enrollments;
// the secret is shown to the operator ONCE and stored only as a SHA-256 hash. The container then presents that
// secret to /v1/orb/token. The minted token's installation_id comes from the enrollment ROW (bound server-side at
// issue time) — never from the request — so a stolen secret for install X can never mint a token for install Y.
// Every path is inert (404) until ORB_BROKER_ENABLED is set. (Maintainer-OAuth self-enrollment is a later layer;
// the operator-issued path here avoids the OAuth privilege-escalation surface the red-team flagged.)
import { createOpaqueToken, hashToken } from "../auth/security";
import { createOrbInstallationToken } from "./app-auth";

export function isOrbBrokerEnabled(env: Env): boolean {
return /^(1|true|yes|on)$/i.test(String(env.ORB_BROKER_ENABLED ?? "").trim());
}

export type IssueResult = { enrollId: string; secret: string } | { error: "installation_not_found" | "installation_not_registered" };

/** Operator-only: mint a one-time enrollment secret for a REGISTERED install. Returns the plaintext secret ONCE
* (stored only hashed) for the operator to hand to the container's config. */
export async function issueOrbEnrollment(env: Env, installationId: number): Promise<IssueResult> {
const install = await env.DB.prepare("SELECT registered FROM orb_github_installations WHERE installation_id = ?").bind(installationId).first<{ registered: number }>();
if (!install) return { error: "installation_not_found" };
if (install.registered !== 1) return { error: "installation_not_registered" };
const enrollId = createOpaqueToken("orbenr");
const secret = createOpaqueToken("orbsec");
await env.DB.prepare(
`INSERT INTO orb_enrollments (enroll_id, installation_id, secret_hash, state, authorized_at, enrolled_at)
VALUES (?, ?, ?, 'enrolled', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`,
)
.bind(enrollId, installationId, await hashToken(secret))
.run();
return { enrollId, secret };
}

export type BrokerResult = { token: string; installationId: number; expiresAt: string } | { error: "invalid_enrollment" | "installation_not_eligible" };

/** The container's token-exchange: a valid enrollment secret → a short-lived installation token for the BOUND
* install. installation_id is read from the enrollment row, never the caller; the install must still be
* registered=1 and neither suspended nor removed at mint time (the gate is re-checked, not trusted from issue). */
export async function brokerOrbToken(env: Env, secret: string): Promise<BrokerResult> {
const row = await env.DB
.prepare("SELECT enroll_id, installation_id, state, revoked_at FROM orb_enrollments WHERE secret_hash = ?")
.bind(await hashToken(secret))
.first<{ enroll_id: string; installation_id: number; state: string; revoked_at: string | null }>();
if (!row || row.state !== "enrolled" || row.revoked_at !== null) return { error: "invalid_enrollment" };
const install = await env.DB
.prepare("SELECT registered, suspended_at, removed_at FROM orb_github_installations WHERE installation_id = ?")
.bind(row.installation_id)
.first<{ registered: number; suspended_at: string | null; removed_at: string | null }>();
if (!install || install.registered !== 1 || install.suspended_at !== null || install.removed_at !== null) return { error: "installation_not_eligible" };
const minted = await createOrbInstallationToken(env, row.installation_id);
await env.DB.prepare("UPDATE orb_enrollments SET last_token_at = CURRENT_TIMESTAMP WHERE enroll_id = ?").bind(row.enroll_id).run();
return { token: minted.token, installationId: row.installation_id, expiresAt: minted.expiresAt };
}
113 changes: 113 additions & 0 deletions test/integration/orb-broker.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { createApp } from "../../src/api/routes";
import { brokerOrbToken, isOrbBrokerEnabled, issueOrbEnrollment } from "../../src/orb/broker";
import { createTestEnv, type TestD1Database } from "../helpers/d1";

async function pkcs8Pem(): Promise<string> {
const key = (await crypto.subtle.generateKey({ name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: "SHA-256" }, true, ["sign", "verify"])) as CryptoKeyPair;
const b64 = Buffer.from((await crypto.subtle.exportKey("pkcs8", key.privateKey)) as ArrayBuffer).toString("base64").replace(/(.{64})/g, "$1\n");
return `-----BEGIN PRIVATE KEY-----\n${b64}\n-----END PRIVATE KEY-----`;
}
const db = (e: Env) => e.DB as unknown as TestD1Database;
const seedInstall = (e: Env, id: number, cols: Record<string, string | number | null> = {}) => {
const all: Record<string, string | number | null> = { installation_id: id, registered: 1, ...cols };
const keys = Object.keys(all);
return db(e).prepare(`INSERT INTO orb_github_installations (${keys.join(", ")}) VALUES (${keys.map(() => "?").join(", ")})`).bind(...keys.map((k) => all[k] as string | number | null)).run();
};
const brokerEnv = async (over: Partial<Env> = {}): Promise<Env> =>
createTestEnv({ ORB_BROKER_ENABLED: "true", ORB_GITHUB_APP_ID: "4139483", ORB_GITHUB_APP_PRIVATE_KEY: await pkcs8Pem(), INTERNAL_JOB_TOKEN: "dev-internal-token", ...over });
const tokenFetch = (token = "ghs_broker", expires = "2026-06-25T08:00:00Z") => vi.stubGlobal("fetch", async () => Response.json({ token, expires_at: expires }));

afterEach(() => vi.unstubAllGlobals());

describe("isOrbBrokerEnabled", () => {
it("is off by default, on for a truthy flag", () => {
expect(isOrbBrokerEnabled(createTestEnv())).toBe(false);
expect(isOrbBrokerEnabled(createTestEnv({ ORB_BROKER_ENABLED: "true" }))).toBe(true);
});
});

describe("issueOrbEnrollment", () => {
it("404s an unknown install, rejects an unregistered one, issues a hashed secret for a registered one", async () => {
const e = await brokerEnv();
expect(await issueOrbEnrollment(e, 999)).toEqual({ error: "installation_not_found" });
await seedInstall(e, 200, { registered: 0 });
expect(await issueOrbEnrollment(e, 200)).toEqual({ error: "installation_not_registered" });
await seedInstall(e, 201, { registered: 1 });
const issued = await issueOrbEnrollment(e, 201);
expect(issued).toMatchObject({ enrollId: expect.stringMatching(/^orbenr_/), secret: expect.stringMatching(/^orbsec_/) });
const row = await db(e).prepare("SELECT state, installation_id, secret_hash FROM orb_enrollments WHERE installation_id=201").first<{ state: string; installation_id: number; secret_hash: string }>();
expect(row).toMatchObject({ state: "enrolled", installation_id: 201 });
expect(row?.secret_hash).not.toContain("orbsec"); // stored hashed, never plaintext
});
});

describe("brokerOrbToken", () => {
it("mints a token for a valid enrollment on a registered install (id bound server-side)", async () => {
const e = await brokerEnv();
await seedInstall(e, 300, { registered: 1 });
const { secret } = (await issueOrbEnrollment(e, 300)) as { secret: string };
tokenFetch("ghs_minted", "2026-06-25T08:00:00Z");
expect(await brokerOrbToken(e, secret)).toEqual({ token: "ghs_minted", installationId: 300, expiresAt: "2026-06-25T08:00:00Z" });
expect((await db(e).prepare("SELECT last_token_at FROM orb_enrollments WHERE installation_id=300").first<{ last_token_at: string | null }>())?.last_token_at).not.toBeNull();
});

it("rejects an unknown or revoked enrollment", async () => {
const e = await brokerEnv();
expect(await brokerOrbToken(e, "orbsec_bogus")).toEqual({ error: "invalid_enrollment" });
await seedInstall(e, 301, { registered: 1 });
const { secret } = (await issueOrbEnrollment(e, 301)) as { secret: string };
await db(e).prepare("UPDATE orb_enrollments SET revoked_at=CURRENT_TIMESTAMP WHERE installation_id=301").run();
expect(await brokerOrbToken(e, secret)).toEqual({ error: "invalid_enrollment" });
});

it("re-checks the install gate at mint time (unregistered / suspended / removed → not eligible)", async () => {
const e = await brokerEnv();
await seedInstall(e, 302, { registered: 1 });
const { secret } = (await issueOrbEnrollment(e, 302)) as { secret: string };
await db(e).prepare("UPDATE orb_github_installations SET suspended_at=CURRENT_TIMESTAMP WHERE installation_id=302").run();
expect(await brokerOrbToken(e, secret)).toEqual({ error: "installation_not_eligible" });
});
});

describe("broker endpoints", () => {
const app = createApp();
const auth = { authorization: "Bearer dev-internal-token" };

it("both routes 404 when the broker flag is off (byte-identical deploy)", async () => {
const off = createTestEnv({ INTERNAL_JOB_TOKEN: "dev-internal-token" });
expect((await app.request("/v1/orb/token", { method: "POST" }, off)).status).toBe(404);
expect((await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: "{}" }, off)).status).toBe(404);
});

it("the full operator-issue → container-exchange flow over HTTP", async () => {
const e = await brokerEnv();
await seedInstall(e, 400, { registered: 1 });
const issueRes = await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: JSON.stringify({ installationId: 400 }) }, e);
expect(issueRes.status).toBe(200);
const { secret } = (await issueRes.json()) as { secret: string };
tokenFetch("ghs_flow");
const tokRes = await app.request("/v1/orb/token", { method: "POST", headers: { authorization: `Bearer ${secret}` } }, e);
expect(tokRes.status).toBe(200);
expect(await tokRes.json()).toMatchObject({ token: "ghs_flow", installationId: 400 });
});

it("/v1/orb/token: 401 without a Bearer secret, 401 on a bad secret, 403 when the install became ineligible", async () => {
const e = await brokerEnv();
expect((await app.request("/v1/orb/token", { method: "POST" }, e)).status).toBe(401);
expect((await app.request("/v1/orb/token", { method: "POST", headers: { authorization: "Bearer orbsec_bad" } }, e)).status).toBe(401);
await seedInstall(e, 401, { registered: 1 });
const { secret } = (await issueOrbEnrollment(e, 401)) as { secret: string };
await db(e).prepare("UPDATE orb_github_installations SET registered=0 WHERE installation_id=401").run();
expect((await app.request("/v1/orb/token", { method: "POST", headers: { authorization: `Bearer ${secret}` } }, e)).status).toBe(403);
});

it("/v1/internal/orb/enrollments: 400 missing id, 409 unregistered, 404 unknown", async () => {
const e = await brokerEnv();
await seedInstall(e, 402, { registered: 0 });
expect((await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: "{}" }, e)).status).toBe(400);
expect((await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: "{bad" }, e)).status).toBe(400); // unparseable JSON → catch → null
expect((await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: JSON.stringify({ installationId: 402 }) }, e)).status).toBe(409);
expect((await app.request("/v1/internal/orb/enrollments", { method: "POST", headers: auth, body: JSON.stringify({ installationId: 999 }) }, e)).status).toBe(404);
});
});
Loading