@ionite/core 0.0.24-beta.20261003.17 → 0.0.24-beta.20261006.21

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.
package/dist/server.d.ts CHANGED
@@ -9,205 +9,54 @@ type NoReserved<
9
9
  > = TAttributes extends object ? [TFullShapeKeys] extends [never] ? TAttributes & { [TKey in Extract<keyof TAttributes, TReserved>]? : never } : [TFullShapeKeys] extends [keyof TAttributes] ? Omit<TAttributes, TReserved> : TAttributes & { [TKey in Extract<keyof TAttributes, TReserved>]? : never } : TAttributes;
10
10
  import { StandardSchemaV1 as StandardSchemaV18 } from "@standard-schema/spec";
11
11
  /**
12
- * Third-party CIAM provider presets and claim mapping.
12
+ * ConfigSource-only block that lets an issuing IdP's token claims satisfy `has*` checks.
13
+ * Placed independently on `sso`, each CIAM provider, and each incoming workload IdP.
14
+ * Absent means that issuer's claims never satisfy entitlements.
13
15
  *
14
- * Defines the built-in provider adapters used by the CIAM module for customer
15
- * login. Each preset supplies default OIDC/OAuth2 endpoints, scope, branding,
16
- * and a claim mapper that normalizes provider claims into a candidate customer.
17
- * The application's `CustomerStore` remains the system of record: the SDK only
18
- * extracts standard claims and hands a candidate to `lookup`/`upsert`.
16
+ * Derived rows are session-scoped for users and customers (expanded onto the session at login
17
+ * and refresh) and per-request for workloads. They are never written to the access stores.
19
18
  */
20
- /** Built-in provider types. `oidc` is a generic OIDC provider; `magic-link` is the passwordless flow. */
21
- type CIAMProviderType = "oidc" | "google" | "github" | "microsoft" | "apple" | "magic-link";
22
- /** Branding colors for rendering a provider button in light and dark themes. */
23
- type CIAMProviderColors = {
24
- /** Background/brand color for light theme (hex or CSS color). */
25
- light: string;
26
- /** Background/brand color for dark theme (hex or CSS color). */
27
- dark: string;
28
- /** Optional secondary/accent color. */
29
- secondary?: string;
30
- };
31
- /** Operator-supplied branding so the UI can render a provider list without hardcoding. */
32
- type CIAMProviderBranding = {
33
- /** Human-readable name shown on the login button (e.g. "Google"). */
34
- displayName: string;
35
- /** External icon URL (typically an SVG/PNG hosted on a CDN). */
36
- iconUrl?: string;
37
- /** Light/dark brand colors. */
38
- colors?: CIAMProviderColors;
39
- /** Optional provider info/marketing URL. */
40
- url?: string;
41
- };
42
- /**
43
- * A single CIAM provider declaration. OIDC/OAuth2 endpoint and credential
44
- * fields come from the ConfigSource (never application code).
45
- */
46
- type CIAMProviderConfig = {
47
- /** Provider adapter type. Selects defaults and claim mapping. */
48
- type: CIAMProviderType;
49
- /** Whether this provider is enabled. Defaults to true when omitted. */
50
- enabled?: boolean;
51
- /** Recommended display order (ascending). Ties broken by declaration order. */
52
- order?: number;
53
- /** UI branding metadata. */
54
- branding?: CIAMProviderBranding;
55
- /** OIDC issuer/authority (used for discovery when explicit endpoints are omitted). */
56
- authority?: string;
57
- /** Authorization endpoint (overrides preset). */
58
- authorizationUrl?: string;
59
- /** Token endpoint (overrides preset). */
60
- tokenUrl?: string;
61
- /** JWKS endpoint for ID token verification (overrides preset). */
62
- jwksUri?: string;
63
- /** OAuth2 UserInfo endpoint for providers without an ID token (e.g. GitHub). */
64
- userInfoUrl?: string;
65
- /** OAuth2 client id (ConfigSource-only). */
66
- clientId?: string;
67
- /** OAuth2 client secret (ConfigSource-only). */
68
- clientSecret?: string;
69
- /** Requested scope (overrides preset). */
70
- scope?: string;
71
- /** Apple: team id for client-secret generation. */
72
- teamId?: string;
73
- /** Apple: key id for client-secret generation. */
74
- keyId?: string;
75
- /** Apple: PKCS#8 PEM private key for client-secret generation. */
76
- privateKey?: string;
77
- };
78
- /** Sanitized provider entry returned by the discovery endpoint for the UI. */
79
- type CIAMProviderDescriptor = {
80
- /** Provider key (matches the RemoteConfig map key); used as `?provider=`. */
81
- name: string;
82
- type: CIAMProviderType;
83
- displayName: string;
84
- /** SDK-derived login URL to navigate to (begins the flow). */
85
- loginUrl: string;
86
- iconUrl?: string;
87
- colors?: CIAMProviderColors;
88
- url?: string;
89
- };
90
- /** Normalized claim candidate produced by a provider claim mapper. */
91
- type CiamClaimCandidate = {
92
- /** Provider-native subject identifier. */
93
- sub: string;
94
- email?: string;
95
- name?: string;
96
- userName?: string;
97
- avatar?: string;
98
- };
99
- /** How a provider conveys identity after token exchange. */
100
- type CIAMProviderFlow = "id_token" | "userinfo";
101
- type CIAMProviderPreset = {
102
- flow: CIAMProviderFlow;
103
- authorizationUrl?: string;
104
- tokenUrl?: string;
105
- jwksUri?: string;
106
- userInfoUrl?: string;
107
- scope: string;
108
- branding: CIAMProviderBranding;
109
- /** Extra authorization query parameters (e.g. Apple `response_mode`). */
110
- extraAuthParams?: Record<string, string>;
111
- mapClaims: (claims: Record<string, unknown>) => CiamClaimCandidate;
112
- };
113
- declare const CIAM_PROVIDER_PRESETS: Record<Exclude<CIAMProviderType, "magic-link">, CIAMProviderPreset>;
114
- /** Returns the preset for a provider type, or undefined for `magic-link`. */
115
- declare function getProviderPreset(type: CIAMProviderType): CIAMProviderPreset | undefined;
116
- /** Effective provider settings after merging preset defaults with config overrides. */
117
- type ResolvedCiamProvider = {
118
- name: string;
119
- type: CIAMProviderType;
120
- flow: CIAMProviderFlow;
121
- authorizationUrl?: string;
122
- tokenUrl?: string;
123
- jwksUri?: string;
124
- userInfoUrl?: string;
125
- scope: string;
126
- clientId?: string;
127
- clientSecret?: string;
128
- extraAuthParams?: Record<string, string>;
129
- branding: CIAMProviderBranding;
130
- mapClaims: (claims: Record<string, unknown>) => CiamClaimCandidate;
131
- config: CIAMProviderConfig;
132
- };
133
- /**
134
- * Merge a provider config with its preset to produce effective OIDC/OAuth2
135
- * settings and branding. Returns undefined for `magic-link` (handled separately).
136
- */
137
- declare function resolveCiamProvider(name: string, config: CIAMProviderConfig): ResolvedCiamProvider | undefined;
138
- /** Effective branding for a provider (preset defaults merged with config overrides). */
139
- declare function resolveProviderBranding(config: CIAMProviderConfig): CIAMProviderBranding;
140
- /**
141
- * Order and filter providers for display. Enabled providers only, sorted by
142
- * `order` (ascending), with declaration order as the tie-breaker. This is the
143
- * recommended presentation order returned by the discovery endpoint.
144
- */
145
- declare function orderedEnabledProviders(providers: Record<string, CIAMProviderConfig> | undefined): Array<{
146
- name: string;
147
- config: CIAMProviderConfig;
148
- }>;
149
- declare const DISPLAY_COOKIE_VERSION = 1;
150
- type DisplayCookieConfig = {
19
+ type ClaimsAsEntitlements = {
151
20
  /**
152
- * When true, the SDK writes a non-HttpOnly display cookie beside the opaque session cookie.
153
- * ConfigSource-only. Default false.
21
+ * Claim names to read. A dotted path addresses a nested claim (`realm_access.roles`).
22
+ * `scope` is space-delimited; every other string claim is one value; arrays contribute each string.
23
+ * Required and non-empty.
154
24
  */
155
- enabled?: boolean;
25
+ claims: string[];
156
26
  /**
157
- * How many display cookies a single response may leave on the browser, newest session window
158
- * first. ConfigSource-only. Values below 1 fall back to {@link DEFAULT_DISPLAY_COOKIE_MAX}.
159
- * @default 10
27
+ * Expand a matched catalog role or group into the permissions it confers.
28
+ * Default false: a claim value matches only the catalog id it names.
160
29
  */
161
- max?: number;
30
+ expand?: boolean;
162
31
  /**
163
- * Session-less tenant hints written by an App after an explicit directory lookup.
164
- * ConfigSource-only and off unless `enabled` is true.
32
+ * Let catalog-bound resources on expanded rows satisfy resource-scoped checks.
33
+ * Default false. Ignored unless `expand` is true: a claim string cannot name a resource.
165
34
  */
166
- discovered?: DiscoveredDisplayCookieConfig;
35
+ resources?: boolean;
167
36
  };
168
- type DiscoveredDisplayCookieConfig = {
169
- enabled?: boolean;
170
- /** Browser hint lifetime. This is not a session expiry. @default 14400 */
171
- ttlSeconds?: number;
172
- /** Independent cap; discovered hints never displace live-session entries. @default 10 */
173
- max?: number;
37
+ /** One effective entitlement derived from a token claim. Not a stored grant. */
38
+ type DerivedEntitlement = {
39
+ targetType: OaaAccessTargetType;
40
+ target: string;
41
+ /** Set only when the plane opted into `resources` and the catalog row binds this resource. */
42
+ resource?: string;
43
+ /** Claim name the value was read from. */
44
+ claim: string;
45
+ /** Expansion root id when the row came from catalog expansion. Absent for a direct id match. */
46
+ root?: string;
174
47
  };
175
- /** What `${basePath}/auth/user` and `${basePath}/auth/customer` return. ConfigSource-only. */
176
- type UserEndpointPolicy = "full" | "display" | "off";
177
- type DisplayUser = Record<string, string | number | boolean | null>;
178
- type DisplayAuthType = "sso" | "ciam";
179
- type DisplayCookieEnvelope = {
180
- v: typeof DISPLAY_COOKIE_VERSION;
48
+ /** Secret-free projection of a configured claims block, including whether the App can persist it. */
49
+ type ClaimsAsEntitlementsDescription = {
50
+ claims: string[];
51
+ expand: boolean;
52
+ resources: boolean;
181
53
  /**
182
- * Session envelopes omit this field (absent means `'session'`) so the wire format stays identical
183
- * to cookies written before discovered hints existed; only discovered hints set it explicitly.
184
- * Readers must treat `undefined` as `'session'`.
54
+ * User and customer planes: the session store declares it persists `Session.entitlements`.
55
+ * Workload plane: always true (nothing is stored).
185
56
  */
186
- kind?: "session" | "discovered";
187
- tenantId?: string;
188
- authType: DisplayAuthType;
189
- /** ISO timestamp copied from the session window at write time. Display only. */
190
- expiresAt: string;
191
- user: DisplayUser;
192
- tenantName?: string;
193
- };
194
- type ParsedDisplayCookie = {
195
- name: string;
196
- prefix: string;
197
- envelope: DisplayCookieEnvelope;
57
+ capable: boolean;
198
58
  };
199
59
  /**
200
- * Minimal logger interface compatible with common structured logging patterns.
201
- */
202
- interface Logger {
203
- trace(message?: unknown, ...optionalParams: unknown[]): void;
204
- debug(message?: unknown, ...optionalParams: unknown[]): void;
205
- info(message?: unknown, ...optionalParams: unknown[]): void;
206
- warn(message?: unknown, ...optionalParams: unknown[]): void;
207
- error(message?: unknown, ...optionalParams: unknown[]): void;
208
- }
209
- import { StandardSchemaV1 as StandardSchemaV15 } from "@standard-schema/spec";
210
- /**
211
60
  * Shared minimal identity fields used to build both the workforce user
212
61
  * record (`BaseUser`) and the CIAM customer record (`BaseCustomer`), as well
213
62
  * as the authenticated session types (`WorkforceUser`, `Customer`).
@@ -963,11 +812,10 @@ type WorkloadIdentity = {
963
812
  /** Granted OAuth scopes (space-delimited). */
964
813
  scope?: string;
965
814
  /**
966
- * Whether the trusted IdP that validated this token opts into honoring its OAuth `scope` claim as
967
- * entitlements (per-IdP intent, resolved at validation time). Consumed by the request-level `has*`
968
- * helpers; `undefined` falls back to the global `RemoteConfig.authz.scopesAsEntitlements`.
815
+ * Claim-derived entitlements for this request. Present only when the matched incoming IdP
816
+ * configured `claimsAsEntitlements`. Never persisted.
969
817
  */
970
- scopesAsEntitlements?: boolean;
818
+ derivedEntitlements?: DerivedEntitlement[];
971
819
  /**
972
820
  * Enterprise Controlled ID for this workload (`ecid:<realm>:<opaque>`), when carried on the token
973
821
  * `ecid` claim or enriched from a wired `WorkloadIdentityStore`. A workload is its own governance
@@ -1409,6 +1257,12 @@ interface WorkloadIdentityStore<TExtended = object> {
1409
1257
  * policy (flag, auto-revoke + notify, or simply audit).
1410
1258
  */
1411
1259
  type IamDeltaMode = "patch";
1260
+ /** ConfigSource-owned authenticated destination for the AccessRequests protocol. */
1261
+ type AccessRequestsDestination = {
1262
+ url: string;
1263
+ /** Named outgoing workload client. Omit to use the default workload server identity. */
1264
+ audience?: string;
1265
+ };
1412
1266
  type AccessRequestCreate = {
1413
1267
  subject: string;
1414
1268
  subjectType: SubjectType;
@@ -1489,13 +1343,6 @@ interface IamDeltaEmitter {
1489
1343
  /** Re-push the complete IAM-visible snapshot to the destination (app-driven full reconcile). */
1490
1344
  reconcile(input: IamDeltaReconcileInput): Promise<IamDeltaReconcileResult>;
1491
1345
  /**
1492
- * Submit an on-behalf access request at `${iam.url}/AccessRequests`. Requires only the
1493
- * destination channel (`url` plus the `deltaAudience` workload client) — not `isReady()`, which
1494
- * additionally demands delta egress (a per-kind `'patch'` mode and its receipt stores). A tenant
1495
- * whose IAM aggregates by pull alone may still submit requests.
1496
- */
1497
- requestAccess(input: AccessRequestCreate): Promise<AccessRequestResult>;
1498
- /**
1499
1346
  * Read a previously captured destination reference without making a network request. Returns
1500
1347
  * undefined until the resource has been resolved or pushed to the configured IAM destination.
1501
1348
  * `correlationId` is a User's `externalId` or an entitlement's App-owned catalog `id`.
@@ -1504,84 +1351,601 @@ interface IamDeltaEmitter {
1504
1351
  isReady(): boolean;
1505
1352
  ready(timeout?: number): Promise<void>;
1506
1353
  }
1507
- import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
1508
- type ChangeListener = () => void;
1354
+ /** Built-in provider types. `oidc` is a generic OIDC provider; `magic-link` is the passwordless flow. */
1355
+ type CIAMProviderType = "oidc" | "google" | "github" | "microsoft" | "apple" | "magic-link";
1356
+ /** Branding colors for rendering a provider button in light and dark themes. */
1357
+ type CIAMProviderColors = {
1358
+ /** Background/brand color for light theme (hex or CSS color). */
1359
+ light: string;
1360
+ /** Background/brand color for dark theme (hex or CSS color). */
1361
+ dark: string;
1362
+ /** Optional secondary/accent color. */
1363
+ secondary?: string;
1364
+ };
1365
+ /** Operator-supplied branding so the UI can render a provider list without hardcoding. */
1366
+ type CIAMProviderBranding = {
1367
+ /** Human-readable name shown on the login button (e.g. "Google"). */
1368
+ displayName: string;
1369
+ /** External icon URL (typically an SVG/PNG hosted on a CDN). */
1370
+ iconUrl?: string;
1371
+ /** Light/dark brand colors. */
1372
+ colors?: CIAMProviderColors;
1373
+ /** Optional provider info/marketing URL. */
1374
+ url?: string;
1375
+ };
1509
1376
  /**
1510
- * Reactive source/adapter contract. Implemented by config-source-backed adapters
1511
- * (for example `SecretsSource`) to advertise change subscription and source liveness.
1512
- * `isAvailable()` here is an adapter liveness hook and is intentionally distinct from
1513
- * module readiness (`ModuleReadiness.isReady()` / `ready()`).
1377
+ * A single CIAM provider declaration. OIDC/OAuth2 endpoint and credential
1378
+ * fields come from the ConfigSource (never application code).
1514
1379
  */
1515
- type ReactiveHandle = {
1516
- beforeChange?(listener: ChangeListener): () => void;
1517
- afterChange?(listener: ChangeListener): () => void;
1518
- isAvailable?(): boolean;
1380
+ type CIAMProviderConfig = {
1381
+ /** Provider adapter type. Selects defaults and claim mapping. */
1382
+ type: CIAMProviderType;
1383
+ /** Whether this provider is enabled. Defaults to true when omitted. */
1384
+ enabled?: boolean;
1385
+ /** Recommended display order (ascending). Ties broken by declaration order. */
1386
+ order?: number;
1387
+ /** UI branding metadata. */
1388
+ branding?: CIAMProviderBranding;
1389
+ /** OIDC issuer/authority (used for discovery when explicit endpoints are omitted). */
1390
+ authority?: string;
1391
+ /** Authorization endpoint (overrides preset). */
1392
+ authorizationUrl?: string;
1393
+ /** Token endpoint (overrides preset). */
1394
+ tokenUrl?: string;
1395
+ /** JWKS endpoint for ID token verification (overrides preset). */
1396
+ jwksUri?: string;
1397
+ /** OAuth2 UserInfo endpoint for providers without an ID token (e.g. GitHub). */
1398
+ userInfoUrl?: string;
1399
+ /** OAuth2 client id (ConfigSource-only). */
1400
+ clientId?: string;
1401
+ /** OAuth2 client secret (ConfigSource-only). */
1402
+ clientSecret?: string;
1403
+ /** Requested scope (overrides preset). */
1404
+ scope?: string;
1405
+ /** Apple: team id for client-secret generation. */
1406
+ teamId?: string;
1407
+ /** Apple: key id for client-secret generation. */
1408
+ keyId?: string;
1409
+ /** Apple: PKCS#8 PEM private key for client-secret generation. */
1410
+ privateKey?: string;
1411
+ /**
1412
+ * ConfigSource-only. When set, this provider's token claims are derived into session-scoped
1413
+ * customer entitlements at login. Magic link has no issuer and ignores this block.
1414
+ */
1415
+ claimsAsEntitlements?: ClaimsAsEntitlements;
1416
+ /** ConfigSource-only IdP assignment workflow used when IAM assignment is `'idp'`. */
1417
+ accessRequests?: AccessRequestsDestination;
1418
+ };
1419
+ /** Sanitized provider entry returned by the discovery endpoint for the UI. */
1420
+ type CIAMProviderDescriptor = {
1421
+ /** Provider key (matches the RemoteConfig map key); used as `?provider=`. */
1422
+ name: string;
1423
+ type: CIAMProviderType;
1424
+ displayName: string;
1425
+ /** SDK-derived login URL to navigate to (begins the flow). */
1426
+ loginUrl: string;
1427
+ iconUrl?: string;
1428
+ colors?: CIAMProviderColors;
1429
+ url?: string;
1430
+ };
1431
+ /** Normalized claim candidate produced by a provider claim mapper. */
1432
+ type CiamClaimCandidate = {
1433
+ /** Provider-native subject identifier. */
1434
+ sub: string;
1435
+ email?: string;
1436
+ name?: string;
1437
+ userName?: string;
1438
+ avatar?: string;
1439
+ };
1440
+ /** How a provider conveys identity after token exchange. */
1441
+ type CIAMProviderFlow = "id_token" | "userinfo";
1442
+ type CIAMProviderPreset = {
1443
+ flow: CIAMProviderFlow;
1444
+ authorizationUrl?: string;
1445
+ tokenUrl?: string;
1446
+ jwksUri?: string;
1447
+ userInfoUrl?: string;
1448
+ scope: string;
1449
+ branding: CIAMProviderBranding;
1450
+ /** Extra authorization query parameters (e.g. Apple `response_mode`). */
1451
+ extraAuthParams?: Record<string, string>;
1452
+ mapClaims: (claims: Record<string, unknown>) => CiamClaimCandidate;
1453
+ };
1454
+ declare const CIAM_PROVIDER_PRESETS: Record<Exclude<CIAMProviderType, "magic-link">, CIAMProviderPreset>;
1455
+ /** Returns the preset for a provider type, or undefined for `magic-link`. */
1456
+ declare function getProviderPreset(type: CIAMProviderType): CIAMProviderPreset | undefined;
1457
+ /** Effective provider settings after merging preset defaults with config overrides. */
1458
+ type ResolvedCiamProvider = {
1459
+ name: string;
1460
+ type: CIAMProviderType;
1461
+ flow: CIAMProviderFlow;
1462
+ authorizationUrl?: string;
1463
+ tokenUrl?: string;
1464
+ jwksUri?: string;
1465
+ userInfoUrl?: string;
1466
+ scope: string;
1467
+ clientId?: string;
1468
+ clientSecret?: string;
1469
+ extraAuthParams?: Record<string, string>;
1470
+ branding: CIAMProviderBranding;
1471
+ mapClaims: (claims: Record<string, unknown>) => CiamClaimCandidate;
1472
+ config: CIAMProviderConfig;
1473
+ };
1474
+ /**
1475
+ * Merge a provider config with its preset to produce effective OIDC/OAuth2
1476
+ * settings and branding. Returns undefined for `magic-link` (handled separately).
1477
+ */
1478
+ declare function resolveCiamProvider(name: string, config: CIAMProviderConfig): ResolvedCiamProvider | undefined;
1479
+ /** Effective branding for a provider (preset defaults merged with config overrides). */
1480
+ declare function resolveProviderBranding(config: CIAMProviderConfig): CIAMProviderBranding;
1481
+ /**
1482
+ * Order and filter providers for display. Enabled providers only, sorted by
1483
+ * `order` (ascending), with declaration order as the tie-breaker. This is the
1484
+ * recommended presentation order returned by the discovery endpoint.
1485
+ */
1486
+ declare function orderedEnabledProviders(providers: Record<string, CIAMProviderConfig> | undefined): Array<{
1487
+ name: string;
1488
+ config: CIAMProviderConfig;
1489
+ }>;
1490
+ declare const DISPLAY_COOKIE_VERSION = 1;
1491
+ type DisplayCookieConfig = {
1492
+ /**
1493
+ * When true, the SDK writes a non-HttpOnly display cookie beside the opaque session cookie.
1494
+ * ConfigSource-only. Default false.
1495
+ */
1496
+ enabled?: boolean;
1497
+ /**
1498
+ * How many display cookies a single response may leave on the browser, newest session window
1499
+ * first. ConfigSource-only. Values below 1 fall back to {@link DEFAULT_DISPLAY_COOKIE_MAX}.
1500
+ * @default 10
1501
+ */
1502
+ max?: number;
1503
+ /**
1504
+ * Session-less tenant hints written by an App after an explicit directory lookup.
1505
+ * ConfigSource-only and off unless `enabled` is true.
1506
+ */
1507
+ discovered?: DiscoveredDisplayCookieConfig;
1508
+ };
1509
+ type DiscoveredDisplayCookieConfig = {
1510
+ enabled?: boolean;
1511
+ /** Browser hint lifetime. This is not a session expiry. @default 14400 */
1512
+ ttlSeconds?: number;
1513
+ /** Independent cap; discovered hints never displace live-session entries. @default 10 */
1514
+ max?: number;
1515
+ };
1516
+ /** What `${basePath}/auth/user` and `${basePath}/auth/customer` return. ConfigSource-only. */
1517
+ type UserEndpointPolicy = "full" | "display" | "off";
1518
+ type DisplayUser = Record<string, string | number | boolean | null>;
1519
+ type DisplayAuthType = "sso" | "ciam";
1520
+ type DisplayCookieEnvelope = {
1521
+ v: typeof DISPLAY_COOKIE_VERSION;
1522
+ /**
1523
+ * Session envelopes omit this field (absent means `'session'`) so the wire format stays identical
1524
+ * to cookies written before discovered hints existed; only discovered hints set it explicitly.
1525
+ * Readers must treat `undefined` as `'session'`.
1526
+ */
1527
+ kind?: "session" | "discovered";
1528
+ tenantId?: string;
1529
+ authType: DisplayAuthType;
1530
+ /** ISO timestamp copied from the session window at write time. Display only. */
1531
+ expiresAt: string;
1532
+ user: DisplayUser;
1533
+ tenantName?: string;
1534
+ };
1535
+ type ParsedDisplayCookie = {
1536
+ name: string;
1537
+ prefix: string;
1538
+ envelope: DisplayCookieEnvelope;
1539
+ };
1540
+ /**
1541
+ * Minimal logger interface compatible with common structured logging patterns.
1542
+ */
1543
+ interface Logger {
1544
+ trace(message?: unknown, ...optionalParams: unknown[]): void;
1545
+ debug(message?: unknown, ...optionalParams: unknown[]): void;
1546
+ info(message?: unknown, ...optionalParams: unknown[]): void;
1547
+ warn(message?: unknown, ...optionalParams: unknown[]): void;
1548
+ error(message?: unknown, ...optionalParams: unknown[]): void;
1549
+ }
1550
+ import { StandardSchemaV1 as StandardSchemaV15 } from "@standard-schema/spec";
1551
+ type ChangeListener = () => void;
1552
+ /**
1553
+ * Reactive source/adapter contract. Implemented by config-source-backed adapters
1554
+ * (for example `SecretsSource`) to advertise change subscription and source liveness.
1555
+ * `isAvailable()` here is an adapter liveness hook and is intentionally distinct from
1556
+ * module readiness (`ModuleReadiness.isReady()` / `ready()`).
1557
+ */
1558
+ type ReactiveHandle = {
1559
+ beforeChange?(listener: ChangeListener): () => void;
1560
+ afterChange?(listener: ChangeListener): () => void;
1561
+ isAvailable?(): boolean;
1562
+ };
1563
+ /**
1564
+ * Readiness contract for reactive module/service handles whose usability depends on
1565
+ * RemoteConfig that can arrive or change at runtime (for example `ion.secrets`,
1566
+ * `ion.workload`, `ion.otel`, `ion.authz.oaa`).
1567
+ *
1568
+ * - `isReady()` is a synchronous check; use it in request handlers and health probes and
1569
+ * return 503 (or branch) when false instead of blocking.
1570
+ * - `ready(timeout?)` resolves once the handle first becomes usable; use it at startup or
1571
+ * as a feature gate. With no timeout it waits indefinitely; with `timeout > 0` it rejects
1572
+ * after that many milliseconds.
1573
+ */
1574
+ type ModuleReadiness = {
1575
+ beforeChange?(listener: ChangeListener): () => void;
1576
+ afterChange?(listener: ChangeListener): () => void;
1577
+ isReady?(): boolean;
1578
+ ready?(timeout?: number): Promise<void>;
1579
+ };
1580
+ /**
1581
+ * Canonical ionite module names used by the holistic readiness report and the
1582
+ * `requireReady([...])` helper. These are the config-driven module handles whose readiness can be
1583
+ * composed; `ion.isReady()` itself stays the narrow "config applied" signal and is NOT one of these.
1584
+ *
1585
+ * IAM-delta egress is intentionally NOT a member. Delta patch-back is best-effort and non-fatal (a
1586
+ * failed push only logs `degraded` and is reconciled by a later full sync — the Batch 7 drift-signal
1587
+ * contract), so folding it into `iam` readiness would let a delta-target outage make
1588
+ * `requireReady(['iam'])` false and take the app out of rotation for a signal that is designed not to
1589
+ * block. Apps that DO treat delta as a hard dependency can gate on `ion.iam.delta.isReady()` /
1590
+ * `ion.iam.delta.ready()` explicitly instead.
1591
+ */
1592
+ type ModuleName = "sso" | "ciam" | "iam" | "workload" | "otel" | "secrets" | "kms" | "authz" | "tm";
1593
+ /** Per-module readiness snapshot in {@link ReadinessReport}. */
1594
+ type ModuleReadinessState = {
1595
+ /** Whether the module is active (present/requested) in the effective config. Inactive modules never block readiness. */
1596
+ active: boolean;
1597
+ /** Synchronous readiness of the module right now (mirrors the module's own `isReady()`). */
1598
+ ready: boolean;
1599
+ /** Human-readable explanation when the module is `active` but not `ready`. */
1600
+ reason?: string;
1601
+ };
1602
+ /**
1603
+ * Holistic, read-only readiness snapshot for an ionite instance. Returned by
1604
+ * `ion.readiness()` and used by the built-in `${basePath}/readyz` endpoint. This is composition
1605
+ * DATA, not a request gate: applications combine it (via `ion.requireReady([...])`) with their own
1606
+ * non-ionite dependency checks to build their health endpoints.
1607
+ */
1608
+ type ReadinessReport = {
1609
+ /** True when config has been applied (`applied`) and every ACTIVE module is ready. */
1610
+ ok: boolean;
1611
+ /** Whether RemoteConfig has been applied at least once (mirrors `ion.isReady()`). */
1612
+ applied: boolean;
1613
+ /** Per-module readiness keyed by {@link ModuleName}. */
1614
+ modules: Record<ModuleName, ModuleReadinessState>;
1615
+ /** Most recent config-application outcome (last applied time and last error, if any). */
1616
+ config: ConfigApplyStatus;
1617
+ };
1618
+ /** Result of `ion.requireReady(names)`: a synchronous, declarative readiness gate over selected modules. */
1619
+ type RequireReadyResult = {
1620
+ /** True when config is applied and every requested module is ready. */
1621
+ ok: boolean;
1622
+ /** The requested modules that are not ready, with reasons when available. */
1623
+ notReady: Array<{
1624
+ name: ModuleName;
1625
+ reason?: string;
1626
+ }>;
1627
+ };
1628
+ type CapabilityEndpointUrls = {
1629
+ readyzUrl: string;
1630
+ capabilitiezUrl: string;
1631
+ sso: {
1632
+ loginUrl: string;
1633
+ userUrl: string;
1634
+ logoutUrl: string;
1635
+ logoutBackChannelUrl: string;
1636
+ refreshUrl: string;
1637
+ callbackUrl: string;
1638
+ };
1639
+ workload: {
1640
+ validateUrl: string;
1641
+ jwksUrl: string;
1642
+ };
1643
+ iam: {
1644
+ basePath: string;
1645
+ usersUrl: string;
1646
+ groupsUrl: string;
1647
+ workloadsUrl: string;
1648
+ customersUrl: string;
1649
+ };
1650
+ ciam: {
1651
+ magicLinkUrl: string;
1652
+ magicLinkLoginUrl: string;
1653
+ customerUrl: string;
1654
+ customerLoginUrl: string;
1655
+ customerCallbackUrl: string;
1656
+ customerProvidersUrl: string;
1657
+ logoutUrl: string;
1658
+ };
1659
+ };
1660
+ type OtelProtocol = "http/protobuf" | "http/json";
1661
+ type OtelProviderType = "otlp" | "grafana-lgtm" | "datadog" | "splunk" | "elastic";
1662
+ type OtelLogLevel = "trace" | "debug" | "info" | "warn" | "error";
1663
+ type OtelLevels = Partial<Record<OtelLogLevel, boolean>>;
1664
+ /** Local console mirror policy: disable it or set its minimum emitted severity. */
1665
+ type OtelConsoleMode = "off" | OtelLogLevel;
1666
+ type OtelAttributeValue = string | number | boolean | string[] | number[] | boolean[];
1667
+ type OtelAttributes = Record<string, OtelAttributeValue>;
1668
+ type OtelSignalConfig = {
1669
+ /** Per-signal OTLP endpoint. When omitted, the module appends /v1/{signal} to `endpoint`. */
1670
+ endpoint?: string;
1671
+ headers?: Record<string, string>;
1672
+ timeoutMillis?: number;
1673
+ concurrencyLimit?: number;
1674
+ };
1675
+ type OtelMetricsSignalConfig = OtelSignalConfig & {
1676
+ exportIntervalMillis?: number;
1677
+ exportTimeoutMillis?: number;
1678
+ };
1679
+ type OtelOAuthClientCredentialsConfig = {
1680
+ tokenUrl: string;
1681
+ clientId: string;
1682
+ clientSecret: string;
1683
+ scope?: string;
1684
+ audience?: string;
1685
+ headers?: Record<string, string>;
1686
+ };
1687
+ /**
1688
+ * OpenTelemetry config supplied by ConfigSource/RemoteConfig.
1689
+ * Keep environment-specific URLs and credentials here, not in FrameworkConfig.
1690
+ */
1691
+ type OtelConfig = {
1692
+ type?: OtelProviderType;
1693
+ name: string;
1694
+ endpoint?: string;
1695
+ protocol?: OtelProtocol;
1696
+ headers?: Record<string, string>;
1697
+ apiKey?: string;
1698
+ /** Name of a `RemoteConfig.kms` key pool used for exporter authentication. */
1699
+ apiKeyPool?: string;
1700
+ apiKeyHeader?: string;
1701
+ oauth?: OtelOAuthClientCredentialsConfig;
1702
+ version?: string;
1703
+ deploymentEnvironment?: string;
1704
+ resourceAttributes?: OtelAttributes;
1705
+ levels?: OtelLevels;
1706
+ /** `'off'` exports only to OTel; a level dual-emits at or above that severity. Defaults to `'info'`. */
1707
+ console?: OtelConsoleMode;
1708
+ traces?: boolean | OtelSignalConfig;
1709
+ metrics?: boolean | OtelMetricsSignalConfig;
1710
+ logs?: boolean | OtelSignalConfig;
1711
+ };
1712
+ type Otel = {
1713
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
1714
+ describe(): OtelDescription;
1715
+ beforeChange(listener: ChangeListener): () => void;
1716
+ afterChange(listener: ChangeListener): () => void;
1717
+ isReady(): boolean;
1718
+ ready(timeout?: number): Promise<void>;
1719
+ logger: Logger;
1720
+ forceFlush(): Promise<void>;
1721
+ shutdown(): Promise<void>;
1722
+ };
1723
+ type ModuleDescriptionBase<TModule extends ModuleName> = {
1724
+ module: TModule;
1725
+ active: boolean;
1726
+ ready: boolean;
1727
+ };
1728
+ type CookieDescription = {
1729
+ prefix?: string;
1730
+ path?: string;
1731
+ secure?: boolean;
1732
+ sameSite?: "Strict" | "Lax";
1733
+ domain?: string;
1734
+ };
1735
+ type SsoDescription = ModuleDescriptionBase<"sso"> & {
1736
+ authority?: string;
1737
+ tokenUrl?: string;
1738
+ authorizationUrl?: string;
1739
+ clientId?: string;
1740
+ redirectUri?: string;
1741
+ responseType?: "code";
1742
+ scope?: string;
1743
+ silentRedirectUri?: string;
1744
+ jwksUri?: string;
1745
+ endSessionEndpoint?: string;
1746
+ revocationEndpoint?: string;
1747
+ cookies: CookieDescription;
1748
+ loginStateTtl?: number;
1749
+ rotationGraceSeconds?: number;
1750
+ sessionWindowSeconds?: number;
1751
+ displayCookie?: DisplayCookieConfig;
1752
+ jit: {
1753
+ enabled: boolean;
1754
+ userStore: boolean;
1755
+ };
1756
+ sessionStore: boolean;
1757
+ /** Present when RemoteConfig configured claim-derived entitlements for this workforce issuer. */
1758
+ claimsAsEntitlements?: ClaimsAsEntitlementsDescription;
1759
+ urls: Partial<CapabilityEndpointUrls["sso"]>;
1760
+ };
1761
+ type CiamProviderDescription = {
1762
+ name: string;
1763
+ type: CIAMProviderType;
1764
+ enabled: boolean;
1765
+ order?: number;
1766
+ branding?: CIAMProviderBranding;
1767
+ authority?: string;
1768
+ authorizationUrl?: string;
1769
+ tokenUrl?: string;
1770
+ jwksUri?: string;
1771
+ userInfoUrl?: string;
1772
+ clientId?: string;
1773
+ scope?: string;
1774
+ claimsAsEntitlements?: ClaimsAsEntitlementsDescription;
1775
+ loginUrl: string;
1776
+ };
1777
+ type CiamDescription = ModuleDescriptionBase<"ciam"> & {
1778
+ magicLinkTtl?: number;
1779
+ loginStateTtl?: number;
1780
+ sessionTtl?: number;
1781
+ cookies: CookieDescription;
1782
+ displayCookie?: DisplayCookieConfig;
1783
+ jit: {
1784
+ enabled: boolean;
1785
+ customerStore: boolean;
1786
+ };
1787
+ sessionStore: boolean;
1788
+ magicLinkStore: boolean;
1789
+ providers: CiamProviderDescription[];
1790
+ urls: Partial<CapabilityEndpointUrls["ciam"]>;
1791
+ };
1792
+ type IamPrincipalDescription = {
1793
+ enabled: boolean;
1794
+ assignment: IamAssignmentPolicy;
1795
+ delta?: "patch";
1796
+ storeWired: boolean;
1519
1797
  };
1520
- /**
1521
- * Readiness contract for reactive module/service handles whose usability depends on
1522
- * RemoteConfig that can arrive or change at runtime (for example `ion.secrets`,
1523
- * `ion.workload`, `ion.otel`, `ion.authz.oaa`).
1524
- *
1525
- * - `isReady()` is a synchronous check; use it in request handlers and health probes and
1526
- * return 503 (or branch) when false instead of blocking.
1527
- * - `ready(timeout?)` resolves once the handle first becomes usable; use it at startup or
1528
- * as a feature gate. With no timeout it waits indefinitely; with `timeout > 0` it rejects
1529
- * after that many milliseconds.
1530
- */
1531
- type ModuleReadiness = {
1532
- beforeChange?(listener: ChangeListener): () => void;
1533
- afterChange?(listener: ChangeListener): () => void;
1534
- isReady?(): boolean;
1535
- ready?(timeout?: number): Promise<void>;
1798
+ type AccessRequestsChannelsDescription = {
1799
+ iam?: string;
1800
+ sso?: string;
1801
+ ciam: Record<string, string>;
1536
1802
  };
1537
- /**
1538
- * Canonical ionite module names used by the holistic readiness report and the
1539
- * `requireReady([...])` helper. These are the config-driven module handles whose readiness can be
1540
- * composed; `ion.isReady()` itself stays the narrow "config applied" signal and is NOT one of these.
1541
- *
1542
- * IAM-delta egress is intentionally NOT a member. Delta patch-back is best-effort and non-fatal (a
1543
- * failed push only logs `degraded` and is reconciled by a later full sync — the Batch 7 drift-signal
1544
- * contract), so folding it into `iam` readiness would let a delta-target outage make
1545
- * `requireReady(['iam'])` false and take the app out of rotation for a signal that is designed not to
1546
- * block. Apps that DO treat delta as a hard dependency can gate on `ion.iam.delta.isReady()` /
1547
- * `ion.iam.delta.ready()` explicitly instead.
1548
- */
1549
- type ModuleName = "sso" | "ciam" | "iam" | "workload" | "otel" | "secrets" | "kms" | "authz" | "tm";
1550
- /** Per-module readiness snapshot in {@link ReadinessReport}. */
1551
- type ModuleReadinessState = {
1552
- /** Whether the module is active (present/requested) in the effective config. Inactive modules never block readiness. */
1553
- active: boolean;
1554
- /** Synchronous readiness of the module right now (mirrors the module's own `isReady()`). */
1803
+ type IamDescription = ModuleDescriptionBase<"iam"> & {
1804
+ url?: string;
1805
+ deltaAudience?: string;
1806
+ accessRequests: AccessRequestsChannelsDescription;
1807
+ users: IamPrincipalDescription;
1808
+ workloads: IamPrincipalDescription;
1809
+ customers: IamPrincipalDescription;
1810
+ groups: {
1811
+ enabled: boolean;
1812
+ catalogWired: boolean;
1813
+ };
1814
+ urls: Partial<CapabilityEndpointUrls["iam"]>;
1815
+ };
1816
+ type WorkloadTrustedIdpDescription = {
1817
+ issuer: string;
1818
+ jwksUri: string;
1819
+ audience: string | string[];
1820
+ claimsAsEntitlements?: ClaimsAsEntitlementsDescription;
1821
+ };
1822
+ type WorkloadClientDescription = {
1823
+ name: string;
1555
1824
  ready: boolean;
1556
- /** Human-readable explanation when the module is `active` but not `ready`. */
1557
- reason?: string;
1825
+ clientId?: string;
1826
+ workloadId?: string;
1827
+ idpTokenUrl?: string;
1828
+ scope?: string;
1829
+ audience?: string;
1830
+ authMethod: "client_secret" | "private_key_jwt" | "server_only";
1558
1831
  };
1559
- /**
1560
- * Holistic, read-only readiness snapshot for an ionite instance. Returned by
1561
- * `ion.readiness()` and used by the built-in `${basePath}/readyz` endpoint. This is composition
1562
- * DATA, not a request gate: applications combine it (via `ion.requireReady([...])`) with their own
1563
- * non-ionite dependency checks to build their health endpoints.
1564
- */
1565
- type ReadinessReport = {
1566
- /** True when config has been applied (`applied`) and every ACTIVE module is ready. */
1832
+ type WorkloadDescription = ModuleDescriptionBase<"workload"> & {
1833
+ incoming: {
1834
+ trustedIdps: WorkloadTrustedIdpDescription[];
1835
+ };
1836
+ outgoing: WorkloadClientDescription[];
1837
+ urls: Partial<CapabilityEndpointUrls["workload"]>;
1838
+ };
1839
+ type SecretsSourceDescription = {
1840
+ name: string;
1841
+ type: "dev" | "gcp" | "vault" | "aws" | "azure";
1842
+ ready: boolean;
1843
+ url?: string;
1844
+ clientId?: string;
1845
+ authMethod?: string;
1846
+ };
1847
+ type SecretsDescription = ModuleDescriptionBase<"secrets"> & {
1848
+ sources: SecretsSourceDescription[];
1849
+ };
1850
+ type KmsPoolDescription = {
1851
+ name: string;
1852
+ source?: string;
1853
+ path: string;
1854
+ ready: boolean;
1855
+ };
1856
+ type KmsDescription = ModuleDescriptionBase<"kms"> & {
1857
+ pools: KmsPoolDescription[];
1858
+ };
1859
+ type OtelSignalDescription = {
1860
+ enabled: boolean;
1861
+ endpoint?: string;
1862
+ timeoutMillis?: number;
1863
+ concurrencyLimit?: number;
1864
+ exportIntervalMillis?: number;
1865
+ exportTimeoutMillis?: number;
1866
+ };
1867
+ type OtelDescription = ModuleDescriptionBase<"otel"> & {
1868
+ type?: OtelProviderType;
1869
+ name?: string;
1870
+ endpoint?: string;
1871
+ protocol?: OtelProtocol;
1872
+ apiKeyPool?: string;
1873
+ apiKeyHeader?: string;
1874
+ oauth?: {
1875
+ tokenUrl: string;
1876
+ clientId: string;
1877
+ scope?: string;
1878
+ audience?: string;
1879
+ };
1880
+ version?: string;
1881
+ deploymentEnvironment?: string;
1882
+ resourceAttributes?: OtelAttributes;
1883
+ levels?: OtelLevels;
1884
+ console?: OtelConsoleMode;
1885
+ signals: {
1886
+ traces: OtelSignalDescription;
1887
+ metrics: OtelSignalDescription;
1888
+ logs: OtelSignalDescription;
1889
+ };
1890
+ };
1891
+ type OaaDescription = {
1892
+ ready: boolean;
1893
+ url?: string;
1894
+ providerId?: string;
1895
+ datasourceId?: string;
1896
+ applicationName?: string;
1897
+ applicationType?: string;
1898
+ population?: Record<string, string>;
1899
+ fullSync: boolean;
1900
+ };
1901
+ type AuthzDescription = ModuleDescriptionBase<"authz"> & {
1902
+ trustedProvisioningClients: Array<{
1903
+ issuer: string;
1904
+ audience: string | string[];
1905
+ clientId: string;
1906
+ permissions: string[];
1907
+ }>;
1908
+ oaa?: OaaDescription;
1909
+ /** True when the App explicitly accepts IAM-sourced entitlement creation. */
1910
+ inboundEntitlements: boolean;
1911
+ stores: {
1912
+ accessStore: boolean;
1913
+ accessIndex: boolean;
1914
+ catalogStore: boolean;
1915
+ resources: boolean;
1916
+ };
1917
+ };
1918
+ type TmDescription = ModuleDescriptionBase<"tm"> & {
1919
+ enabled: boolean;
1920
+ ionite?: {
1921
+ id: string;
1922
+ name?: string;
1923
+ url: string;
1924
+ issuer: string;
1925
+ jwksUri: string;
1926
+ audience: string;
1927
+ clientId: string;
1928
+ };
1929
+ consentTtlSeconds?: number;
1930
+ maxRedeemAttempts?: number;
1931
+ };
1932
+ type ModuleDescription = SsoDescription | CiamDescription | IamDescription | WorkloadDescription | SecretsDescription | KmsDescription | OtelDescription | AuthzDescription | TmDescription;
1933
+ type IoniteDescription = {
1934
+ id?: string;
1935
+ sdk: {
1936
+ version: string;
1937
+ catalogVersion: number;
1938
+ };
1939
+ basePath: string;
1940
+ urls: CapabilityEndpointUrls;
1567
1941
  ok: boolean;
1568
- /** Whether RemoteConfig has been applied at least once (mirrors `ion.isReady()`). */
1569
1942
  applied: boolean;
1570
- /** Per-module readiness keyed by {@link ModuleName}. */
1571
- modules: Record<ModuleName, ModuleReadinessState>;
1572
- /** Most recent config-application outcome (last applied time and last error, if any). */
1573
1943
  config: ConfigApplyStatus;
1574
- };
1575
- /** Result of `ion.requireReady(names)`: a synchronous, declarative readiness gate over selected modules. */
1576
- type RequireReadyResult = {
1577
- /** True when config is applied and every requested module is ready. */
1578
- ok: boolean;
1579
- /** The requested modules that are not ready, with reasons when available. */
1580
- notReady: Array<{
1581
- name: ModuleName;
1582
- reason?: string;
1944
+ modules: Record<ModuleName, ModuleReadinessState & {
1945
+ description?: ModuleDescription;
1583
1946
  }>;
1584
1947
  };
1948
+ import { StandardSchemaV1 as StandardSchemaV14 } from "@standard-schema/spec";
1585
1949
  /**
1586
1950
  * Token caching for workload identity authentication.
1587
1951
  *
@@ -1772,12 +2136,10 @@ type WorkloadConfigBase = {
1772
2136
  */
1773
2137
  audience?: string;
1774
2138
  /**
1775
- * Per-IdP intent (incoming/server role only): when `true`, tokens validated against THIS trust
1776
- * anchor have their OAuth `scope` claim auto-honored as entitlements by the request-level `has*`
1777
- * helpers (audience-bound). Mirrors `RemoteConfig.authz.scopesAsEntitlements` but scoped to a single
1778
- * trusted IdP so first-party scopes can be honored while federated/third-party ones are not.
2139
+ * Per-IdP intent (incoming/server role only): token claims from THIS trust anchor may satisfy
2140
+ * `has*` (audience-bound). ConfigSource-only. There is no global fallback.
1779
2141
  */
1780
- scopesAsEntitlements?: boolean;
2142
+ claimsAsEntitlements?: ClaimsAsEntitlements;
1781
2143
  /**
1782
2144
  * Default OAuth2 scopes (space-delimited)
1783
2145
  */
@@ -1887,21 +2249,10 @@ type ClientCredentialsWorkloadConfig = WorkloadConfigBase & {
1887
2249
  */
1888
2250
  clientId: string;
1889
2251
  /**
1890
- * OAuth2 client secret. Omit it to authenticate with a federated token instead
1891
- * (see `federatedTokenFile`). A client secret wins when both are present.
1892
- */
1893
- clientSecret?: string;
1894
- /**
1895
- * Path to a projected federated token (e.g. AKS Workload Identity), sent as a jwt-bearer
1896
- * `client_assertion` in place of `client_secret`. Defaults to `AZURE_FEDERATED_TOKEN_FILE`.
1897
- * Read on every acquisition because the platform rotates it.
1898
- */
1899
- federatedTokenFile?: string;
1900
- /**
1901
- * `managed_identity`: get the token from the platform's managed identity endpoint instead of an
1902
- * IdP token URL. `clientId` is the managed identity's client ID; `scope` names the audience.
2252
+ * OAuth2 client secret
2253
+ * REQUIRED for Client Credentials mode
1903
2254
  */
1904
- authMethod?: "managed_identity";
2255
+ clientSecret: string;
1905
2256
  };
1906
2257
  /**
1907
2258
  * Server-Only Workload Configuration
@@ -1935,7 +2286,7 @@ type ServerOnlyWorkloadConfig = WorkloadConfigBase & {
1935
2286
  * detected based on which fields are present:
1936
2287
  *
1937
2288
  * - **JWT Bearer Grant**: Requires `workload_id` + `private_key`
1938
- * - **Client Credentials**: Requires `client_id` + `client_secret`, a federated token file, or `authMethod: "managed_identity"`
2289
+ * - **Client Credentials**: Requires `client_id` + `client_secret`
1939
2290
  * - **Server-Only**: Only `jwks_uri` (and optionally `issuer`) for token validation
1940
2291
  *
1941
2292
  * The developer uses the same API regardless of mode - the library handles the details.
@@ -1964,8 +2315,8 @@ type TrustedIdp = {
1964
2315
  * one of these values (with `azp` fallback). REQUIRED: prevents cross-app token reuse.
1965
2316
  */
1966
2317
  audience: string | string[];
1967
- /** Per-IdP scopesAsEntitlements intent (see {@link WorkloadConfigBase.scopesAsEntitlements}). */
1968
- scopesAsEntitlements?: boolean;
2318
+ /** Per-IdP claims intent (see {@link WorkloadConfigBase.claimsAsEntitlements}). */
2319
+ claimsAsEntitlements?: ClaimsAsEntitlements;
1969
2320
  };
1970
2321
  /**
1971
2322
  * Workload config with separate incoming (server) and outgoing (client) roles.
@@ -1994,6 +2345,8 @@ interface WorkloadGetToken {
1994
2345
  * Workload Identity Authentication Interface
1995
2346
  */
1996
2347
  type Workload = WorkloadConfig & ModuleReadiness & Required<Pick<ModuleReadiness, "isReady" | "ready">> & {
2348
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
2349
+ describe: () => WorkloadDescription;
1997
2350
  /**
1998
2351
  * Returns a token for this workload configuration.
1999
2352
  * The optional argument overrides the configured default scope.
@@ -2065,7 +2418,8 @@ interface IAMHandlerConfig {
2065
2418
  * IAM configuration
2066
2419
  *
2067
2420
  * - If `url` is provided, groups_outbound is enabled (app calls external IAM)
2068
- * - If a `catalogStore` is provided, groups_inbound is enabled (external IAM calls app)
2421
+ * - If a catalog source is provided, groups_inbound is enabled (external IAM calls app);
2422
+ * `POST /Groups` additionally requires `AuthzConfig.onInboundEntitlement`
2069
2423
  * - If `userStore` is provided, users_inbound is enabled (external IAM calls app)
2070
2424
  */
2071
2425
  type IAMConfig = {
@@ -2181,6 +2535,7 @@ type IAMAccessRequestsConfig = {
2181
2535
  request: AccessRequestResult;
2182
2536
  created: boolean;
2183
2537
  }>;
2538
+ get?: (id: string) => Promise<AccessRequestResult | undefined>;
2184
2539
  };
2185
2540
  type IAMEntitlementRequestUrlContext = {
2186
2541
  request: Request;
@@ -2409,7 +2764,7 @@ type IAMGroupsOutbound = {
2409
2764
  };
2410
2765
  /**
2411
2766
  * Groups Inbound extension - for receiving group provisioning from external IAM providers.
2412
- * Enabled when a `catalogStore` is configured.
2767
+ * Enabled when a catalog source is configured; IAM-sourced create remains AppDev opt-in.
2413
2768
  */
2414
2769
  type IAMGroupsInbound = {
2415
2770
  /**
@@ -2434,10 +2789,12 @@ type IAMUsersInbound = {
2434
2789
  *
2435
2790
  * - Core functions are user-related (outbound to external IAM)
2436
2791
  * - `groups_outbound` is available when `url` is configured
2437
- * - `groups_inbound` is available when a `catalogStore` is configured
2792
+ * - `groups_inbound` is available when a catalog source is configured
2438
2793
  * - `users_inbound` is available when `userStore` is configured
2439
2794
  */
2440
2795
  type IAM = Omit<IAMConfig, "users" | "workloads" | "customers" | "groups"> & {
2796
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
2797
+ describe: () => IamDescription;
2441
2798
  users: IamPrincipalKind;
2442
2799
  workloads: IamPrincipalKind;
2443
2800
  customers: IamPrincipalKind;
@@ -2460,7 +2817,8 @@ type IAM = Omit<IAMConfig, "users" | "workloads" | "customers" | "groups"> & {
2460
2817
  groups_outbound?: IAMGroupsOutbound;
2461
2818
  /**
2462
2819
  * Groups Inbound extension - receive group provisioning from external IAM.
2463
- * Available when a `catalogStore` is configured.
2820
+ * Available when a catalog source is configured. `POST /Groups` returns 501 unless the App
2821
+ * implements `AuthzConfig.onInboundEntitlement` and wires an `AccessCatalogStore`.
2464
2822
  */
2465
2823
  groups_inbound?: IAMGroupsInbound;
2466
2824
  /**
@@ -2505,7 +2863,7 @@ declare function assertSubjectTypeAssignable(catalogStore: AccessCatalogStore |
2505
2863
  * @param workload - Workload instance for authentication
2506
2864
  * @returns IAM service instance
2507
2865
  */
2508
- declare function iam(validators: IAMValidators, log: Logger, workload: Workload | undefined, authz: Authz, accessStore?: AccessAssignmentStore, accessIndex?: AccessIndex, catalogStore?: AccessCatalogStore, codeCatalog?: CodeCatalog, resources?: ResourceProvider, fromVault?: Partial<IAMConfig>, fromCode?: Partial<IAMConfig>, iamDelta?: IamDeltaEmitter, onAccessMutation?: (summary: ScimMutationSummary) => void, trustedClientGrants?: AccessChange[], authzQueueSettle?: () => Promise<void>): IAM | undefined;
2866
+ declare function iam(validators: IAMValidators, log: Logger, workload: Workload | undefined, authz: Authz, accessStore?: AccessAssignmentStore, accessIndex?: AccessIndex, catalogStore?: AccessCatalogStore, codeCatalog?: CodeCatalog, resources?: ResourceProvider, fromVault?: Partial<IAMConfig>, fromCode?: Partial<IAMConfig>, iamDelta?: IamDeltaEmitter, onAccessMutation?: (summary: ScimMutationSummary) => void, onInboundEntitlement?: (input: InboundEntitlementRequest) => InboundEntitlementDecision | Promise<InboundEntitlementDecision>, trustedClientGrants?: AccessChange[], authzQueueSettle?: () => Promise<void>): IAM | undefined;
2509
2867
  type KmsKeyState = "pending" | "active" | "deprecated" | "rotated" | "revoked";
2510
2868
  type KmsKey = {
2511
2869
  id: string;
@@ -2614,6 +2972,8 @@ type KmsPoolSlot = {
2614
2972
  ready(timeout?: number): Promise<void>;
2615
2973
  };
2616
2974
  type KmsPool = {
2975
+ /** Secret-free, JSON-serializable pool configuration and readiness summary. */
2976
+ describe(): KmsPoolDescription;
2617
2977
  /**
2618
2978
  * Returns the current granted key for slot 0. Calls wait on one shared local subscription until a
2619
2979
  * grant is available; repeated calls resolve from the local cache and never initiate a vault request.
@@ -2646,73 +3006,14 @@ type KmsPool = {
2646
3006
  * (`ion.kms.<name>`); use `getPool(name)` when the name is dynamic.
2647
3007
  */
2648
3008
  type Kms = ModuleReadiness & {
3009
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
3010
+ describe(): KmsDescription;
2649
3011
  readonly config: Readonly<KmsConfig>;
2650
3012
  listPools(): string[];
2651
3013
  getPool(name: string): KmsPool;
2652
3014
  /** Issuer handle for a named pool. Independent of consumer slot grants. */
2653
3015
  issuer(name: string): KmsPoolIssuer;
2654
3016
  };
2655
- type OtelProtocol = "http/protobuf" | "http/json";
2656
- type OtelProviderType = "otlp" | "grafana-lgtm" | "datadog" | "splunk" | "elastic";
2657
- type OtelLogLevel = "trace" | "debug" | "info" | "warn" | "error";
2658
- type OtelLevels = Partial<Record<OtelLogLevel, boolean>>;
2659
- /** Local console mirror policy: disable it or set its minimum emitted severity. */
2660
- type OtelConsoleMode = "off" | OtelLogLevel;
2661
- type OtelAttributeValue = string | number | boolean | string[] | number[] | boolean[];
2662
- type OtelAttributes = Record<string, OtelAttributeValue>;
2663
- type OtelSignalConfig = {
2664
- /** Per-signal OTLP endpoint. When omitted, the module appends /v1/{signal} to `endpoint`. */
2665
- endpoint?: string;
2666
- headers?: Record<string, string>;
2667
- timeoutMillis?: number;
2668
- concurrencyLimit?: number;
2669
- };
2670
- type OtelMetricsSignalConfig = OtelSignalConfig & {
2671
- exportIntervalMillis?: number;
2672
- exportTimeoutMillis?: number;
2673
- };
2674
- type OtelOAuthClientCredentialsConfig = {
2675
- tokenUrl: string;
2676
- clientId: string;
2677
- clientSecret: string;
2678
- scope?: string;
2679
- audience?: string;
2680
- headers?: Record<string, string>;
2681
- };
2682
- /**
2683
- * OpenTelemetry config supplied by ConfigSource/RemoteConfig.
2684
- * Keep environment-specific URLs and credentials here, not in FrameworkConfig.
2685
- */
2686
- type OtelConfig = {
2687
- type?: OtelProviderType;
2688
- name: string;
2689
- endpoint?: string;
2690
- protocol?: OtelProtocol;
2691
- headers?: Record<string, string>;
2692
- apiKey?: string;
2693
- /** Name of a `RemoteConfig.kms` key pool used for exporter authentication. */
2694
- apiKeyPool?: string;
2695
- apiKeyHeader?: string;
2696
- oauth?: OtelOAuthClientCredentialsConfig;
2697
- version?: string;
2698
- deploymentEnvironment?: string;
2699
- resourceAttributes?: OtelAttributes;
2700
- levels?: OtelLevels;
2701
- /** `'off'` exports only to OTel; a level dual-emits at or above that severity. Defaults to `'info'`. */
2702
- console?: OtelConsoleMode;
2703
- traces?: boolean | OtelSignalConfig;
2704
- metrics?: boolean | OtelMetricsSignalConfig;
2705
- logs?: boolean | OtelSignalConfig;
2706
- };
2707
- type Otel = {
2708
- beforeChange(listener: ChangeListener): () => void;
2709
- afterChange(listener: ChangeListener): () => void;
2710
- isReady(): boolean;
2711
- ready(timeout?: number): Promise<void>;
2712
- logger: Logger;
2713
- forceFlush(): Promise<void>;
2714
- shutdown(): Promise<void>;
2715
- };
2716
3017
  /**
2717
3018
  * Session management for tracking user sessions and enabling backchannel logout.
2718
3019
  *
@@ -2805,6 +3106,12 @@ type Session<TExtended = object> = {
2805
3106
  */
2806
3107
  claims?: Record<string, unknown>;
2807
3108
  /**
3109
+ * Entitlements derived from token claims at login and refresh. Session-scoped: they die with the
3110
+ * record (TTL, rotation, back-channel logout) and are never written to the access stores.
3111
+ * Only meaningful when the store declares {@link SessionStoreCapabilities.derivedEntitlements}.
3112
+ */
3113
+ entitlements?: DerivedEntitlement[];
3114
+ /**
2808
3115
  * Optional access-token claims for modules that keep access/id claims separate.
2809
3116
  */
2810
3117
  accessClaims?: Record<string, unknown>;
@@ -2841,7 +3148,21 @@ type Session<TExtended = object> = {
2841
3148
  * }
2842
3149
  * ```
2843
3150
  */
3151
+ /**
3152
+ * Store-declared capabilities. The SDK does not probe a custom store (a write at init is forbidden);
3153
+ * a store that omits the declaration is treated as incapable.
3154
+ */
3155
+ type SessionStoreCapabilities = {
3156
+ /**
3157
+ * The store persists {@link Session.entitlements} round-trip. JSON-backed stores (the SDK Redis
3158
+ * and in-memory implementations) declare this. A column-mapped SQL store declares it only after
3159
+ * it has a column for the derived set.
3160
+ */
3161
+ derivedEntitlements?: boolean;
3162
+ };
2844
3163
  interface SessionStore<TExtended = object> {
3164
+ /** Declaration of what this store persists beyond the required session fields. */
3165
+ readonly capabilities?: SessionStoreCapabilities;
2845
3166
  /**
2846
3167
  * Create a new session in the store.
2847
3168
  *
@@ -3199,6 +3520,13 @@ type SSOConfig<
3199
3520
  */
3200
3521
  userStore?: UserStore<TUserData>;
3201
3522
  /**
3523
+ * ConfigSource-only. When set, the named token claims are derived into session-scoped
3524
+ * entitlements at login and refresh. Never set from application code.
3525
+ */
3526
+ claimsAsEntitlements?: ClaimsAsEntitlements;
3527
+ /** ConfigSource-only IdP assignment workflow used when IAM assignment is `'idp'`. */
3528
+ accessRequests?: AccessRequestsDestination;
3529
+ /**
3202
3530
  * Enable Just-In-Time (JIT) user provisioning.
3203
3531
  * When enabled, new users are automatically created in the userStore on their first login.
3204
3532
  * When disabled (default), only existing users in the userStore are updated on login.
@@ -3301,6 +3629,8 @@ type SSO<
3301
3629
  TSessionData = Record<string, never>,
3302
3630
  TUserData = Record<string, never>
3303
3631
  > = SSOConfig<TSessionData, TUserData> & {
3632
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
3633
+ describe: () => SsoDescription;
3304
3634
  getUser: (request: Request) => Promise<WorkforceUser | undefined>;
3305
3635
  /**
3306
3636
  * Degraded-aware resolution: distinguishes a signed-out visitor (`unauthenticated`) from a failed
@@ -3322,8 +3652,9 @@ type SSO<
3322
3652
  declare function sso<
3323
3653
  TSessionData = Record<string, never>,
3324
3654
  TUserData = Record<string, never>
3325
- >(validators: SSOValidators, log: Logger, fromVault?: Partial<SSOConfig<TSessionData, TUserData>>, fromCode?: Partial<SSOConfig<TSessionData, TUserData>>, _options?: {
3655
+ >(validators: SSOValidators, log: Logger, fromVault?: Partial<SSOConfig<TSessionData, TUserData>>, fromCode?: Partial<SSOConfig<TSessionData, TUserData>>, options?: {
3326
3656
  sessionCache?: ResolvedSessionCacheConfig;
3657
+ catalogView?: () => CatalogView | undefined;
3327
3658
  }): SSO<TSessionData, TUserData> | undefined;
3328
3659
  /**
3329
3660
  * Helper gets the workforce (SSO) user from the Request using the supplied
@@ -3601,7 +3932,12 @@ type SecretsSource = ReactiveHandle & ModuleReadiness & {
3601
3932
  */
3602
3933
  close?(): void | Promise<void>;
3603
3934
  };
3935
+ type DescribedSecretsSource = SecretsSource & {
3936
+ describe(): SecretsSourceDescription;
3937
+ };
3604
3938
  type Secrets = ModuleReadiness & {
3939
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
3940
+ describe(): SecretsDescription;
3605
3941
  /**
3606
3942
  * Liveness across configured sources. Returns false when any ready source reports a degraded
3607
3943
  * `isAvailable()` (e.g. an HTTP-poll source whose upstream is failing). Surfaced in
@@ -3613,7 +3949,7 @@ type Secrets = ModuleReadiness & {
3613
3949
  /** Returns configured secrets source names/keys. */
3614
3950
  listSecretsSources(): string[];
3615
3951
  /** Gets a named secrets source client. Throws when missing. */
3616
- getSecretsSource(sourceName: string): SecretsSource;
3952
+ getSecretsSource(sourceName: string): DescribedSecretsSource;
3617
3953
  /** Creates a lazy handle for a secret in a named source. */
3618
3954
  get(sourceName: string, path: string, options?: SecretsOperationOptions): SecretHandle;
3619
3955
  /** Reads a secret from a named secrets source client. */
@@ -3964,17 +4300,6 @@ type TrustedProvisioningClient = {
3964
4300
  type AuthzConfig = {
3965
4301
  oaa?: AuthzOaaConfig;
3966
4302
  /**
3967
- * RemoteConfig/Ops-governed intent. When `true`, the request-level `has*` helpers auto-honor a
3968
- * principal's OAuth token `scope` claims as entitlements: an app-wide check (no `resource` arg)
3969
- * also allows when the target id matches a granted scope. Default `false` (secure): scopes never
3970
- * satisfy `has*` and authorization comes solely from the `AccessAssignmentStore`/`AccessIndex` (IAM/SCIM).
3971
- * Read at check time so it can be flipped live. This NEVER affects token validation, and the
3972
- * explicit `hasScope*` helpers are always available regardless of this flag. Resource-scoped
3973
- * checks (a `resource` arg supplied) are never satisfied by scopes. For workload principals this is
3974
- * a fallback only: a matched trusted IdP's per-entry `scopesAsEntitlements` takes precedence.
3975
- */
3976
- scopesAsEntitlements?: boolean;
3977
- /**
3978
4303
  * Config-declared trusted provisioning clients (read-path overlay; ConfigSource/ECI-owned). Each
3979
4304
  * entry pre-authorizes a workload client to hold specific permissions without a stored grant. See
3980
4305
  * {@link TrustedProvisioningClient}. Honored in the request-level `has*` helpers BEFORE the
@@ -4024,6 +4349,12 @@ type AuthzConfig = {
4024
4349
  * have been updated. Best-effort and isolated — throwing here never affects the SCIM response.
4025
4350
  */
4026
4351
  onAccessMutation?: (summary: ScimMutationSummary) => void;
4352
+ /**
4353
+ * Explicit AppDev opt-in for IAM-sourced entitlement definitions. Inbound SCIM `POST /Groups`
4354
+ * returns 501 when omitted. The callback may reject the proposal or accept it while attaching
4355
+ * App-owned catalog structure; IAM-owned presentation is persisted from the SCIM resource.
4356
+ */
4357
+ onInboundEntitlement?: (input: InboundEntitlementRequest) => InboundEntitlementDecision | Promise<InboundEntitlementDecision>;
4027
4358
  };
4028
4359
  /**
4029
4360
  * Remote config read from a config source (vault, lfv, etc.).
@@ -4053,7 +4384,7 @@ type RemoteConfig = {
4053
4384
  /** OpenTelemetry collector/exporter config loaded from ConfigSource. */
4054
4385
  otel?: OtelConfig;
4055
4386
  authz?: AuthzConfig;
4056
- /** ConfigSource-owned Tenant Management activation and trusted integrator identity. */
4387
+ /** ConfigSource-owned Tenant Management activation and trusted ionite identity. */
4057
4388
  tm?: RemoteTmConfig;
4058
4389
  /**
4059
4390
  * Built-in readiness endpoint intent (Ops/ECI). The SDK mounts `${basePath}/readyz` by default;
@@ -4394,6 +4725,8 @@ type CIAM<
4394
4725
  TMagicLinkData = Record<string, never>,
4395
4726
  TCustomerData = Record<string, never>
4396
4727
  > = CIAMConfig<TMagicLinkData, TCustomerData> & {
4728
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
4729
+ describe: () => CiamDescription;
4397
4730
  getCustomer: (request: Request) => Promise<Customer | undefined>;
4398
4731
  /**
4399
4732
  * Degraded-aware resolution: distinguishes a signed-out visitor (`unauthenticated`) from a failed
@@ -4428,8 +4761,9 @@ declare function ciam<
4428
4761
  TCustomerData = Record<string, never>
4429
4762
  >(validators: CIAMValidators, log: Logger, fromVault?: Partial<CIAMConfig<TMagicLinkData, TCustomerData>>, fromCode?: Partial<CIAMConfigFromCode<TMagicLinkData, TCustomerData>>, workload?: {
4430
4763
  getWorkloadIdentity: (request: Request) => Promise<WorkloadIdentity | undefined>;
4431
- }, _options?: {
4764
+ }, options?: {
4432
4765
  sessionCache?: ResolvedSessionCacheConfig;
4766
+ catalogView?: () => CatalogView | undefined;
4433
4767
  }): CIAM<TMagicLinkData, TCustomerData> | undefined;
4434
4768
  /**
4435
4769
  * Helper gets the CIAM customer from the Request using the supplied Ionite.
@@ -4504,6 +4838,15 @@ type IoniteBase = {
4504
4838
  * module is ready; `notReady` lists the requested modules that are not ready, with reasons.
4505
4839
  */
4506
4840
  requireReady(names: ModuleName[]): RequireReadyResult;
4841
+ /**
4842
+ * Returns an allow-listed, JSON-serializable snapshot for privileged App-owned diagnostics.
4843
+ * Unlike `config`, this never contains credentials, secret values, stores, or callbacks.
4844
+ *
4845
+ * Diagnostics only: each call rebuilds the readiness report and every module projection. Call it
4846
+ * from an admin loader, never per request; handlers and health probes use `isReady()` /
4847
+ * `requireReady()`.
4848
+ */
4849
+ describe(): IoniteDescription;
4507
4850
  /** Most recent RemoteConfig application outcome (last applied time and last error, if any). */
4508
4851
  configStatus: ConfigApplyStatus;
4509
4852
  /** Release config subscriptions and background resources. */
@@ -4529,7 +4872,10 @@ interface TenantWebhookPayload {
4529
4872
  error?: string;
4530
4873
  }
4531
4874
  type BaseTenant = {
4875
+ /** App-owned routing key. */
4532
4876
  id: string;
4877
+ /** Stable tenant id in the ionite domain, once this tenant is known to ionite. */
4878
+ ioniteId?: string;
4533
4879
  companyId: string;
4534
4880
  companyName: string;
4535
4881
  environmentType: EnvironmentType;
@@ -4551,7 +4897,7 @@ type BaseTenant = {
4551
4897
  */
4552
4898
  configLocator?: ConfigLocator;
4553
4899
  /**
4554
- * App-internal state for an integrator-requested Tenant Management handover. Protocol-internal
4900
+ * App-internal state for an ionite-requested Tenant Management handover. Protocol-internal
4555
4901
  * fields are never serialized by the TM wire handlers.
4556
4902
  */
4557
4903
  integration?: TenantIntegration;
@@ -4559,9 +4905,8 @@ type BaseTenant = {
4559
4905
  type TenantIntegrationStatus = "consent_requested" | "consented" | "declined";
4560
4906
  type TenantIntegration = {
4561
4907
  version: number;
4562
- integrator: string;
4563
- integratorName?: string;
4564
- integratorTenantId: string;
4908
+ ioniteName?: string;
4909
+ ioniteId: string;
4565
4910
  productId: string;
4566
4911
  applicationId: string;
4567
4912
  handleHash: string;
@@ -4585,18 +4930,29 @@ type SetTenantIntegrationOptions = {
4585
4930
  /** Compare-and-set guard for transitions within the same handle. */
4586
4931
  expectedVersion?: number;
4587
4932
  };
4588
- type TmIntegratorConfig = {
4933
+ /**
4934
+ * ConfigSource-only trust for the ionite control plane that drives Tenant Management. Code never
4935
+ * sets these fields; do not duplicate them in `workload.incoming` or `authz.trustedProvisioningClients`.
4936
+ */
4937
+ type TmIoniteConfig = {
4938
+ /** Stable identifier of the trusted ionite control plane. */
4589
4939
  id: string;
4940
+ /** Optional human-readable name used in consent copy. */
4590
4941
  name?: string;
4942
+ /** Browser-facing ionite URL used to restart an integration. */
4591
4943
  url: string;
4944
+ /** Required `iss` claim on ionite-signed staged-request handles and ionite workload tokens. */
4592
4945
  issuer: string;
4946
+ /** ionite JWKS endpoint used to verify signed staged-request handles. */
4593
4947
  jwksUri: string;
4948
+ /** Required `aud` claim binding an ionite-staged request to this App. */
4594
4949
  audience: string;
4950
+ /** Required `azp`/client identity of the trusted ionite control plane. */
4595
4951
  clientId: string;
4596
4952
  };
4597
4953
  type RemoteTmConfig = {
4598
4954
  enabled: true;
4599
- integrator: TmIntegratorConfig;
4955
+ ionite: TmIoniteConfig;
4600
4956
  consentTtlSeconds?: number;
4601
4957
  maxRedeemAttempts?: number;
4602
4958
  };
@@ -4607,6 +4963,8 @@ type RecordTenantConsentInput = {
4607
4963
  decidedBy?: string;
4608
4964
  };
4609
4965
  type TenantManagement<TTenant extends BaseTenant = BaseTenant> = ModuleReadiness & {
4966
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
4967
+ describe(): TmDescription;
4610
4968
  handler(request: Request): Promise<Response>;
4611
4969
  getTenant(id: string): Promise<TTenant | undefined>;
4612
4970
  recordConsent(input: RecordTenantConsentInput): Promise<TTenant>;
@@ -4642,6 +5000,19 @@ type TenantIonCache = {
4642
5000
  fetch(id: string): Promise<Ionite | undefined>;
4643
5001
  delete(id: string): unknown;
4644
5002
  };
5003
+ /**
5004
+ * App-owned tenant persistence for routing and Tenant Management.
5005
+ *
5006
+ * Tenants carry two ids: `id` is the App-owned routing key and `ioniteId` is ionite's stable id.
5007
+ * Implementations MUST keep `ioniteId` unique across tenants and MUST treat it as immutable once
5008
+ * set: `upsert` and `setIntegration` reject a different `ioniteId` for a bound tenant (the
5009
+ * built-in in-memory and Redis stores throw `ionite id cannot change`) and an `upsert` that omits
5010
+ * `ioniteId` keeps the stored value. The TM handler enforces the same rule at the HTTP layer; the
5011
+ * store rule protects App-owned write paths that bypass `ion.tm`.
5012
+ *
5013
+ * `setIntegration` and `getByIntegrationHandleHash` are required only for ionite consent. They are
5014
+ * metadata-only writes and MUST NOT evict the tenant runtime cache; ordinary `upsert`/`delete` do.
5015
+ */
4645
5016
  interface TenantStore<TTenant extends BaseTenant = BaseTenant> {
4646
5017
  get(id: string): Promise<TTenant | undefined>;
4647
5018
  list(options?: ListOptions): Promise<ListResult<TTenant>>;
@@ -4650,6 +5021,8 @@ interface TenantStore<TTenant extends BaseTenant = BaseTenant> {
4650
5021
  getIon(id: string): Promise<Ionite | undefined>;
4651
5022
  setIntegration?(tenantId: string, integration: TenantIntegration | undefined, options?: SetTenantIntegrationOptions): Promise<TTenant | undefined>;
4652
5023
  getByIntegrationHandleHash?(handleHash: string): Promise<TTenant | undefined>;
5024
+ /** Looks up a tenant by ionite's stable id; TM create is idempotent by this key. */
5025
+ getByIoniteId(ioniteId: string): Promise<TTenant | undefined>;
4653
5026
  }
4654
5027
  type InMemoryTenantStoreOptions = {
4655
5028
  /**
@@ -4669,6 +5042,7 @@ declare class InMemoryTenantStore<TTenant extends BaseTenant = BaseTenant> imple
4669
5042
  getIon(id: string): Promise<Ionite | undefined>;
4670
5043
  setIntegration(tenantId: string, integration: TenantIntegration | undefined, options?: SetTenantIntegrationOptions): Promise<TTenant | undefined>;
4671
5044
  getByIntegrationHandleHash(handleHash: string): Promise<TTenant | undefined>;
5045
+ getByIoniteId(ioniteId: string): Promise<TTenant | undefined>;
4672
5046
  }
4673
5047
  declare function sendTenantWebhook(webhookUrl: string, payload: TenantWebhookPayload, log: Logger): Promise<void>;
4674
5048
  /**
@@ -4881,6 +5255,35 @@ type OaaAccessTargetType = "group" | "role" | "permission";
4881
5255
  type EntitlementType = OaaAccessTargetType;
4882
5256
  /** A catalog Entitlement: a group, role, or permission (see {@link EntitlementType}). */
4883
5257
  type Entitlement<TAttributes extends object = object> = Group2<TAttributes> | Role2<TAttributes> | Permission<TAttributes>;
5258
+ /** Validated IAM-authored presentation and membership proposed through inbound SCIM `POST /Groups`. */
5259
+ type InboundEntitlementRequest = {
5260
+ type: EntitlementType;
5261
+ displayName: string;
5262
+ description?: string;
5263
+ externalId?: string;
5264
+ riskLevel?: RiskLevel;
5265
+ attributes?: Record<string, unknown>;
5266
+ resources?: string[];
5267
+ members: GroupMember[];
5268
+ request: Request;
5269
+ };
5270
+ /**
5271
+ * AppDev decision for an IAM-sourced entitlement. Accepting may attach only App-owned structure;
5272
+ * IAM-owned presentation remains sourced from the inbound SCIM resource.
5273
+ */
5274
+ type InboundEntitlementDecision = {
5275
+ accept: false;
5276
+ reason?: string;
5277
+ } | {
5278
+ accept: true;
5279
+ id?: string;
5280
+ assignableSubjectTypes?: SubjectType[];
5281
+ recommendedSubjectTypes?: SubjectType[];
5282
+ permissions?: string[];
5283
+ roles?: string[];
5284
+ groups?: string[];
5285
+ resources?: string[];
5286
+ };
4884
5287
  type AccessRef<T> = string | T;
4885
5288
  /**
4886
5289
  * A catalog entry returned synchronously by {@link EntityStore.set}. It is the entity itself,
@@ -5212,6 +5615,8 @@ interface OaaClient<
5212
5615
  TCustomer = BaseCustomer,
5213
5616
  TWorkload = BaseWorkloadIdentity
5214
5617
  > {
5618
+ /** Secret-free, JSON-serializable OAA configuration and readiness summary. */
5619
+ describe(): OaaDescription;
5215
5620
  pushUser(user: TUser): Promise<void>;
5216
5621
  pushCustomer(customer: TCustomer): Promise<void>;
5217
5622
  pushWorkload(workload: TWorkload): Promise<void>;
@@ -5330,6 +5735,8 @@ type ReconcileReport = {
5330
5735
  * wired they deny by default (`{ allow: false }`, `metadata.reason = 'access_index_not_configured'`).
5331
5736
  */
5332
5737
  type Authz = {
5738
+ /** Secret-free, JSON-serializable configuration and readiness summary. */
5739
+ describe(): AuthzDescription;
5333
5740
  oaa: OaaClient;
5334
5741
  /** Read the effective catalog union (materialized rows plus unmaterialized code templates). */
5335
5742
  entitlements: {
@@ -5381,9 +5788,9 @@ type Authz = {
5381
5788
  */
5382
5789
  grant(change: AccessChange): Promise<AccessChange>;
5383
5790
  /**
5384
- * Submit one grant or revoke request on a principal's behalf. Allowed only when that kind's
5385
- * assignment policy is `'iam-request'`. One operation maps to one approval decision, status,
5386
- * and tracking URL. Authenticated with the module-wide `deltaAudience` workload client.
5791
+ * Submit one grant or revoke request on a principal's behalf. Under `'iam-request'`, targets the
5792
+ * IAM destination; under `'idp'`, targets the claims-bearing SSO/CIAM provider. One operation
5793
+ * maps to one approval decision, status, and tracking URL.
5387
5794
  */
5388
5795
  requestAccess(change: {
5389
5796
  subject: AccessChange["subject"];
@@ -5392,6 +5799,20 @@ type Authz = {
5392
5799
  target: AccessChange["target"];
5393
5800
  operation: "grant" | "revoke";
5394
5801
  justification?: string;
5802
+ /** CIAM provider key. Required only when multiple providers expose an AccessRequests channel. */
5803
+ provider?: string;
5804
+ }): Promise<{
5805
+ id: string;
5806
+ status: "pending" | "approved" | "denied";
5807
+ operation: "grant" | "revoke";
5808
+ trackingUrl: string;
5809
+ }>;
5810
+ /** Read the destination-owned state of an on-behalf access request. */
5811
+ getAccessRequest(input: {
5812
+ id: string;
5813
+ subjectType: SubjectType;
5814
+ /** CIAM provider key. Required only when multiple providers expose an AccessRequests channel. */
5815
+ provider?: string;
5395
5816
  }): Promise<{
5396
5817
  id: string;
5397
5818
  status: "pending" | "approved" | "denied";
@@ -5411,8 +5832,10 @@ type Authz = {
5411
5832
  * - `'iam'` — never assign in-app; show a request link (or a read-only entitlement table).
5412
5833
  * - `'iam-request'` — never assign in-app; the app submits a request on the principal's behalf via
5413
5834
  * {@link Authz.requestAccess}.
5835
+ * - `'idp'` — the claims-bearing SSO/CIAM provider is assigner of record; requests use that
5836
+ * provider's ConfigSource-owned AccessRequests channel.
5414
5837
  */
5415
- type IamAssignmentPolicy = "app" | "iam" | "iam-request";
5838
+ type IamAssignmentPolicy = "app" | "iam" | "iam-request" | "idp";
5416
5839
  /** Why an {@link AccessCheckResult} resolved the way it did. */
5417
5840
  type AccessCheckReason = "ok" | "unauthenticated" | "forbidden" | "not_configured";
5418
5841
  /**
@@ -5593,8 +6016,7 @@ declare function hasAny2(request: Request, ion: Ionite, targets: AccessCheckTarg
5593
6016
  declare function hasAll2(request: Request, ion: Ionite, targets: AccessCheckTarget[], subjectType?: SubjectScope): Promise<AccessCheckResult>;
5594
6017
  /**
5595
6018
  * Request-level check that a resolved principal's OAuth token carries a given `scope` claim. This is
5596
- * the explicit OAuth-claims authorization path (always available, independent of
5597
- * `RemoteConfig.authz.scopesAsEntitlements`). It is orthogonal to {@link has} / {@link hasPermission}
6019
+ * the explicit OAuth-claims authorization path (always available). It is orthogonal to {@link has} / {@link hasPermission}
5598
6020
  * which authorize against IAM/SCIM entitlements in the AccessIndex.
5599
6021
  *
5600
6022
  * Resolves the principal(s) per `subjectType` (default `'any'`), reads each principal's token scopes
@@ -5607,6 +6029,36 @@ declare function hasScope(request: Request, ion: Ionite, scope: string, subjectT
5607
6029
  declare function hasAnyScope(request: Request, ion: Ionite, scopes: string[], subjectType?: SubjectScope): Promise<AccessCheckResult>;
5608
6030
  /** Request-level check that a resolved principal's token carries ALL of `scopes`. See {@link hasScope}. */
5609
6031
  declare function hasAllScope(request: Request, ion: Ionite, scopes: string[], subjectType?: SubjectScope): Promise<AccessCheckResult>;
6032
+ type DeriveClaimsInput = {
6033
+ block: ClaimsAsEntitlements;
6034
+ /** ID-token claims (user/customer) or the validated workload token claims. First source wins. */
6035
+ claims: Record<string, unknown>;
6036
+ /** Access-token claims, when the token is a JWT. ID-token claims win on name collisions. */
6037
+ accessClaims?: Record<string, unknown>;
6038
+ /** Token-response `scope`, used when neither claim set carries `scope`. */
6039
+ scope?: string;
6040
+ subjectType: SubjectType;
6041
+ subjectId: string;
6042
+ catalog?: CatalogView;
6043
+ log?: Logger;
6044
+ };
6045
+ /**
6046
+ * Resolve configured claims to catalog ids and, when `expand` is set, to the closure `expandRoot`
6047
+ * would materialize for a grant of that id. Unknown values are dropped. SDK-owned `ion:*`
6048
+ * permission ids resolve as permissions even when the App catalog does not list them, because they
6049
+ * are the platform's own endpoint gates.
6050
+ */
6051
+ declare function deriveClaimsEntitlements(input: DeriveClaimsInput): Promise<DerivedEntitlement[]>;
6052
+ /**
6053
+ * Decode supplemental access-token claims without treating that payload as an authentication
6054
+ * authority. SSO/CIAM call this only for the access token returned by their client-authenticated
6055
+ * token exchange, alongside a verified ID token whose claims win on collision. Opaque tokens and
6056
+ * malformed payloads return undefined.
6057
+ */
6058
+ declare function decodeUnverifiedJwtPayload(token: string | undefined): Record<string, unknown> | undefined;
6059
+ /** Remember the session's derived rows for this request so `has*` can see them without a public user field. */
6060
+ declare function rememberDerivedEntitlements(request: Request, subjectType: "user" | "customer", rows: DerivedEntitlement[] | undefined): void;
6061
+ declare function derivedEntitlementsFor(request: Request, subjectType: "user" | "customer"): DerivedEntitlement[] | undefined;
5610
6062
  /**
5611
6063
  * LFV callback signature helpers (Ed25519).
5612
6064
  *
@@ -6025,4 +6477,4 @@ type TenantAccessLoginUrlOptions = {
6025
6477
  tenantIonBasePath?: (tenantId: string) => string;
6026
6478
  };
6027
6479
  declare function tenantAccessLoginUrl(tenantId: string, options: TenantAccessLoginUrlOptions): string;
6028
- export { AccessRequestCreate, AccessRequestResult, AfterLoginContext, AfterLoginHook, AfterLogoutContext, AfterLogoutHook, AssertTrustedIdTokenClaimsOptions, BeforeLoginContext, BeforeLoginHook, BeforeLogoutContext, BeforeLogoutHook, CACHE_BOUNDS, CIAM, CIAMConfig, CIAMConfigFromCode, CIAMProviderBranding, CIAMProviderColors, CIAMProviderConfig, CIAMProviderDescriptor, CIAMProviderType, CIAMValidators, CIAM_PROVIDER_PRESETS, CatalogAdjacencyCache, CatalogView, CreateGroupOptions, CreateUserOptions, DiscoveredTenantCookie, DisplayCookieAttributes, DisplayCookieCandidate, DisplayCookieTrim, GroupsInboundHandlerConfig, IAM, IAMAccessRequestsConfig, IAMConfig, IAMDiscoveryConfig, IAMDiscoveryContext, IAMDiscoveryHandlerConfig, IAMGroupsInbound, IAMGroupsOutbound, IAMHandlerConfig, IAMInboundUserContext, IAMInboundUsersConfig, IAMUsersInbound, IAMValidators, InMemoryTenantStore, JWKS, ReconcileStores, RedirectContext, RedirectPolicy, RedirectResult, RedirectRule, RedirectRuleContext, RedirectVerdict, ReferencedDefinitions, ResolveTenantOwnerOptions, ResolveTenantSessionOptions, ResolvedCacheConfig, ResolvedJwksCacheConfig, ResolvedSessionCacheConfig, ResolvedSessionReadCacheConfig, SSO, SSOConfig, SSOHandlerConfig, SSOValidators, ScimAuthenticationScheme, ScimError, ScimListResponse, ScimResourceType, ScimResourceTypeSchemaExtension, ScimResult, ScimSchemaAttributeDefinition, ScimSchemaDefinition, ScimServiceProviderConfig, SessionDiscoveryOptions, SessionResponse, SigningKey, SigningKeyRing, StateCookie, StaticSigningKeyRingDocument, TENANT_OWNER_PERMISSION, TenantAccessDenial, TenantAccessDenialReason, TenantAccessLoginUrlOptions, TenantAccessResult, TenantAccessSession, TenantAccessSessionKind, UsersInboundHandlerConfig, VerifyIdTokenOptions, VerifyLogoutTokenOptions, VerifyUserOptions, allowHosts, allowOrigins, allowRelativePaths, allowSameOrigin, assertSubjectTypeAssignable, assertTrustedIdTokenClaims, baseCustomerToScim, baseUserToScimUser, baseWorkloadToScim, blockDangerousUrls, buildAuthorizationUrl, buildLfvSigningInput, callback, captureRedirect, ciam, clearDiscoveryCache, clearJwksCache, collectReferencedDefinitions, configureJwksCache, defaultRedirectPolicies, discoverOidcEndpoints, discoverSessionRecords, discoverSessions, entitlementToScimGroup, exchangeAuthorizationCode, expandRoot, fetchUserInfo, formatDisplayClearCookie, formatDisplaySetCookie, generateAppleClientSecret, generateLfvEd25519KeyPair, getCachedVerificationKey, getCustomer, getDiscoveredSessionUsers, getJwks, getOwnedTenantIds, getProviderPreset, getPublicKeyForKid, getPublicKeyFromJwks, getUser, getVerificationKeyForKid, has, hasAll2 as hasAll, hasAllScope, hasAny2 as hasAny, hasAnyScope, hasGroup2 as hasGroup, hasPermission2 as hasPermission, hasRole2 as hasRole, hasScope, iam, initiateLogin, isRedirectAllowed, listSsoClientIdsFromCookies, normalizeOidcIssuer, orderedEnabledProviders, parseLfvJwks, pkceChallengeFromVerifier, planDiscoveredTenantCookies, planDisplayCookieClear, planDisplayCookieHeaders, reconcileSubjectAccess, resolveAfterRedirect, resolveCacheConfig, resolveCapturedRedirect, resolveCiamProvider, resolveCustomer, resolveJwkForKid, resolveJwksCacheConfig, resolveProviderBranding, resolveRedirect, resolveSessionCacheConfig, resolveTenantOwner, resolveTenantSession, resolveUser, sameOriginRedirectRules, selectDisplayCookies, sendTenantWebhook, setDiscoveryClock, setJwksClock, setJwksRetry, signCompactJwt, signLfvPayload, sso, staticSigningKeyRing, stringifyLfvJwks, tenantAccessLoginUrl, tenantOwnerEntitlementId, tenantOwnerPermission, tenantResourceId, verifyCompactJwt, verifyIdToken, verifyLfvSignature, verifyLogoutToken, verifyUser, withSetCookies };
6480
+ export { AccessRequestCreate, AccessRequestResult, AccessRequestsDestination, AfterLoginContext, AfterLoginHook, AfterLogoutContext, AfterLogoutHook, AssertTrustedIdTokenClaimsOptions, BeforeLoginContext, BeforeLoginHook, BeforeLogoutContext, BeforeLogoutHook, CACHE_BOUNDS, CIAM, CIAMConfig, CIAMConfigFromCode, CIAMProviderBranding, CIAMProviderColors, CIAMProviderConfig, CIAMProviderDescriptor, CIAMProviderType, CIAMValidators, CIAM_PROVIDER_PRESETS, CatalogAdjacencyCache, CatalogView, ClaimsAsEntitlements, ClaimsAsEntitlementsDescription, CreateGroupOptions, CreateUserOptions, DeriveClaimsInput, DerivedEntitlement, DiscoveredTenantCookie, DisplayCookieAttributes, DisplayCookieCandidate, DisplayCookieTrim, GroupsInboundHandlerConfig, IAM, IAMAccessRequestsConfig, IAMConfig, IAMDiscoveryConfig, IAMDiscoveryContext, IAMDiscoveryHandlerConfig, IAMGroupsInbound, IAMGroupsOutbound, IAMHandlerConfig, IAMInboundUserContext, IAMInboundUsersConfig, IAMUsersInbound, IAMValidators, InMemoryTenantStore, JWKS, ReconcileStores, RedirectContext, RedirectPolicy, RedirectResult, RedirectRule, RedirectRuleContext, RedirectVerdict, ReferencedDefinitions, ResolveTenantOwnerOptions, ResolveTenantSessionOptions, ResolvedCacheConfig, ResolvedJwksCacheConfig, ResolvedSessionCacheConfig, ResolvedSessionReadCacheConfig, SSO, SSOConfig, SSOHandlerConfig, SSOValidators, ScimAuthenticationScheme, ScimError, ScimListResponse, ScimResourceType, ScimResourceTypeSchemaExtension, ScimResult, ScimSchemaAttributeDefinition, ScimSchemaDefinition, ScimServiceProviderConfig, SessionDiscoveryOptions, SessionResponse, SigningKey, SigningKeyRing, StateCookie, StaticSigningKeyRingDocument, TENANT_OWNER_PERMISSION, TenantAccessDenial, TenantAccessDenialReason, TenantAccessLoginUrlOptions, TenantAccessResult, TenantAccessSession, TenantAccessSessionKind, UsersInboundHandlerConfig, VerifyIdTokenOptions, VerifyLogoutTokenOptions, VerifyUserOptions, allowHosts, allowOrigins, allowRelativePaths, allowSameOrigin, assertSubjectTypeAssignable, assertTrustedIdTokenClaims, baseCustomerToScim, baseUserToScimUser, baseWorkloadToScim, blockDangerousUrls, buildAuthorizationUrl, buildLfvSigningInput, callback, captureRedirect, ciam, clearDiscoveryCache, clearJwksCache, collectReferencedDefinitions, configureJwksCache, decodeUnverifiedJwtPayload, defaultRedirectPolicies, deriveClaimsEntitlements, derivedEntitlementsFor, discoverOidcEndpoints, discoverSessionRecords, discoverSessions, entitlementToScimGroup, exchangeAuthorizationCode, expandRoot, fetchUserInfo, formatDisplayClearCookie, formatDisplaySetCookie, generateAppleClientSecret, generateLfvEd25519KeyPair, getCachedVerificationKey, getCustomer, getDiscoveredSessionUsers, getJwks, getOwnedTenantIds, getProviderPreset, getPublicKeyForKid, getPublicKeyFromJwks, getUser, getVerificationKeyForKid, has, hasAll2 as hasAll, hasAllScope, hasAny2 as hasAny, hasAnyScope, hasGroup2 as hasGroup, hasPermission2 as hasPermission, hasRole2 as hasRole, hasScope, iam, initiateLogin, isRedirectAllowed, listSsoClientIdsFromCookies, normalizeOidcIssuer, orderedEnabledProviders, parseLfvJwks, pkceChallengeFromVerifier, planDiscoveredTenantCookies, planDisplayCookieClear, planDisplayCookieHeaders, reconcileSubjectAccess, rememberDerivedEntitlements, resolveAfterRedirect, resolveCacheConfig, resolveCapturedRedirect, resolveCiamProvider, resolveCustomer, resolveJwkForKid, resolveJwksCacheConfig, resolveProviderBranding, resolveRedirect, resolveSessionCacheConfig, resolveTenantOwner, resolveTenantSession, resolveUser, sameOriginRedirectRules, selectDisplayCookies, sendTenantWebhook, setDiscoveryClock, setJwksClock, setJwksRetry, signCompactJwt, signLfvPayload, sso, staticSigningKeyRing, stringifyLfvJwks, tenantAccessLoginUrl, tenantOwnerEntitlementId, tenantOwnerPermission, tenantResourceId, verifyCompactJwt, verifyIdToken, verifyLfvSignature, verifyLogoutToken, verifyUser, withSetCookies };