@coffre/core 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -166,7 +166,7 @@ declare function assignableToEnvironment(role: Role): boolean;
166
166
  declare const INSTANCE_ROLES: {
167
167
  readonly member: {
168
168
  readonly name: "Member";
169
- readonly description: "Only what projects grant.";
169
+ readonly description: "None.";
170
170
  readonly permissions: readonly [];
171
171
  };
172
172
  readonly auditor: {
@@ -186,7 +186,7 @@ declare const INSTANCE_ROLES: {
186
186
  };
187
187
  readonly owner: {
188
188
  readonly name: "Owner";
189
- readonly description: "Everything an admin does, and read every secret.";
189
+ readonly description: "Everything, every secret included.";
190
190
  readonly permissions: readonly ["secret.read", "secret.write", "secret.archive", "audit.read", "environment.manage", "grant.manage", "project.manage"];
191
191
  };
192
192
  };
@@ -196,6 +196,16 @@ declare function isInstanceRole(value: string): value is InstanceRole;
196
196
  declare function instanceRoleGrants(role: InstanceRole, permission: Permission): boolean;
197
197
  /** The roles that manage people and access: Admin, and Owner, which adds every secret. */
198
198
  declare function administers(role: InstanceRole): boolean;
199
+ /**
200
+ * Whether a role and its scope make someone an Owner of the whole
201
+ * instance: the one standing the instance always keeps at least one of,
202
+ * and the one its bootstrap gives while it has none. An Owner scoped to
203
+ * `market` owns market, not the instance.
204
+ */
205
+ declare function ownsInstance(standing: {
206
+ role: InstanceRole;
207
+ scope: Scope;
208
+ }): boolean;
199
209
  /**
200
210
  * Which projects, or environments, a scope takes in: all of them, only
201
211
  * those listed, or all but those listed. Projects are listed by id, so a
@@ -303,8 +313,6 @@ declare function isCredentialScope(value: unknown): value is CredentialScope;
303
313
  * cannot drift apart.
304
314
  */
305
315
  type Holdings = {
306
- /** Named in the vault's configuration: everything, everywhere. */
307
- isRootAdmin: boolean;
308
316
  /** `member` for service accounts, and for anyone not active. */
309
317
  role: InstanceRole;
310
318
  scope: Scope;
@@ -342,8 +350,8 @@ declare function allows(holder: Holdings, permission: Permission, place: Place):
342
350
  */
343
351
  declare function mayManageAccess(actor: Holdings, place: Place): boolean;
344
352
  /**
345
- * Whether the holder runs the instance: a root admin, or an Admin or Owner
346
- * whose scope narrows nothing. They add and remove people, set instance
353
+ * Whether the holder runs the instance: an Admin or Owner whose scope
354
+ * narrows nothing. They add and remove people, set instance
347
355
  * roles, and change the instance's settings; they read its whole log too
348
356
  * (`readsWholeLog`). A scoped Admin manages only inside its scope, so it
349
357
  * does none of these.
@@ -404,7 +412,7 @@ declare function issuesCredential(holder: Holdings, setting: ServiceSetting | nu
404
412
  * Whether the holder reads the whole log, the instance's own entries
405
413
  * (sign-ins, people, service accounts, settings) with every project's, and
406
414
  * verifies it: whoever holds `audit.read` by an instance role whose scope
407
- * narrows nothing, an Auditor's included, and a root admin. Reading, not
415
+ * narrows nothing, an Auditor's included. Reading, not
408
416
  * running: an Auditor of the whole instance changes nothing it reads about.
409
417
  * Anyone else reads the places they hold `audit.read` in.
410
418
  */
@@ -417,4 +425,4 @@ declare function readsWholeLog(holder: Holdings): boolean;
417
425
  */
418
426
  declare function makesProjects(holder: Holdings): boolean;
419
427
  //#endregion
420
- export { instanceRoleGrants as A, readsWholeLog as B, allows as C, givesService as D, credentialScopeInWords as E, issuesCredential as F, unscoped as G, runsInstance as H, makesProjects as I, ConvertedGrant as J, Conversion as K, managesService as L, isInstanceRole as M, isRole as N, grantKind as O, isScope as P, mayManageAccess as R, admits as S, covers as T, scopeInWords as U, roleGrants as V, setsUpServices as W, Narrowed as X, EveryProjectGrant as Y, convertEveryProjectGrants as Z, Role as _, Holdings as a, ServiceSetting as b, InstanceRole as c, PERMISSIONS as d, PROJECT_ONLY_PERMISSIONS as f, ROLE_NAMES as g, ROLES as h, GrantPlace as i, isCredentialScope as j, inScope as k, MAX_SCOPE_PLACES as l, Place as m, EVERYWHERE as n, INSTANCE_ROLES as o, Permission as p, ConversionInput as q, Filter as r, INSTANCE_ROLE_NAMES as s, CredentialScope as t, NOTHING as u, Scope as v, assignableToEnvironment as w, administers as x, ServiceAccount as y, normalScope as z };
428
+ export { instanceRoleGrants as A, ownsInstance as B, allows as C, givesService as D, credentialScopeInWords as E, issuesCredential as F, setsUpServices as G, roleGrants as H, makesProjects as I, ConversionInput as J, unscoped as K, managesService as L, isInstanceRole as M, isRole as N, grantKind as O, isScope as P, convertEveryProjectGrants as Q, mayManageAccess as R, admits as S, covers as T, runsInstance as U, readsWholeLog as V, scopeInWords as W, EveryProjectGrant as X, ConvertedGrant as Y, Narrowed as Z, Role as _, Holdings as a, ServiceSetting as b, InstanceRole as c, PERMISSIONS as d, PROJECT_ONLY_PERMISSIONS as f, ROLE_NAMES as g, ROLES as h, GrantPlace as i, isCredentialScope as j, inScope as k, MAX_SCOPE_PLACES as l, Place as m, EVERYWHERE as n, INSTANCE_ROLES as o, Permission as p, Conversion as q, Filter as r, INSTANCE_ROLE_NAMES as s, CredentialScope as t, NOTHING as u, Scope as v, assignableToEnvironment as w, administers as x, ServiceAccount as y, normalScope as z };
@@ -137,7 +137,6 @@ function byRole(role, scope, grant) {
137
137
  function holdsAlready(role, scope, held, grant, at) {
138
138
  const lasting = held.filter((other) => outlasts(other.expiresAt, grant.expiresAt));
139
139
  return ROLES[grant.role].permissions.every((permission) => allows({
140
- isRootAdmin: false,
141
140
  role,
142
141
  scope,
143
142
  grants: lasting
@@ -241,7 +240,7 @@ function assignableToEnvironment(role) {
241
240
  const INSTANCE_ROLES = {
242
241
  member: {
243
242
  name: "Member",
244
- description: "Only what projects grant.",
243
+ description: "None.",
245
244
  permissions: []
246
245
  },
247
246
  auditor: {
@@ -266,7 +265,7 @@ const INSTANCE_ROLES = {
266
265
  },
267
266
  owner: {
268
267
  name: "Owner",
269
- description: "Everything an admin does, and read every secret.",
268
+ description: "Everything, every secret included.",
270
269
  permissions: [...PERMISSIONS]
271
270
  }
272
271
  };
@@ -281,6 +280,15 @@ function instanceRoleGrants(role, permission) {
281
280
  function administers(role) {
282
281
  return role === "admin" || role === "owner";
283
282
  }
283
+ /**
284
+ * Whether a role and its scope make someone an Owner of the whole
285
+ * instance: the one standing the instance always keeps at least one of,
286
+ * and the one its bootstrap gives while it has none. An Owner scoped to
287
+ * `market` owns market, not the instance.
288
+ */
289
+ function ownsInstance(standing) {
290
+ return standing.role === "owner" && unscoped(standing.scope);
291
+ }
284
292
  /** Every project and every environment. */
285
293
  const EVERYWHERE = {
286
294
  projects: "all",
@@ -387,7 +395,6 @@ function isCredentialScope(value) {
387
395
  }
388
396
  /** Someone who holds nothing: a stranger, a removed member, a tampered row. */
389
397
  const NOTHING = {
390
- isRootAdmin: false,
391
398
  role: "member",
392
399
  scope: EVERYWHERE,
393
400
  grants: []
@@ -416,7 +423,6 @@ function covers(grant, place) {
416
423
  */
417
424
  function allows(holder, permission, place) {
418
425
  if (holder.credentialScope != null && !holdsAt(holder.credentialScope, permission, place)) return false;
419
- if (holder.isRootAdmin) return true;
420
426
  if (instanceRoleGrants(holder.role, permission) && inScope(holder.scope, place)) return true;
421
427
  return holdsAt(holder.grants, permission, place);
422
428
  }
@@ -434,14 +440,14 @@ function mayManageAccess(actor, place) {
434
440
  return allows(actor, "grant.manage", place);
435
441
  }
436
442
  /**
437
- * Whether the holder runs the instance: a root admin, or an Admin or Owner
438
- * whose scope narrows nothing. They add and remove people, set instance
443
+ * Whether the holder runs the instance: an Admin or Owner whose scope
444
+ * narrows nothing. They add and remove people, set instance
439
445
  * roles, and change the instance's settings; they read its whole log too
440
446
  * (`readsWholeLog`). A scoped Admin manages only inside its scope, so it
441
447
  * does none of these.
442
448
  */
443
449
  function runsInstance(holder) {
444
- return holder.isRootAdmin || administers(holder.role) && unscoped(holder.scope);
450
+ return administers(holder.role) && unscoped(holder.scope);
445
451
  }
446
452
  /**
447
453
  * Whether `holder` may give a service account `role` at `place`: where
@@ -500,12 +506,12 @@ function issuesCredential(holder, setting, actor, account, scope) {
500
506
  * Whether the holder reads the whole log, the instance's own entries
501
507
  * (sign-ins, people, service accounts, settings) with every project's, and
502
508
  * verifies it: whoever holds `audit.read` by an instance role whose scope
503
- * narrows nothing, an Auditor's included, and a root admin. Reading, not
509
+ * narrows nothing, an Auditor's included. Reading, not
504
510
  * running: an Auditor of the whole instance changes nothing it reads about.
505
511
  * Anyone else reads the places they hold `audit.read` in.
506
512
  */
507
513
  function readsWholeLog(holder) {
508
- return holder.isRootAdmin || instanceRoleGrants(holder.role, "audit.read") && unscoped(holder.scope);
514
+ return instanceRoleGrants(holder.role, "audit.read") && unscoped(holder.scope);
509
515
  }
510
516
  /**
511
517
  * Whether the holder may make a project: an Admin or Owner whose scope
@@ -514,9 +520,8 @@ function readsWholeLog(holder) {
514
520
  * hold the project around them.
515
521
  */
516
522
  function makesProjects(holder) {
517
- if (holder.isRootAdmin) return true;
518
523
  const { projects, environments } = holder.scope;
519
524
  return administers(holder.role) && environments === "all" && (projects === "all" || "except" in projects);
520
525
  }
521
526
  //#endregion
522
- export { roleGrants as A, isScope as C, mayManageAccess as D, managesService as E, convertEveryProjectGrants as F, scopeInWords as M, setsUpServices as N, normalScope as O, unscoped as P, isRole as S, makesProjects as T, grantKind as _, NOTHING as a, isCredentialScope as b, ROLES as c, admits as d, allows as f, givesService as g, credentialScopeInWords as h, MAX_SCOPE_PLACES as i, runsInstance as j, readsWholeLog as k, ROLE_NAMES as l, covers as m, INSTANCE_ROLES as n, PERMISSIONS as o, assignableToEnvironment as p, INSTANCE_ROLE_NAMES as r, PROJECT_ONLY_PERMISSIONS as s, EVERYWHERE as t, administers as u, inScope as v, issuesCredential as w, isInstanceRole as x, instanceRoleGrants as y };
527
+ export { readsWholeLog as A, isScope as C, mayManageAccess as D, managesService as E, unscoped as F, convertEveryProjectGrants as I, runsInstance as M, scopeInWords as N, normalScope as O, setsUpServices as P, isRole as S, makesProjects as T, grantKind as _, NOTHING as a, isCredentialScope as b, ROLES as c, admits as d, allows as f, givesService as g, credentialScopeInWords as h, MAX_SCOPE_PLACES as i, roleGrants as j, ownsInstance as k, ROLE_NAMES as l, covers as m, INSTANCE_ROLES as n, PERMISSIONS as o, assignableToEnvironment as p, INSTANCE_ROLE_NAMES as r, PROJECT_ONLY_PERMISSIONS as s, EVERYWHERE as t, administers as u, inScope as v, issuesCredential as w, isInstanceRole as x, instanceRoleGrants as y };
package/dist/access.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as instanceRoleGrants, B as readsWholeLog, C as allows, D as givesService, E as credentialScopeInWords, F as issuesCredential, G as unscoped, H as runsInstance, I as makesProjects, J as ConvertedGrant, K as Conversion, L as managesService, M as isInstanceRole, N as isRole, O as grantKind, P as isScope, R as mayManageAccess, S as admits, T as covers, U as scopeInWords, V as roleGrants, W as setsUpServices, X as Narrowed, Y as EveryProjectGrant, Z as convertEveryProjectGrants, _ as Role, a as Holdings, b as ServiceSetting, c as InstanceRole, d as PERMISSIONS, f as PROJECT_ONLY_PERMISSIONS, g as ROLE_NAMES, h as ROLES, i as GrantPlace, j as isCredentialScope, k as inScope, l as MAX_SCOPE_PLACES, m as Place, n as EVERYWHERE, o as INSTANCE_ROLES, p as Permission, q as ConversionInput, r as Filter, s as INSTANCE_ROLE_NAMES, t as CredentialScope, u as NOTHING, v as Scope, w as assignableToEnvironment, x as administers, y as ServiceAccount, z as normalScope } from "./access-CwsFLfTz.js";
2
- export { type Conversion, type ConversionInput, type ConvertedGrant, CredentialScope, EVERYWHERE, type EveryProjectGrant, Filter, GrantPlace, Holdings, INSTANCE_ROLES, INSTANCE_ROLE_NAMES, InstanceRole, MAX_SCOPE_PLACES, NOTHING, type Narrowed, PERMISSIONS, PROJECT_ONLY_PERMISSIONS, Permission, Place, ROLES, ROLE_NAMES, Role, Scope, ServiceAccount, ServiceSetting, administers, admits, allows, assignableToEnvironment, convertEveryProjectGrants, covers, credentialScopeInWords, givesService, grantKind, inScope, instanceRoleGrants, isCredentialScope, isInstanceRole, isRole, isScope, issuesCredential, makesProjects, managesService, mayManageAccess, normalScope, readsWholeLog, roleGrants, runsInstance, scopeInWords, setsUpServices, unscoped };
1
+ import { A as instanceRoleGrants, B as ownsInstance, C as allows, D as givesService, E as credentialScopeInWords, F as issuesCredential, G as setsUpServices, H as roleGrants, I as makesProjects, J as ConversionInput, K as unscoped, L as managesService, M as isInstanceRole, N as isRole, O as grantKind, P as isScope, Q as convertEveryProjectGrants, R as mayManageAccess, S as admits, T as covers, U as runsInstance, V as readsWholeLog, W as scopeInWords, X as EveryProjectGrant, Y as ConvertedGrant, Z as Narrowed, _ as Role, a as Holdings, b as ServiceSetting, c as InstanceRole, d as PERMISSIONS, f as PROJECT_ONLY_PERMISSIONS, g as ROLE_NAMES, h as ROLES, i as GrantPlace, j as isCredentialScope, k as inScope, l as MAX_SCOPE_PLACES, m as Place, n as EVERYWHERE, o as INSTANCE_ROLES, p as Permission, q as Conversion, r as Filter, s as INSTANCE_ROLE_NAMES, t as CredentialScope, u as NOTHING, v as Scope, w as assignableToEnvironment, x as administers, y as ServiceAccount, z as normalScope } from "./access-BpcBLDW9.js";
2
+ export { type Conversion, type ConversionInput, type ConvertedGrant, CredentialScope, EVERYWHERE, type EveryProjectGrant, Filter, GrantPlace, Holdings, INSTANCE_ROLES, INSTANCE_ROLE_NAMES, InstanceRole, MAX_SCOPE_PLACES, NOTHING, type Narrowed, PERMISSIONS, PROJECT_ONLY_PERMISSIONS, Permission, Place, ROLES, ROLE_NAMES, Role, Scope, ServiceAccount, ServiceSetting, administers, admits, allows, assignableToEnvironment, convertEveryProjectGrants, covers, credentialScopeInWords, givesService, grantKind, inScope, instanceRoleGrants, isCredentialScope, isInstanceRole, isRole, isScope, issuesCredential, makesProjects, managesService, mayManageAccess, normalScope, ownsInstance, readsWholeLog, roleGrants, runsInstance, scopeInWords, setsUpServices, unscoped };
package/dist/access.js CHANGED
@@ -1,2 +1,2 @@
1
- import { A as roleGrants, C as isScope, D as mayManageAccess, E as managesService, F as convertEveryProjectGrants, M as scopeInWords, N as setsUpServices, O as normalScope, P as unscoped, S as isRole, T as makesProjects, _ as grantKind, a as NOTHING, b as isCredentialScope, c as ROLES, d as admits, f as allows, g as givesService, h as credentialScopeInWords, i as MAX_SCOPE_PLACES, j as runsInstance, k as readsWholeLog, l as ROLE_NAMES, m as covers, n as INSTANCE_ROLES, o as PERMISSIONS, p as assignableToEnvironment, r as INSTANCE_ROLE_NAMES, s as PROJECT_ONLY_PERMISSIONS, t as EVERYWHERE, u as administers, v as inScope, w as issuesCredential, x as isInstanceRole, y as instanceRoleGrants } from "./access-pnJ-u4x2.js";
2
- export { EVERYWHERE, INSTANCE_ROLES, INSTANCE_ROLE_NAMES, MAX_SCOPE_PLACES, NOTHING, PERMISSIONS, PROJECT_ONLY_PERMISSIONS, ROLES, ROLE_NAMES, administers, admits, allows, assignableToEnvironment, convertEveryProjectGrants, covers, credentialScopeInWords, givesService, grantKind, inScope, instanceRoleGrants, isCredentialScope, isInstanceRole, isRole, isScope, issuesCredential, makesProjects, managesService, mayManageAccess, normalScope, readsWholeLog, roleGrants, runsInstance, scopeInWords, setsUpServices, unscoped };
1
+ import { A as readsWholeLog, C as isScope, D as mayManageAccess, E as managesService, F as unscoped, I as convertEveryProjectGrants, M as runsInstance, N as scopeInWords, O as normalScope, P as setsUpServices, S as isRole, T as makesProjects, _ as grantKind, a as NOTHING, b as isCredentialScope, c as ROLES, d as admits, f as allows, g as givesService, h as credentialScopeInWords, i as MAX_SCOPE_PLACES, j as roleGrants, k as ownsInstance, l as ROLE_NAMES, m as covers, n as INSTANCE_ROLES, o as PERMISSIONS, p as assignableToEnvironment, r as INSTANCE_ROLE_NAMES, s as PROJECT_ONLY_PERMISSIONS, t as EVERYWHERE, u as administers, v as inScope, w as issuesCredential, x as isInstanceRole, y as instanceRoleGrants } from "./access-DvwZzqRR.js";
2
+ export { EVERYWHERE, INSTANCE_ROLES, INSTANCE_ROLE_NAMES, MAX_SCOPE_PLACES, NOTHING, PERMISSIONS, PROJECT_ONLY_PERMISSIONS, ROLES, ROLE_NAMES, administers, admits, allows, assignableToEnvironment, convertEveryProjectGrants, covers, credentialScopeInWords, givesService, grantKind, inScope, instanceRoleGrants, isCredentialScope, isInstanceRole, isRole, isScope, issuesCredential, makesProjects, managesService, mayManageAccess, normalScope, ownsInstance, readsWholeLog, roleGrants, runsInstance, scopeInWords, setsUpServices, unscoped };
@@ -1,5 +1,120 @@
1
1
  import { BindingClaims, BindingInvalid, BindingPolicy, ClaimValue, EVENT_EXPOSURE, GITHUB_EVENTS, GITHUB_ISSUER, GITLAB_ISSUER, GITLAB_PIPELINE_SOURCES, MAX_BINDINGS, MAX_CLAIMS, MAX_CLAIM_BYTES, REQUIRED_CLAIMS, Starts, WORKLOAD_PROFILES, WorkloadProfile, canonicalClaims, checkBinding, checkFetchUrl, claimValues, defaultIssuer, differingClaims, fullRef, githubEventsAt, githubReusable, githubWorkflow, gitlabProject, workflowRef } from "./workloads.js";
2
2
  import { JSONWebKeySet, JWTPayload } from "jose";
3
+ //#region src/identity/github-app.d.ts
4
+ /**
5
+ * The deployment's own GitHub App, made once with `githubApp(…)` and passed
6
+ * to each side that uses it (docs/github-app.md):
7
+ *
8
+ * const app = githubApp({ appId, privateKey, clientId, clientSecret });
9
+ * signin({ providers: [github({ app })], workloads: { limits, github: app } })
10
+ *
11
+ * Sign-in uses its OAuth client, `clientId` and `clientSecret`, as it would
12
+ * an OAuth app's (`github({ app })`). The forge side, `workloads.github`,
13
+ * uses its ID and private key: it lists the repositories the app is
14
+ * installed on, private ones too, where an owner trusts a workflow, and
15
+ * resolves `owner/name` to the IDs a binding names, with Metadata:
16
+ * read-only, so what it reaches there is the names and IDs of those
17
+ * repositories. Either side may go without the other: `github({ clientId,
18
+ * clientSecret })` signs in alone, and an app with no client lists alone.
19
+ *
20
+ * The app's private key lives with the app, the rest of the deployment's
21
+ * configuration, not in the vault: it opens no secret, and the vault's
22
+ * isolation is for the keys that do. It and the client secret stay in this
23
+ * object's private fields, and no message, log line or answer carries them,
24
+ * the JWTs it signs or the installation tokens they buy.
25
+ *
26
+ * The object lives as long as its configuration: the isolate's, on Workers,
27
+ * or the process's. It keeps settled values only (AGENTS.md, "What an
28
+ * isolate keeps"): the imported key, the app's name, and each
29
+ * installation's token until shortly before it expires. Concurrent first
30
+ * calls each import and ask for their own.
31
+ */
32
+ export type GitHubAppOptions = {
33
+ /** The app's ID, on its settings page ("App ID"): a number, not its client ID. */
34
+ appId: string | number;
35
+ /** A private key generated on its settings page: the PEM file, as downloaded. */
36
+ privateKey: string;
37
+ /** Its OAuth client, on the same page: for signing people in with it, `github({ app })`. Both, or neither. */
38
+ clientId?: string;
39
+ clientSecret?: string;
40
+ /** https://github.com, or a GitHub Enterprise Server host: where people sign in. */
41
+ webUrl?: string;
42
+ /**
43
+ * https://api.github.com, or `${host}/api/v3` on Enterprise Server. The
44
+ * forge side takes github.com's only, or the dev IdP's on loopback: an
45
+ * Enterprise Server's runs sign their tokens under another issuer than
46
+ * the one its lookups bind to.
47
+ */
48
+ apiUrl?: string;
49
+ };
50
+ /** A repository, by the IDs a binding names: what `GET /workloads/lookup` answers. */
51
+ export type GitHubRepository = {
52
+ github: string;
53
+ repositoryId: string;
54
+ ownerId: string;
55
+ private: boolean;
56
+ };
57
+ /** What the app reaches: its name and where to install it, the accounts it is installed on, and their repositories. */
58
+ export type GitHubAppRepositories = {
59
+ app: {
60
+ name: string;
61
+ installUrl: string;
62
+ };
63
+ /** The accounts it is installed on, by login. */
64
+ installations: string[];
65
+ /** By name. */
66
+ repositories: GitHubRepository[];
67
+ /** More than one listing reaches: the others are typed by name. */
68
+ truncated: boolean;
69
+ };
70
+ /** GitHub could not be asked, or refused the app: the message says which, for a person, and carries no credential. */
71
+ export declare class GitHubAppUnavailable extends Error {
72
+ constructor(message: string, options?: {
73
+ cause?: unknown;
74
+ });
75
+ }
76
+ /** Requests one listing makes at most, tokens included: well under a Worker's subrequest limit. */
77
+ export declare const MAX_LISTING_REQUESTS = 40;
78
+ /** A token GitHub issues for an hour is used until this long before it ends, so that none expires mid-call. */
79
+ export declare const TOKEN_MARGIN_MS: number;
80
+ export declare class GitHubApp {
81
+ #private;
82
+ readonly appId: string;
83
+ readonly webUrl: string;
84
+ readonly apiUrl: string;
85
+ /** Its OAuth client's ID, which is no secret; null when it was given none. */
86
+ readonly clientId: string | null;
87
+ constructor(options: {
88
+ appId: string;
89
+ apiUrl: string;
90
+ webUrl?: string;
91
+ pkcs8: Uint8Array<ArrayBuffer>;
92
+ client?: {
93
+ clientId: string;
94
+ clientSecret: string;
95
+ };
96
+ }, io?: {
97
+ fetch?: typeof fetch;
98
+ now?: () => number;
99
+ });
100
+ /** Its OAuth client, for `github({ app })`; null when it was given none. */
101
+ signinClient(): {
102
+ clientId: string;
103
+ clientSecret: string;
104
+ } | null;
105
+ /** Every repository of every account the app is installed on, as many as `MAX_LISTING_REQUESTS` reach. */
106
+ repositories(): Promise<GitHubAppRepositories>;
107
+ /** `owner/name`'s IDs, when the app is installed on it; null when it is not, its installation is suspended, or GitHub knows no such repository. */
108
+ repository(fullName: string): Promise<GitHubRepository | null>;
109
+ }
110
+ /**
111
+ * A private key's PEM as PKCS #8, which Web Crypto imports: GitHub's
112
+ * download is PKCS #1 (`BEGIN RSA PRIVATE KEY`), wrapped here as an RSA
113
+ * key's PKCS #8. Escaped newlines, as an environment variable sometimes
114
+ * holds them, are newlines. Never says what the key was.
115
+ */
116
+ export declare function pkcs8Of(pem: string): Uint8Array<ArrayBuffer>;
117
+ //#endregion
3
118
  //#region src/identity/signin/types.d.ts
4
119
  /**
5
120
  * What a provider reports about the person who just signed in.
@@ -216,6 +331,15 @@ export type WorkloadsOptions = {
216
331
  * loopback too, so no instance anyone else can reach ever trusts one.
217
332
  */
218
333
  allowLoopbackIssuersForDevelopment?: boolean;
334
+ /**
335
+ * The deployment's own GitHub App, `githubApp(…)`, installed on its
336
+ * organizations: where an owner trusts a workflow, the pages list the
337
+ * repositories it is installed on, private ones too, and a repository
338
+ * typed by name resolves through it (docs/github-app.md). The same value
339
+ * may sign people in, `github({ app })`. Left out, repositories are typed,
340
+ * and only public ones' IDs are looked up.
341
+ */
342
+ github?: GitHubApp;
219
343
  };
220
344
  /** Cloudflare's rate-limiting binding, as coffre calls it; `processLimits()` has the same shape. */
221
345
  export type RateLimiter = {
@@ -232,6 +356,7 @@ export type WorkloadLimits = {
232
356
  export type WorkloadsConfig = {
233
357
  allowLoopback: boolean;
234
358
  limits: WorkloadLimits;
359
+ github: GitHubApp | null;
235
360
  };
236
361
  /**
237
362
  * MCP clients, Claude among them, acting as the people who connect them,
@@ -303,14 +428,32 @@ export declare function microsoft(options: Credentials & {
303
428
  label?: string;
304
429
  tenant: string;
305
430
  }): OidcSigninProvider;
306
- /** GitHub accounts; with `organization`, only that organization's members. */
307
- export declare function github(options: Credentials & {
431
+ /**
432
+ * GitHub accounts; with `organization`, only that organization's members.
433
+ * Through an OAuth app, or a GitHub App's OAuth client, by its credentials;
434
+ * or the deployment's GitHub App, `github({ app })`, the same value its
435
+ * `workloads.github` lists repositories with:
436
+ *
437
+ * const app = githubApp({ appId, privateKey, clientId, clientSecret });
438
+ * signin({ providers: [github({ app, organization: 'acme' })], workloads: { limits, github: app } })
439
+ */
440
+ export declare function github(options: {
308
441
  id?: string;
309
442
  label?: string;
310
443
  organization?: string;
444
+ } & ((Credentials & {
311
445
  webUrl?: string;
312
446
  apiUrl?: string;
313
- }): GitHubSigninProvider;
447
+ }) | {
448
+ app: GitHubApp;
449
+ })): GitHubSigninProvider;
450
+ /**
451
+ * The deployment's GitHub App, checked where it is written: its numeric ID
452
+ * and private key, and its OAuth client when it signs people in too. One
453
+ * value, passed to each side that uses it: `github({ app })` and
454
+ * `workloads.github` (`../github-app.ts`).
455
+ */
456
+ export declare function githubApp(options: GitHubAppOptions): GitHubApp;
314
457
  export declare function publicOrigin(value: string): string;
315
458
  /** Sign-in configuration, checked: at least one provider, each whole, ids unique, lifetimes sane. */
316
459
  export declare function defineSignin(options: {