@ionite/server 0.0.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.
@@ -0,0 +1,1032 @@
1
+ import { ConfigChangeCallback, ConfigSource as ConfigSource9, FrameworkConfigInput, FrameworkStores as FrameworkStores2, Ionite as Ionite5, ModifiableFrameworkConfig, RemoteConfig as RemoteConfig2, RemoteConfigRetryHook, RemoteConfigRetryOptions, RoutingOptions as RoutingOptions2 } from "@ionite/core";
2
+ import { ApplicationValidators, BaseTenant, CreateTenantRequest, Ionite, TenantStore, TenantValidators, UpdateTenantRequest, Workload } from "@ionite/core";
3
+ import { CapabilityEndpointUrls } from "@ionite/core";
4
+ /**
5
+ * Deterministic set of ionite endpoint paths derived from a single `basePath`.
6
+ * These are the only URLs the SDK mounts; application code cannot customize individual paths
7
+ * (only `basePath`). Post-login/logout destinations are NOT here: they are decided by the appdev's
8
+ * `beforeLogin`/`afterLogin`/`beforeLogout`/`afterLogout` hooks (application UI routes), not ES endpoints derivable from `basePath`.
9
+ */
10
+ type UrlMap = CapabilityEndpointUrls;
11
+ /**
12
+ * Derive every ionite endpoint path from `basePath`. Single source of truth consumed
13
+ * by module construction, the router, the readiness endpoint, and tenant-manager responses.
14
+ */
15
+ declare function deriveIonUrls(basePath: string): UrlMap;
16
+ type TenantManagerOptions<
17
+ TCreateTenantRequest extends CreateTenantRequest = CreateTenantRequest,
18
+ TUpdateTenantRequest extends UpdateTenantRequest = UpdateTenantRequest,
19
+ TTenant extends BaseTenant = BaseTenant
20
+ > = {
21
+ validators: ApplicationValidators<TenantValidators<TCreateTenantRequest, TUpdateTenantRequest>>;
22
+ basePath: string;
23
+ store: TenantStore<TTenant>;
24
+ workloadAuth?: Workload;
25
+ /**
26
+ * Resolve the ionite `basePath` for a given tenant (e.g. `/api/${tenant.id}/es`).
27
+ * When provided, tenant responses include the derived absolute SSO `callbackUrl` and full `ionUrls`
28
+ * so the ECI/ConfigSource can register the matching callback path at the IdP. ES endpoint URLs are
29
+ * deterministic from this basePath; they are not configurable per-URL.
30
+ */
31
+ tenantEsBasePath?: (tenant: TTenant) => string | undefined;
32
+ };
33
+ /** Absolute ionite URL set for a tenant, derived from its basePath and request origin. */
34
+ type TenantIonUrls = {
35
+ callbackUrl: string;
36
+ ionUrls: UrlMap;
37
+ };
38
+ type TenantManager<TTenant extends BaseTenant = BaseTenant> = {
39
+ handler(request: Request): Promise<Response>;
40
+ getTenant(id: string): Promise<TTenant | undefined>;
41
+ };
42
+ declare function tenantManager<
43
+ TCreateTenantRequest extends CreateTenantRequest = CreateTenantRequest,
44
+ TUpdateTenantRequest extends UpdateTenantRequest = UpdateTenantRequest,
45
+ TTenant extends BaseTenant = BaseTenant
46
+ >(ion: Ionite, options: TenantManagerOptions<TCreateTenantRequest, TUpdateTenantRequest, TTenant>): TenantManager<TTenant>;
47
+ import { CapabilityKey as CapabilityKey2 } from "@ionite/core";
48
+ export * from "@ionite/core";
49
+ import { AccessCheckReason, AccessCheckResult, CreateTenantRequest as CreateTenantRequest2, EnvironmentType, FrameworkOtelConfig, FrameworkStores as FrameworkStores3, generateULID, IoniteError, IoniteErrorOptions, listGenerator, Otel, OtelConfig, OtelLevels, OtelLogLevel, OtelLogRecord, OtelMetricsSignalConfig, OtelOAuthClientCredentialsConfig, OtelProviderType, OtelSignalConfig, SubjectScope, SubjectType as SubjectType3, TenantResponse, TenantStatus, TenantValidators as TenantValidators2, TenantWebhookPayload, UpdateTenantRequest as UpdateTenantRequest2 } from "@ionite/core";
50
+ import { AfterLoginContext, AfterLoginHook, AfterLogoutContext, AfterLogoutHook, BeforeLoginContext, BeforeLoginHook, BeforeLogoutContext, BeforeLogoutHook, CatalogView, ReconcileStores, RedirectContext, RedirectPolicy, RedirectResult, RedirectRule, RedirectRuleContext, RedirectVerdict, ReferencedDefinitions } from "@ionite/core/server";
51
+ import { allowHosts, allowOrigins, allowRelativePaths, allowSameOrigin, blockDangerousUrls, CatalogAdjacencyCache, callback, captureRedirect, ciam, collectReferencedDefinitions, defaultRedirectPolicies, discoverSessionRecords, discoverSessions, expandRoot, getCustomer, getUser, has, hasAll as hasAll2, hasAllScope, hasAny as hasAny2, hasAnyScope, hasGroup as hasGroup2, hasPermission as hasPermission2, hasRole as hasRole2, hasScope, InMemoryTenantStore, iam, initiateLogin, isRedirectAllowed, listSsoClientIdsFromCookies, reconcileSubjectAccess, resolveAfterRedirect, resolveCapturedRedirect, resolveRedirect, SessionDiscoveryOptions, SessionResponse, sameOriginRedirectRules, sendTenantWebhook, sso, VerifyUserOptions, verifyUser, withDefaultExternalId, withSetCookies } from "@ionite/core/server";
52
+ import { AccessAssignmentStore as AccessAssignmentStore2, AccessChange, ListOptions, ListResult, OaaAccessTargetType, SubjectType } from "@ionite/core";
53
+ /**
54
+ * In-memory store of granted access **roots** (source of truth). Roots are keyed by their
55
+ * deterministic id (`accessRootId`) and carry provenance + status. All stored roots are `active`
56
+ * (the SDK uses a transactional fail-fast model with no `pendingRemoval` sweep).
57
+ */
58
+ declare class InMemoryAccessAssignmentStore implements AccessAssignmentStore2<AccessChange> {
59
+ private readonly roots;
60
+ private readonly byTarget;
61
+ private readonly bySubject;
62
+ private static targetKey;
63
+ private static subjectKey;
64
+ private indexAdd;
65
+ private indexRemove;
66
+ private deindex;
67
+ private index;
68
+ private resolve;
69
+ grant(change: AccessChange): Promise<void>;
70
+ revoke(change: AccessChange): Promise<void>;
71
+ get(id: string): Promise<AccessChange | undefined>;
72
+ list(options?: ListOptions): Promise<ListResult<AccessChange>>;
73
+ listByTarget(targetType: OaaAccessTargetType, target: string, options?: ListOptions): Promise<ListResult<AccessChange>>;
74
+ listByTargets(targets: Array<{
75
+ targetType: OaaAccessTargetType;
76
+ target: string;
77
+ }>): Promise<Map<string, AccessChange[]>>;
78
+ listBySubject(subject: string, subjectType?: SubjectType, options?: ListOptions): Promise<ListResult<AccessChange>>;
79
+ }
80
+ import { AccessCatalogStore as AccessCatalogStore2, CatalogChangeListener, EntityStore, Group, ListOptions as ListOptions2, ListResult as ListResult2, Permission, Resource, ResourceChangeListener, ResourceProvider as ResourceProvider2, Role } from "@ionite/core";
81
+ declare class InMemoryAccessCatalogStore implements AccessCatalogStore2 {
82
+ private readonly listeners;
83
+ private readonly emit;
84
+ readonly groups: EntityStore<Group>;
85
+ readonly roles: EntityStore<Role>;
86
+ readonly permissions: EntityStore<Permission>;
87
+ afterChange(listener: CatalogChangeListener): () => void;
88
+ }
89
+ /**
90
+ * Writable, in-memory {@link ResourceProvider} for zero-DB quickstarts, dev tools, and validation.
91
+ * Real applications should implement `ResourceProvider` directly over their own database/ORM (the
92
+ * resources already live there) — this store exists so an app with no database yet can still model
93
+ * resources and resource-derived entitlements without one.
94
+ *
95
+ * `set`/`delete` emit the `changed` signal the SDK subscribes to (adjacency-cache invalidation,
96
+ * targeted reconcile, incremental OAA projection). Provide `deriveEntitlements` (plus its reverse
97
+ * resolver `resolveEntitlement`) to expose resource-derived entitlement Groups through this provider.
98
+ */
99
+ declare class InMemoryResourceStore implements ResourceProvider2 {
100
+ private readonly items;
101
+ private readonly listeners;
102
+ constructor(options?: {
103
+ /** Pure derivation of a resource's requestable entitlement Groups (see {@link ResourceProvider.entitlements}). */
104
+ deriveEntitlements?: (resource: Resource) => Group[];
105
+ /** Reverse resolution of a resource-derived entitlement id (see {@link ResourceProvider.entitlement}). */
106
+ resolveEntitlement?: (id: string, get: (resourceId: string) => Resource | undefined) => Group | undefined;
107
+ });
108
+ entitlements?: (resource: Resource) => Group[];
109
+ entitlement?: (id: string) => Promise<Group | undefined>;
110
+ get(id: string): Promise<Resource | undefined>;
111
+ list(options?: ListOptions2): Promise<ListResult2<Resource>>;
112
+ children(id: string): Promise<Resource[]>;
113
+ changed(listener: ResourceChangeListener): () => void;
114
+ /** Upsert a resource and signal `changed`. */
115
+ set(resource: Resource): void;
116
+ /** Delete a resource and signal `changed` with `deleted: true`. */
117
+ delete(id: string): void;
118
+ private emitChange;
119
+ }
120
+ import { AccessCheckTarget, AccessIndex as AccessIndex2, AccessRef, AuthzDecision, BaseUser, EffectiveEdge, Group as Group2, OaaAccessTargetType as OaaAccessTargetType2, Permission as Permission2, Resource as Resource2, Role as Role2, SubjectRef, SubjectType as SubjectType2 } from "@ionite/core";
121
+ /**
122
+ * In-memory derived closure index. Rows are root-attributed: a single
123
+ * `(subject, targetType, target, resource)` edge may be justified by multiple roots, so revoking
124
+ * one root via {@link removeByRoot} leaves edges still backed by other roots. Rebuildable from
125
+ * AccessAssignmentStore roots + the catalog.
126
+ */
127
+ declare class InMemoryAccessIndex implements AccessIndex2 {
128
+ /** edgeKey -> set of rootIds that justify it. */
129
+ private readonly rows;
130
+ /** tripleKey -> set of edgeKeys (for resource-agnostic "any" checks). */
131
+ private readonly byTriple;
132
+ /** rootId -> set of edgeKeys it materialized (for removeByRoot). */
133
+ private readonly byRoot;
134
+ /** subjectId -> set of edgeKeys (for clear(subject)). */
135
+ private readonly bySubject;
136
+ /** edgeKey -> structural metadata (avoids fragile key parsing on cleanup). */
137
+ private readonly edgeMeta;
138
+ /**
139
+ * Subject-disabled markers (subjectKey set). Deliberately independent of edge materialization:
140
+ * `materialize`/`removeByRoot`/`clear` never touch it, so reconcile cannot silently re-enable a
141
+ * SCIM-deactivated subject. Grants stay intact; re-enabling restores prior decisions unchanged.
142
+ */
143
+ private readonly disabledSubjects;
144
+ has(subject: AccessRef<BaseUser>, targetType: OaaAccessTargetType2, target: AccessRef<Group2 | Role2 | Permission2>, resource?: AccessRef<Resource2>, subjectType?: SubjectType2): Promise<AuthzDecision>;
145
+ hasGroup(subject: AccessRef<BaseUser>, group: AccessRef<Group2>, resource?: AccessRef<Resource2>, subjectType?: SubjectType2): Promise<AuthzDecision>;
146
+ hasRole(subject: AccessRef<BaseUser>, role: AccessRef<Role2>, resource?: AccessRef<Resource2>, subjectType?: SubjectType2): Promise<AuthzDecision>;
147
+ hasPermission(subject: AccessRef<BaseUser>, permission: AccessRef<Permission2>, resource?: AccessRef<Resource2>, subjectType?: SubjectType2): Promise<AuthzDecision>;
148
+ hasAny(subject: AccessRef<BaseUser>, targets: AccessCheckTarget[], subjectType?: SubjectType2): Promise<AuthzDecision>;
149
+ hasAll(subject: AccessRef<BaseUser>, targets: AccessCheckTarget[], subjectType?: SubjectType2): Promise<AuthzDecision>;
150
+ materialize(rootId: string, edges: EffectiveEdge[]): Promise<void>;
151
+ removeByRoot(rootId: string): Promise<void>;
152
+ listSubjects(): Promise<SubjectRef[]>;
153
+ setSubjectDisabled(subject: string, subjectType: SubjectType2, disabled: boolean): Promise<void>;
154
+ isSubjectDisabled(subject: string, subjectType?: SubjectType2): Promise<boolean>;
155
+ clear(subject?: string, subjectType?: SubjectType2): Promise<void>;
156
+ private allow;
157
+ private addToSet;
158
+ /** Fully remove an edge key from every secondary index (used when no roots justify it anymore). */
159
+ private dropEdge;
160
+ private removeFromSet;
161
+ }
162
+ import { AwsSecretsConfig, ConfigSource, SecretsSource } from "@ionite/core";
163
+ type AwsConfigSourceOptions = Omit<AwsSecretsConfig, "type"> & {
164
+ vault?: SecretsSource;
165
+ ttl?: number;
166
+ };
167
+ /**
168
+ * Creates a ConfigSource which reads from AWS Secrets Manager.
169
+ *
170
+ * Not implemented in this build. Throws synchronously at construction (config-source resolution /
171
+ * startup) so a `type: 'aws'` locator fails fast with a clear error rather than type-checking and
172
+ * then throwing at first config access.
173
+ */
174
+ declare function awsConfig(_config: AwsConfigSourceOptions): ConfigSource;
175
+ import { AzureSecretsConfig, ConfigSource as ConfigSource2, SecretsSource as SecretsSource2 } from "@ionite/core";
176
+ type AzureConfigSourceOptions = Omit<AzureSecretsConfig, "type"> & {
177
+ vault?: SecretsSource2;
178
+ path?: string;
179
+ };
180
+ /**
181
+ * Creates a ConfigSource which reads from Azure Key Vault.
182
+ * The path is mapped to a Key Vault secret name via secretNameTransform (default: `/` → `-`).
183
+ * If ttl is set, the vault is polled every ttl milliseconds (minimum 10 minutes) and onConfig is called when config changes.
184
+ */
185
+ declare function azureConfig(config: AzureConfigSourceOptions): ConfigSource2;
186
+ import { ConfigLocator, ConfigSource as ConfigSource3, ConfigSourceEnv, ConfigSourceType } from "@ionite/core";
187
+ type EnvConfigOptions = {
188
+ /** Override config source type (default: ION_CONFIG_TYPE from env). */
189
+ type?: ConfigSourceType;
190
+ };
191
+ declare function configLocatorFromEnvValues(env: Partial<ConfigSourceEnv>, options?: EnvConfigOptions): ConfigLocator;
192
+ /**
193
+ * Creates a ConfigSource from environment variables.
194
+ * Reads ION_CONFIG_TYPE to choose the source.
195
+ * For each supported type, it merges the options with available environment variables.
196
+ */
197
+ declare function configSourceFromEnvValues(env: Partial<ConfigSourceEnv>, options?: EnvConfigOptions): ConfigSource3;
198
+ declare function envConfig(options?: EnvConfigOptions): ConfigSource3;
199
+ import { ConfigSource as ConfigSource4, GcpSecretsConfig, SecretsSource as SecretsSource3 } from "@ionite/core";
200
+ type GcpConfigSourceOptions = Omit<GcpSecretsConfig, "type"> & {
201
+ vault?: SecretsSource3;
202
+ ttl?: number;
203
+ };
204
+ /**
205
+ * Creates a ConfigSource which reads from GCP Secrets Manager.
206
+ *
207
+ * Not implemented in this build. Throws synchronously at construction (config-source resolution /
208
+ * startup) so a `type: 'gcp'` locator fails fast with a clear error rather than type-checking and
209
+ * then throwing at first config access.
210
+ */
211
+ declare function gcpConfig(_config: GcpConfigSourceOptions): ConfigSource4;
212
+ import { ConfigSource as ConfigSource5, SecretsSource as SecretsSource4 } from "@ionite/core";
213
+ /**
214
+ * Creates a ConfigSource from a SecretsSource path that stores a ConfigLocator.
215
+ * The returned source rebinds subscriptions when the bootstrap secret changes.
216
+ *
217
+ * The resolved inner ConfigSource is cached by a hash of the bootstrap locator so repeated `load()`
218
+ * calls and unchanged bootstrap refreshes reuse the same inner source (and, for vault-backed inner
219
+ * sources, the same websocket/poll) instead of constructing a fresh one every time. When the
220
+ * bootstrap actually changes, the superseded inner source is disposed (its `secret.close()` is
221
+ * invoked) before the new one is built, so config churn cannot leak duplicate connections.
222
+ */
223
+ declare function secretConfigSource(source: SecretsSource4, path: string): ConfigSource5;
224
+ import { ConfigSource as ConfigSource6, SecretsSource as SecretsSource5, VaultSecretsConfig } from "@ionite/core";
225
+ type VaultConfigSourceOptions = Omit<VaultSecretsConfig, "type"> & {
226
+ vault?: SecretsSource5;
227
+ path?: string;
228
+ };
229
+ declare class VaultConfigWaitingForSecretError extends Error {
230
+ readonly path: string;
231
+ constructor(path: string, message?: string);
232
+ readonly code = "not_found";
233
+ readonly sourceType = "vault";
234
+ readonly status = 404;
235
+ }
236
+ declare function isVaultConfigWaitingForSecretError(error: unknown): error is VaultConfigWaitingForSecretError;
237
+ /**
238
+ * The vault config secret exists but is not a usable RemoteConfig (wrong shape / unknown keys). This
239
+ * is a hard, non-recoverable error (unlike {@link VaultConfigWaitingForSecretError}): the initial
240
+ * apply rejects rather than retries.
241
+ */
242
+ declare class VaultConfigInvalidPayloadError extends Error {
243
+ readonly path: string;
244
+ constructor(path: string, message: string);
245
+ readonly code = "invalid_payload";
246
+ readonly sourceType = "vault";
247
+ }
248
+ declare function isVaultConfigInvalidPayloadError(error: unknown): error is VaultConfigInvalidPayloadError;
249
+ /**
250
+ * Creates a ConfigSource that reads config from a vault instance.
251
+ * Uses the given vault client to fetch secrets at the specified path.
252
+ * If ttl is set, the vault will be polled every ttl milliseconds (minimum 10 minutes) and onConfig will be called when the config changes.
253
+ * When a custom vault client is provided, ttl is still honored for config subscription polling if the client does not support native subscribe().
254
+ */
255
+ declare function vaultConfig(config: VaultConfigSourceOptions): ConfigSource6;
256
+ import { BaseCustomer, CustomerStore, ListOptions as ListOptions3, ListResult as ListResult3 } from "@ionite/core";
257
+ /**
258
+ * In-memory customer store implementation using Maps.
259
+ *
260
+ * The CIAM customer domain counterpart to `InMemoryUserStore`.
261
+ */
262
+ declare class InMemoryCustomerStore<TExtended = Record<string, never>> implements CustomerStore<TExtended> {
263
+ private customers;
264
+ private userNameIndex;
265
+ get(id: string): Promise<BaseCustomer<TExtended> | undefined>;
266
+ lookup(customer: BaseCustomer<TExtended>): Promise<BaseCustomer<TExtended> | undefined>;
267
+ upsert(customer: BaseCustomer<TExtended>): Promise<BaseCustomer<TExtended>>;
268
+ delete(id: string): Promise<number>;
269
+ list(options?: ListOptions3): Promise<ListResult3<BaseCustomer<TExtended>>>;
270
+ }
271
+ import { AccessCatalogStore as AccessCatalogStore3, IamDeltaEmitter, IamDeltaIdMapStore as IamDeltaIdMapStore2, IamDeltaMode, Logger, ResourceProvider as ResourceProvider3, UserStore, Workload as Workload2 } from "@ionite/core";
272
+ type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;
273
+ /** RemoteConfig-derived destination descriptor for IAM delta egress. */
274
+ type IamDeltaConfig = {
275
+ mode?: IamDeltaMode;
276
+ /** Destination SCIM base URL of the IAM tool. */
277
+ url?: string;
278
+ /** Outgoing workload client name used to authenticate pushes (`IAMConfig.deltaAudience`). */
279
+ audience?: string;
280
+ /** Tenant scope for the mapping cache. */
281
+ tenantId?: string;
282
+ };
283
+ /** Runtime capabilities the emitter binds to (resolved from the merged config/stores in buildES). */
284
+ type IamDeltaDeps = {
285
+ workload?: Workload2;
286
+ userStore?: UserStore;
287
+ catalogStore?: AccessCatalogStore3;
288
+ /** Read-through provider for derived-entitlement resolution (externalId lookup on delta egress). */
289
+ resources?: ResourceProvider3;
290
+ /** `externalId -> provider id` mapping cache for outbound SCIM (defaults to in-memory). */
291
+ idMapStore?: IamDeltaIdMapStore2;
292
+ /** Injectable fetch (defaults to global fetch); enables testing the patch path in-process. */
293
+ fetchImpl?: FetchLike;
294
+ };
295
+ /**
296
+ * Managed IAM delta module (mirrors `createManagedAuthz`). Creates ONE persistent
297
+ * {@link IamDeltaEmitter} whose readiness promise is stable across RemoteConfig applies;
298
+ * `update(config, deps)` reconfigures the destination/capabilities in place.
299
+ *
300
+ * `'patch'` emits externalId-only outbound SCIM into the IAM tool at the access-mutation
301
+ * chokepoint (needs `url` + workload + an `idMapStore`, which defaults to in-memory). Every push is
302
+ * authenticated with the outgoing workload client, which the destination treats as the verified
303
+ * origin. See the README "IAM delta egress" section.
304
+ */
305
+ declare function createManagedIamDelta(log: Logger): {
306
+ update(config: IamDeltaConfig, deps: IamDeltaDeps): IamDeltaEmitter;
307
+ };
308
+ import { IamDeltaIdMapKey, IamDeltaIdMapStore as IamDeltaIdMapStore3 } from "@ionite/core";
309
+ /**
310
+ * Stable string key for an {@link IamDeltaIdMapKey}. Scoped by tenant + destination so the same
311
+ * `externalId` can map to different provider ids across IAM tools and tenants. Shared by the
312
+ * in-memory and Redis implementations to keep keying identical.
313
+ */
314
+ declare function iamDeltaIdMapKey(key: IamDeltaIdMapKey): string;
315
+ /**
316
+ * In-memory {@link IamDeltaIdMapStore}. Suitable for single-process apps and tests. Production
317
+ * deployments should use a durable implementation (e.g. `RedisIamDeltaIdMapStore` or a SQL-backed
318
+ * store) so the externalId -> provider-id cache survives restarts and is shared across instances.
319
+ */
320
+ declare class InMemoryIamDeltaIdMapStore implements IamDeltaIdMapStore3 {
321
+ private readonly map;
322
+ get(key: IamDeltaIdMapKey): Promise<string | undefined>;
323
+ set(key: IamDeltaIdMapKey, providerId: string): Promise<void>;
324
+ delete(key: IamDeltaIdMapKey): Promise<void>;
325
+ }
326
+ import { CapabilityKey, ModuleName } from "@ionite/core";
327
+ type CapabilityDescriptor = {
328
+ /**
329
+ * Stable machine key (also the catalog map key). This is the SAME key the runtime `capabilitiez`
330
+ * manifest uses ({@link CapabilityKey}); the catalog is generated from that enum (AUDIT_HISTORY.md §11.3) so
331
+ * the two cannot drift and no manual key bridge is required.
332
+ */
333
+ key: CapabilityKey;
334
+ /** Owning module. */
335
+ module: ModuleName;
336
+ /** Human-readable label for ECI catalog UIs. */
337
+ label: string;
338
+ /** Capability the application must wire for this feature to be `capable` (informational). */
339
+ requires?: string;
340
+ /** Reason surfaced when `requested` but not `capable`. */
341
+ incapableReason?: string;
342
+ };
343
+ declare const ION_CAPABILITY_CATALOG: readonly [{
344
+ readonly key: "sso.login";
345
+ readonly module: "sso";
346
+ readonly label: "SSO login (workforce single sign-on)";
347
+ }, {
348
+ readonly key: "sso.jit";
349
+ readonly module: "sso";
350
+ readonly label: "SSO just-in-time user provisioning";
351
+ }, {
352
+ readonly key: "sso.logout";
353
+ readonly module: "sso";
354
+ readonly label: "SSO logout";
355
+ }, {
356
+ readonly key: "sso.back_channel_logout";
357
+ readonly module: "sso";
358
+ readonly label: "SSO back-channel logout";
359
+ }, {
360
+ readonly key: "ciam.magic_link";
361
+ readonly module: "ciam";
362
+ readonly label: "CIAM magic-link login";
363
+ }, {
364
+ readonly key: "ciam.oidc_login";
365
+ readonly module: "ciam";
366
+ readonly label: "CIAM third-party OIDC login";
367
+ }, {
368
+ readonly key: "ciam.customer";
369
+ readonly module: "ciam";
370
+ readonly label: "CIAM customer profile resolution";
371
+ }, {
372
+ readonly key: "ciam.logout";
373
+ readonly module: "ciam";
374
+ readonly label: "CIAM logout";
375
+ }, {
376
+ readonly key: "iam.users";
377
+ readonly module: "iam";
378
+ readonly label: "SCIM Users inbound";
379
+ readonly requires: "userStore";
380
+ readonly incapableReason: "SCIM Users endpoint is enabled by RemoteConfig but no user store capability is wired by the application.";
381
+ }, {
382
+ readonly key: "iam.groups";
383
+ readonly module: "iam";
384
+ readonly label: "SCIM Groups inbound";
385
+ readonly requires: "AccessCatalogStore";
386
+ readonly incapableReason: "SCIM Groups endpoint is enabled by RemoteConfig but no AccessCatalogStore capability is wired by the application.";
387
+ }, {
388
+ readonly key: "workload.incoming";
389
+ readonly module: "workload";
390
+ readonly label: "Workload incoming token validation";
391
+ }, {
392
+ readonly key: "workload.outgoing";
393
+ readonly module: "workload";
394
+ readonly label: "Workload outgoing token minting";
395
+ }, {
396
+ readonly key: "authz.oaa";
397
+ readonly module: "authz";
398
+ readonly label: "Authz OAA projection (Veza)";
399
+ readonly requires: "AccessAssignmentStore + AccessCatalogStore";
400
+ readonly incapableReason: "OAA projection is configured by RemoteConfig but no access/catalog store capability is wired by the application.";
401
+ }];
402
+ import { BaseCustomer as BaseCustomer2, MagicLink, MagicLinkStore } from "@ionite/core";
403
+ /**
404
+ * In-memory implementation of MagicLinkStore from @ionite/core.
405
+ */
406
+ declare class InMemoryMagicLinkStore<TExtended = Record<string, never>> implements MagicLinkStore<TExtended> {
407
+ private magicLinks;
408
+ create(token: string, customer: BaseCustomer2, expiresAt: Date): Promise<void>;
409
+ get(token: string): Promise<MagicLink<TExtended> | null>;
410
+ getAndDelete(token: string): Promise<MagicLink<TExtended> | null>;
411
+ delete(token: string): Promise<void>;
412
+ }
413
+ import { Ionite as Ionite2, ResolvedRoute, RoutingOptions } from "@ionite/core";
414
+ /**
415
+ * Deny-by-default authorization gate for a resolved SDK route. Returns the 401/403 `Response` to
416
+ * short-circuit with when the caller is not authorized, or `undefined` to proceed. Enforcement reuses
417
+ * the request-level `hasPermission` helper so a granted entitlement OR a matching OAuth scope (when
418
+ * `RemoteConfig.authz.scopesAsEntitlements` is enabled) satisfies the gate; the `response()` already
419
+ * logs the denial server-side without leaking the missing permission to the caller.
420
+ */
421
+ declare function enforceEndpointAuthz(request: Request, ion: Ionite2, required: NonNullable<ResolvedRoute["requiredPermission"]>): Promise<Response | undefined>;
422
+ /**
423
+ * Resolve the default route for a request using the deterministic, basePath-derived endpoint paths.
424
+ * Endpoint URLs are no longer read from per-module config; they are computed from `basePath`.
425
+ */
426
+ declare function resolveDefaultRoute(request: Request, ion: Ionite2, urls: UrlMap, pathname?: string): ResolvedRoute | undefined;
427
+ declare function routeRequest(request: Request, ion: Ionite2, routing?: RoutingOptions, urls?: UrlMap): Promise<Response>;
428
+ import { IamDeltaIdMapStore as IamDeltaIdMapStore4, IamDeltaResourceType, Logger as Logger2 } from "@ionite/core";
429
+ type FetchLike2 = (input: string, init?: RequestInit) => Promise<Response>;
430
+ /**
431
+ * Outcome of an outbound membership patch:
432
+ * - `'pushed'` — the destination accepted the mutation (2xx).
433
+ * - `'skipped'` — the mutation was not attempted (subject/target not resolvable at the destination).
434
+ * - `'error'` — the destination rejected the mutation (non-2xx); the caller surfaces this as a
435
+ * degraded egress / reconcile error so it is never mistaken for success (§4.8/§19.1).
436
+ */
437
+ type ScimPatchOutcome = "pushed" | "skipped" | "error";
438
+ /**
439
+ * Result of resolving a destination provider id. A lookup/create HTTP failure is `'error'`, NOT
440
+ * `'absent'`: conflating the two would let a transient 401/500 masquerade as "no such resource",
441
+ * which either triggers a spurious create (duplicate resources) or a misleading `'skipped'` outcome.
442
+ * - `{ found }` — a confirmed provider id (from cache, filter match, or create).
443
+ * - `'absent'` — the destination confirmed no resource exists for this `externalId` (2xx, empty).
444
+ * - `'error'` — the lookup/create could not be completed (non-2xx); treat as degraded egress.
445
+ */
446
+ type ProviderIdResolution = {
447
+ found: string;
448
+ } | "absent" | "error";
449
+ /** Minimal create payload used only when the destination has no resource for an `externalId`. */
450
+ type ScimOutboundCreate = {
451
+ /** SCIM `userName` (required for Users) or unused for Groups. */
452
+ userName?: string;
453
+ /** SCIM `displayName` (required for Groups; optional for Users). */
454
+ displayName?: string;
455
+ };
456
+ type ScimOutboundClientOptions = {
457
+ /** Destination SCIM base URL (e.g. https://iam.example.com/scim/v2). */
458
+ baseUrl: string;
459
+ /** Mints a bearer token for the destination audience (workload outgoing client). */
460
+ getToken: () => Promise<string>;
461
+ /** Durable externalId -> provider-id cache (required). */
462
+ idMapStore: IamDeltaIdMapStore4;
463
+ /** Tenant scope for the mapping cache. */
464
+ tenantId?: string;
465
+ log: Logger2;
466
+ /** Injectable fetch (defaults to global fetch); enables testing against a mock SCIM server. */
467
+ fetchImpl?: FetchLike2;
468
+ };
469
+ /**
470
+ * Escape a value interpolated into a SCIM filter string literal (RFC 7644 §3.4.2.2). Filter string
471
+ * comparison values are JSON strings, so a literal backslash and double-quote must be escaped
472
+ * (backslash first, then quote) — otherwise an id containing `"` would break out of the literal and
473
+ * corrupt the filter (or the remove path), silently matching the wrong resource. IDs are SDK-managed
474
+ * externalIds/provider ids, but this closes the injection/corruption vector regardless of source.
475
+ */
476
+ declare function escapeScimFilterValue(value: string): string;
477
+ /**
478
+ * externalId-only outbound SCIM client for IAM delta `'patch'` egress.
479
+ *
480
+ * Resolves a destination provider id for a resource STRICTLY by `externalId` (never email,
481
+ * employeeId, samAccountName, or any custom attribute), backed by a durable mapping cache:
482
+ *
483
+ * - cache hit -> use the stored provider id
484
+ * - miss -> `GET /{Resource}?filter=externalId eq "..."` -> store + use the returned id
485
+ * - empty -> `POST /{Resource}` (create) -> store + use the returned id
486
+ * - stale (404/410 on a subsequent write) -> drop the mapping, re-resolve, retry once
487
+ *
488
+ * Concurrent resolutions for the same `(destination, resourceType, externalId)` are single-flighted.
489
+ */
490
+ declare class ScimOutboundClient {
491
+ private readonly baseUrl;
492
+ private readonly getToken;
493
+ private readonly idMapStore;
494
+ private readonly tenantId?;
495
+ private readonly log;
496
+ private readonly fetchImpl;
497
+ private readonly inflight;
498
+ constructor(options: ScimOutboundClientOptions);
499
+ private headers;
500
+ private inflightKey;
501
+ /**
502
+ * Resolve (or create) the destination provider id for a resource identified by `externalId`.
503
+ * Returns a discriminated {@link ProviderIdResolution}: a confirmed id, `'absent'` (destination has
504
+ * no such resource and no `create` was supplied/succeeded), or `'error'` (lookup/create failed).
505
+ */
506
+ resolveProviderId(resourceType: IamDeltaResourceType, externalId: string, create?: ScimOutboundCreate): Promise<ProviderIdResolution>;
507
+ private resolveUncached;
508
+ private filterByExternalId;
509
+ private create;
510
+ /** Drop a possibly-stale mapping so the next resolve re-filters the destination. */
511
+ private invalidate;
512
+ /**
513
+ * Add or remove a user (by `userExternalId`) from a group (by `groupExternalId`) at the destination.
514
+ * Resolves both ids by externalId, issues a SCIM PATCH, and self-heals once on a stale group id.
515
+ */
516
+ patchGroupMembership(args: {
517
+ groupExternalId: string;
518
+ userExternalId: string;
519
+ op: "add" | "remove";
520
+ groupCreate?: ScimOutboundCreate;
521
+ userCreate?: ScimOutboundCreate;
522
+ }): Promise<ScimPatchOutcome>;
523
+ }
524
+ import { SecretsSource as SecretsSource6, VaultLfvSecretsConfig } from "@ionite/core";
525
+ type LfvSecretsSource = SecretsSource6 & {
526
+ isDeliveryRequest(request: Request): boolean;
527
+ isEventsRequest(request: Request): boolean;
528
+ handleDelivery(request: Request): Promise<Response>;
529
+ handleEvents(request: Request): Promise<Response>;
530
+ /** Normalized inbound delivery path this source claims (used to enforce global uniqueness at apply time). */
531
+ readonly deliveryPath: string;
532
+ /** Normalized inbound events path this source claims (used to enforce global uniqueness at apply time). */
533
+ readonly eventsPath: string;
534
+ /** Normalized `serverUrl|path` callback-hub key this source registers under. */
535
+ readonly callbackKey: string;
536
+ /** Stable identity of this source's verify JWKS trust material (undefined when unconfigured). */
537
+ readonly trustId?: string;
538
+ };
539
+ declare function lfvVault(config: VaultLfvSecretsConfig): LfvSecretsSource;
540
+ import { SessionStore } from "@ionite/core";
541
+ /**
542
+ * Options for {@link createSessionReadCache}. Values are the already-clamped internal (ms/count)
543
+ * form produced by the core `resolveSessionCacheConfig` from the portable
544
+ * `RemoteConfig.cache.session.read` block.
545
+ */
546
+ type SessionReadCacheOptions = {
547
+ /** Max time a cached `get(sid)` result may be served, in ms. */
548
+ ttlMs: number;
549
+ /** Max distinct session ids held at once (bounded LRU). */
550
+ maxEntries: number;
551
+ };
552
+ /**
553
+ * Instance-local read-through cache decorator for a {@link SessionStore}. DEFAULT-OFF: only wired
554
+ * when `RemoteConfig.cache.session.read.enabled === true`. It is the "monolith knob" — it trades a
555
+ * bounded amount of cross-instance staleness (a logout/refresh on another node is visible after at
556
+ * most `ttlMs`) for eliminating repeated `get(sid)` round-trips to a Redis/DB session store on
557
+ * hot paths.
558
+ *
559
+ * Semantics (portable contract):
560
+ * - `get(sid)` is served from a bounded LRU for up to `ttlMs`; on miss it reads through and caches.
561
+ * - **Misses are NOT cached** (a null result never populates the cache), which avoids a login race
562
+ * (a session created moments later would otherwise be masked) and an unauthenticated-flood memory
563
+ * vector (only valid sids ever become keys).
564
+ * - `update`/`delete` write through to the underlying store AND invalidate the local entry, so a
565
+ * same-process logout/refresh is immediately visible.
566
+ * - `create` writes through; it does not pre-populate the cache (kept lazy; the first `get` caches).
567
+ * - Lazy TTL expiry only (no timers/sweepers — safe in serverless): an expired entry is dropped on
568
+ * the next access.
569
+ *
570
+ * A fresh instance is created on every RemoteConfig apply (the caller drops the previous one) so a
571
+ * live re-tune never leaks the old cache.
572
+ */
573
+ declare function createSessionReadCache<TExtended = Record<string, never>>(store: SessionStore<TExtended>, options: SessionReadCacheOptions): SessionStore<TExtended>;
574
+ import { Session, SessionStore as SessionStore2 } from "@ionite/core";
575
+ /**
576
+ * In-memory session store implementation using Maps.
577
+ *
578
+ * Lazily purges records whose `expiresAt` has passed on `get`, mirroring the physical expiry a
579
+ * TTL-backed store (Redis `EX`) gives for free (AUDIT_HISTORY.md §1.3). There is no background sweep, so an
580
+ * abandoned session lingers in the Map until it is next looked up; for unbounded-lifetime deployments
581
+ * prefer a TTL-backed store.
582
+ */
583
+ declare class InMemorySessionStore<TExtended = Record<string, never>> implements SessionStore2<TExtended> {
584
+ private sessions;
585
+ create(session: Session<TExtended>): Promise<void>;
586
+ get(sid: string): Promise<Session<TExtended> | null>;
587
+ update(sid: string, data: Partial<Session<TExtended>>): Promise<void>;
588
+ delete(sid: string): Promise<void>;
589
+ }
590
+ import { ConfigSource as ConfigSource8, ConfigSourceResolver as ConfigSourceResolver2, TenantConfigLocator as TenantConfigLocator2 } from "@ionite/core";
591
+ import { ConfigLocator as ConfigLocator2, ConfigSource as ConfigSource7 } from "@ionite/core";
592
+ /**
593
+ * Materialize a runtime {@link ConfigSource} from a full, self-contained {@link ConfigLocator}. This
594
+ * is the ROOT/bootstrap path (env vars via `envConfig()`, or a bootstrap secret via
595
+ * `secretConfigSource()`): the locator carries the connection details/credentials needed to build a
596
+ * fresh source. Tenants do NOT use this path — see {@link tenantConfigSourceFromResolver}.
597
+ */
598
+ declare function configSourceFromLocator(locator: ConfigLocator2): ConfigSource7;
599
+ /**
600
+ * Context threaded into tenant config-source validation so a locator can be checked against the tenant
601
+ * that owns it. INVARIANT: each tenant MUST resolve to a DISTINCT backing config path (e.g. a unique
602
+ * vault path). Two tenants sharing a config path would cross-load each other's RemoteConfig.
603
+ */
604
+ type ConfigLocatorContext = {
605
+ tenantId?: string;
606
+ };
607
+ /**
608
+ * Optional validation hook invoked before a tenant's locator is materialized. Implementations should
609
+ * throw when a locator is not valid (e.g. a shared/duplicate config path). The shared source client
610
+ * may still be reused across tenants; only the resolved path must differ per tenant.
611
+ */
612
+ type ConfigLocatorAssert = (input: {
613
+ locator: TenantConfigLocator2;
614
+ }) => void;
615
+ declare function registerConfigLocatorAssert(assert: ConfigLocatorAssert | undefined): void;
616
+ /**
617
+ * Materialize a tenant's {@link ConfigSource} from its {@link TenantConfigLocator}, resolving against
618
+ * the root ES (`ion.secret` / `ion.secrets`). Pass the root `es` (or any {@link ConfigSourceResolver}).
619
+ *
620
+ * This lives in `@ionite/server` (not core) so the materialization can reference the
621
+ * server-only source implementations directly, with no import-time registration side effect — keeping
622
+ * every published package truthfully `sideEffects: false`.
623
+ */
624
+ declare function tenantConfigSource(locator: TenantConfigLocator2, ion: ConfigSourceResolver2): ConfigSource8;
625
+ import { Ionite as Ionite3, TenantPathNamespace, TenantRoutingStrategy } from "@ionite/core";
626
+ type MaybePromise<T> = Promise<T> | T;
627
+ type TenantResolutionContext = {
628
+ namespace: "api" | "ui";
629
+ restPath: string;
630
+ restSegments: string[];
631
+ };
632
+ type ResolveTenantFromRequestOptions = {
633
+ strategy?: TenantRoutingStrategy;
634
+ namespace?: "api" | "ui";
635
+ /**
636
+ * App-owned resolver for tenants that are not encoded in the URL path. The SDK does not infer a
637
+ * tenant from unverified input (JWT/header/host/cookie); if you need that, implement it here and
638
+ * verify it yourself before returning an id.
639
+ */
640
+ custom?: (request: Request) => MaybePromise<string | null | undefined>;
641
+ };
642
+ type CreateTenantIonResolverOptions = {
643
+ store: {
644
+ getIon(id: string): Promise<Ionite3 | undefined>;
645
+ };
646
+ getIon?: never;
647
+ } | {
648
+ store?: never;
649
+ getIon: (id: string) => Promise<Ionite3 | null | undefined>;
650
+ };
651
+ type TenantIonResolver = (id: string) => Promise<Ionite3 | undefined>;
652
+ type CreateTenantIonHandlerOptions = {
653
+ getIon: (id: string, request: Request) => MaybePromise<Ionite3 | null | undefined>;
654
+ resolveTenant?: (request: Request) => MaybePromise<{
655
+ id: string;
656
+ context?: TenantResolutionContext;
657
+ } | string | null | undefined>;
658
+ rewriteRequest?: (request: Request, details: {
659
+ id: string;
660
+ ion: Ionite3;
661
+ context?: TenantResolutionContext;
662
+ }) => MaybePromise<Request>;
663
+ onMissingTenant?: (request: Request) => MaybePromise<Response>;
664
+ onMissingIonite?: (request: Request, id: string) => MaybePromise<Response>;
665
+ };
666
+ declare function createTenantIonResolver(options: CreateTenantIonResolverOptions): TenantIonResolver;
667
+ declare function resolveTenantFromRequest(request: Request, options?: ResolveTenantFromRequestOptions): Promise<{
668
+ id: string;
669
+ context?: TenantResolutionContext;
670
+ } | null>;
671
+ declare function rewriteRequestPath(request: Request, pathname: string): Request;
672
+ declare function rewriteRequestForTenantPath(request: Request, id: string, sourceNamespace: TenantPathNamespace, targetNamespace: TenantPathNamespace): Request;
673
+ declare function rewriteRequestForDefaultTenantApi(request: Request, id: string, targetNamespace?: TenantPathNamespace): Request;
674
+ declare function rewriteRequestForDefaultTenantUi(request: Request, id: string, targetNamespace?: TenantPathNamespace): Request;
675
+ declare function createTenantIonHandler(options: CreateTenantIonHandlerOptions): (request: Request) => Promise<Response>;
676
+ import { BaseUser as BaseUser2, ListOptions as ListOptions4, ListResult as ListResult4, UserStore as UserStore2 } from "@ionite/core";
677
+ /**
678
+ * In-memory user store implementation using Maps.
679
+ */
680
+ declare class InMemoryUserStore<TExtended = Record<string, never>> implements UserStore2<TExtended> {
681
+ private users;
682
+ private userNameIndex;
683
+ get(id: string): Promise<BaseUser2<TExtended> | undefined>;
684
+ lookup(user: BaseUser2<TExtended>): Promise<BaseUser2<TExtended> | undefined>;
685
+ upsert(user: BaseUser2<TExtended>): Promise<BaseUser2<TExtended>>;
686
+ delete(sub: string): Promise<number>;
687
+ list(options?: ListOptions4): Promise<ListResult4<BaseUser2<TExtended>>>;
688
+ }
689
+ import { SecretsSource as SecretsSource8, VaultSecretsConfig as VaultSecretsConfig3 } from "@ionite/core";
690
+ import { Secret, SecretsSource as SecretsSource7, SecretsSubscriptionOptions, VaultSecretsConfig as VaultSecretsConfig2 } from "@ionite/core";
691
+ declare class VaultSubscribeNotSupportedError extends Error {
692
+ readonly sourceType: string;
693
+ readonly path: string;
694
+ constructor(sourceType: string, path: string, message?: string);
695
+ }
696
+ declare const VAULT_MIN_POLLING_TTL_MS = 6e5;
697
+ /** Optional health hooks so a polling source can flip its degraded `isAvailable()` state. */
698
+ type HttpPollingHooks = {
699
+ /** Invoked after every poll attempt (initial read + each tick) with the observed health. */
700
+ onHealth?: (healthy: boolean) => void;
701
+ };
702
+ /**
703
+ * Subscribe to a secret path via HTTP polling using a self-scheduling `setTimeout` chain. The next
704
+ * poll is scheduled only after the current one settles, so polls can never overlap or pile up during
705
+ * a slow/failing upstream (no `setInterval` storm). The returned unsubscribe sets a `closed` flag and
706
+ * cancels any pending timer/sleep, so teardown halts all activity immediately even mid-outage.
707
+ */
708
+ declare function subscribeSecretPathWithHttpPolling<T>(source: Pick<SecretsSource7, "getFullSecret">, path: string, onChange: (fullSecret: Secret<T>) => void, ttl: number, log?: VaultSecretsConfig2["log"], options?: SecretsSubscriptionOptions, hooks?: HttpPollingHooks): Promise<() => void>;
709
+ /**
710
+ * Override the per-command websocket timeout (ms). Primarily a diagnostic/test knob; production code
711
+ * should leave the 30s default in place.
712
+ */
713
+ declare function setVaultWebSocketCommandTimeout(ms: number): void;
714
+ /** Test/diagnostic helper: number of live sockets and total registered subscriptions. */
715
+ declare function vaultSocketDiagnostics(): {
716
+ urls: string[];
717
+ sockets: number;
718
+ subscriptions: number;
719
+ };
720
+ /** Test helper: force-close every vault socket in the process. */
721
+ declare function closeAllVaultSockets(): void;
722
+ type OpenbaoVaultOptions = Omit<VaultSecretsConfig3, "type"> & {
723
+ type?: "openbao" | "vault";
724
+ };
725
+ /**
726
+ * Compatibility helper for OpenBao/Hashicorp-compatible vault endpoints.
727
+ * Internally uses the standard vault secrets source implementation.
728
+ */
729
+ declare function openbaoVault(options: OpenbaoVaultOptions): SecretsSource8;
730
+ import { Workload as CoreWorkload, FrameworkWorkloadConfig, Ionite as Ionite4, JWTAssertionClaims, Logger as Logger4, TokenValidationResult, TrustedIdp, WorkloadConfigMap, WorkloadGetToken, WorkloadIdentityStore as WorkloadIdentityStore2, WorkloadIncomingOutgoing, WorkloadTokenResponse, WorkloadTokenStore } from "@ionite/core";
731
+ import { StandardSchemaV1 } from "@standard-schema/spec";
732
+ /**
733
+ * Common fields shared across all workload authentication modes
734
+ */
735
+ type WorkloadConfigBase = {
736
+ /**
737
+ * App workload token endpoint path (e.g. /api/ionite/tenant/workload/token).
738
+ * Used for routing; set in code.
739
+ */
740
+ tokenUrl?: string;
741
+ /**
742
+ * IdP OAuth2 token endpoint URL (e.g. https://sso.example.com/.../token).
743
+ * REQUIRED for client role (token acquisition); typically set in vault.
744
+ */
745
+ idpTokenUrl?: string;
746
+ /**
747
+ * JWKS endpoint URL for public key retrieval
748
+ * REQUIRED for server role (token validation)
749
+ */
750
+ jwksUri?: string;
751
+ /**
752
+ * Expected token issuer URL for validation
753
+ * RECOMMENDED for server role (token validation)
754
+ */
755
+ issuer?: string;
756
+ /**
757
+ * Target audience for tokens
758
+ */
759
+ audience?: string;
760
+ /**
761
+ * Per-IdP scopesAsEntitlements intent (incoming/server role only). See core `WorkloadConfigBase`.
762
+ */
763
+ scopesAsEntitlements?: boolean;
764
+ /**
765
+ * Default OAuth2 scopes (space-delimited)
766
+ */
767
+ scope?: string;
768
+ /**
769
+ * JWT assertion/token lifetime in seconds
770
+ * @default 300 (5 minutes)
771
+ */
772
+ tokenLifetime?: number;
773
+ /**
774
+ * Refresh threshold in seconds (refresh token this many seconds before expiry)
775
+ * @default 60
776
+ */
777
+ refreshThreshold?: number;
778
+ /**
779
+ * Optional token store for caching access tokens
780
+ */
781
+ tokenStore?: WorkloadTokenStore;
782
+ /**
783
+ * Optional registry/enrichment store for workload principals. When set, `getWorkloadIdentity`
784
+ * merges stored attributes (e.g. `displayName`, `active`, default `scopes`) onto the validated
785
+ * token identity. Authorization works without it; see {@link WorkloadIdentityStore}.
786
+ */
787
+ workloadIdentityStore?: WorkloadIdentityStore2;
788
+ /**
789
+ * Automatically refresh tokens before expiration
790
+ * @default true
791
+ */
792
+ autoRefresh?: boolean;
793
+ /**
794
+ * Optional RFC 7009 token revocation endpoint
795
+ */
796
+ revocationEndpoint?: string;
797
+ /**
798
+ * Optional handler defaults (merged with per-call overrides in `handler`)
799
+ */
800
+ validateUrl?: string;
801
+ jwksUrl?: string;
802
+ refreshUrl?: string;
803
+ validators?: WorkloadValidators;
804
+ };
805
+ type WorkloadValidators = {
806
+ jwtAssertionClaims: StandardSchemaV1<unknown, JWTAssertionClaims>;
807
+ tokenResponse: StandardSchemaV1<unknown, WorkloadTokenResponse>;
808
+ };
809
+ /**
810
+ * JWT Bearer Grant (RFC 7523) Configuration
811
+ *
812
+ * Used for SPIFFE-style workload identities where services have their own
813
+ * cryptographic identity and sign their own JWT assertions.
814
+ *
815
+ * @example
816
+ * ```json
817
+ * {
818
+ * "token_url": "https://auth.example.com/oauth/token",
819
+ * "jwks_uri": "https://auth.example.com/.well-known/jwks.json",
820
+ * "workload_id": "spiffe://trust-domain/ns/service",
821
+ * "audience": "https://auth.example.com/oauth/token",
822
+ * "private_key": "-----BEGIN PRIVATE KEY-----...",
823
+ * "algorithm": "RS256"
824
+ * }
825
+ * ```
826
+ */
827
+ type JwtBearerWorkloadConfig = WorkloadConfigBase & {
828
+ /**
829
+ * Workload identifier (e.g., SPIFFE ID: spiffe://trust-domain/namespace/service)
830
+ * REQUIRED for JWT Bearer Grant mode
831
+ */
832
+ workloadId: string;
833
+ /**
834
+ * PEM-encoded private key for signing JWT assertions
835
+ * REQUIRED for client role in JWT Bearer Grant mode
836
+ */
837
+ privateKey: string;
838
+ /**
839
+ * Key ID (kid) to include in JWT header for key rotation support
840
+ */
841
+ keyId?: string;
842
+ /**
843
+ * JWT signing algorithm
844
+ * @default 'RS256'
845
+ */
846
+ algorithm?: "RS256" | "RS384" | "RS512" | "ES256" | "ES384" | "ES512";
847
+ };
848
+ /**
849
+ * OAuth2 Client Credentials Configuration
850
+ *
851
+ * Standard OAuth2 Client Credentials Grant (RFC 6749 Section 4.4).
852
+ * Used with identity providers like Keycloak for service-to-service authentication.
853
+ *
854
+ * @example
855
+ * ```json
856
+ * {
857
+ * "token_url": "https://sso.example.com/realms/myrealm/protocol/openid-connect/token",
858
+ * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
859
+ * "client_id": "my-service",
860
+ * "client_secret": "secret-from-idp",
861
+ * "issuer": "https://sso.example.com/realms/myrealm",
862
+ * "scope": "api:read api:write"
863
+ * }
864
+ * ```
865
+ */
866
+ type ClientCredentialsWorkloadConfig = WorkloadConfigBase & {
867
+ /**
868
+ * OAuth2 client identifier registered with the authorization server
869
+ * REQUIRED for Client Credentials mode
870
+ */
871
+ clientId: string;
872
+ /**
873
+ * OAuth2 client secret
874
+ * REQUIRED for Client Credentials mode
875
+ */
876
+ clientSecret: string;
877
+ };
878
+ /**
879
+ * Server-Only Workload Configuration
880
+ *
881
+ * Used when a service only needs to validate incoming workload tokens,
882
+ * not acquire tokens for outbound calls. Requires only jwks_uri for
883
+ * public key retrieval.
884
+ *
885
+ * Note: jwksUri and issuer are optional in TypeScript (since they come from vault)
886
+ * but are required at runtime and validated by standardSchema validators.
887
+ *
888
+ * @example
889
+ * ```json
890
+ * {
891
+ * "jwks_uri": "https://sso.example.com/realms/myrealm/protocol/openid-connect/certs",
892
+ * "issuer": "https://sso.example.com/realms/myrealm"
893
+ * }
894
+ * ```
895
+ */
896
+ type ServerOnlyWorkloadConfig = WorkloadConfigBase & {
897
+ /**
898
+ * JWKS endpoint URL for public key retrieval
899
+ * REQUIRED at runtime (validated by standardSchema), optional in TypeScript (comes from vault)
900
+ */
901
+ jwksUri?: string;
902
+ };
903
+ /**
904
+ * Workload Identity Authentication Configuration
905
+ *
906
+ * Union type supporting multiple authentication modes. The mode is automatically
907
+ * detected based on which fields are present:
908
+ *
909
+ * - **JWT Bearer Grant**: Requires `workload_id` + `private_key`
910
+ * - **Client Credentials**: Requires `client_id` + `client_secret`
911
+ * - **Server-Only**: Only `jwks_uri` (and optionally `issuer`) for token validation
912
+ *
913
+ * The developer uses the same API regardless of mode - the library handles the details.
914
+ */
915
+ type WorkloadConfig = JwtBearerWorkloadConfig | ClientCredentialsWorkloadConfig | ServerOnlyWorkloadConfig;
916
+ /**
917
+ * Workload Identity extracted from validated tokens
918
+ */
919
+ type WorkloadIdentity = {
920
+ /**
921
+ * Workload identifier (for JWT Bearer Grant tokens)
922
+ */
923
+ workloadId?: string;
924
+ /**
925
+ * Client identifier (for OAuth2 Client Credentials tokens)
926
+ */
927
+ clientId?: string;
928
+ /**
929
+ * Granted scopes
930
+ */
931
+ scope?: string;
932
+ /**
933
+ * Whether the trusted IdP that validated this token opts into honoring its OAuth `scope` claim as
934
+ * entitlements (per-IdP intent, resolved at validation time).
935
+ */
936
+ scopesAsEntitlements?: boolean;
937
+ /**
938
+ * Enterprise Controlled ID for this workload (`ecid:<realm>:<opaque>`), when carried on the token
939
+ * `ecid` claim or enriched from a wired `WorkloadIdentityStore`. A workload is its own governance
940
+ * root. See https://ionite.com/docs/llms-ecid.txt.
941
+ */
942
+ ecid?: string;
943
+ /**
944
+ * Full JWT claims from the token
945
+ */
946
+ claims: JWTAssertionClaims;
947
+ };
948
+ /**
949
+ * Workload Identity Authentication Interface
950
+ */
951
+ type Workload3 = {
952
+ config: WorkloadConfig;
953
+ beforeChange?: (listener: () => void) => () => void;
954
+ afterChange?: (listener: () => void) => () => void;
955
+ isReady?: () => boolean;
956
+ ready?: (timeout?: number) => Promise<void>;
957
+ getToken: WorkloadGetToken;
958
+ refreshToken: (scope?: string) => Promise<WorkloadTokenResponse>;
959
+ generateJWTAssertion: (scope?: string) => Promise<string>;
960
+ revokeToken: (token: string) => Promise<void>;
961
+ validateToken: (token: string) => Promise<TokenValidationResult>;
962
+ getWorkloadIdentity: (request: Request) => Promise<WorkloadIdentity | undefined>;
963
+ parseJWT: (token: string) => Promise<JWTAssertionClaims>;
964
+ handler: (request: Request) => Promise<Response>;
965
+ };
966
+ declare function workload(validators: WorkloadValidators, log: Logger4, fromVault?: Partial<WorkloadConfig> | WorkloadConfigMap | WorkloadIncomingOutgoing, fromCode?: FrameworkWorkloadConfig, incomingTrustOverride?: TrustedIdp[]): Workload3 | undefined;
967
+ /**
968
+ * Get the workload identity from an incoming request.
969
+ */
970
+ declare function getWorkload(request: Request, ion: Ionite4): Promise<WorkloadIdentity | undefined>;
971
+ /**
972
+ * Get an access token for the configured workload identity.
973
+ * Client is required and must be one of the configured outgoing workload clients.
974
+ * Throws when no outgoing clients are configured on this Ionite instance.
975
+ * Scope defaults to the config for the chosen client (e.g. workload.outgoing.<name>.scope); pass scope to override.
976
+ */
977
+ declare function getWorkloadToken(client: string, ion: Ionite4, scope?: string): Promise<string>;
978
+ /**
979
+ * Validate a workload token from an incoming request.
980
+ * Accepts either an Ionite instance (uses its workload) or a Workload instance directly
981
+ * (e.g. a server-only Workload used for tenant creation auth).
982
+ */
983
+ declare function validateWorkloadToken(request: Request, ionOrWorkload: Ionite4 | CoreWorkload): Promise<TokenValidationResult>;
984
+ /**
985
+ * Revoke a workload access token.
986
+ */
987
+ declare function revokeWorkloadToken(token: string, ion: Ionite4): Promise<void>;
988
+ /**
989
+ * Framework-agnostic request handler for the Workload module (token, validate, jwks, refresh routes).
990
+ */
991
+ declare function workloadHandler(request: Request, ion: Ionite4): Promise<Response>;
992
+ import { BaseWorkloadIdentity, ListOptions as ListOptions5, ListResult as ListResult5, WorkloadIdentityStore as WorkloadIdentityStore3 } from "@ionite/core";
993
+ /**
994
+ * In-memory {@link WorkloadIdentityStore} implementation using Maps. Suitable for development and
995
+ * testing; use a durable store (e.g. Redis/SQL) in production.
996
+ */
997
+ declare class InMemoryWorkloadIdentityStore<TExtended = Record<string, never>> implements WorkloadIdentityStore3<TExtended> {
998
+ private identities;
999
+ private clientIdIndex;
1000
+ get(id: string): Promise<BaseWorkloadIdentity<TExtended> | undefined>;
1001
+ lookup(identity: BaseWorkloadIdentity<TExtended>): Promise<BaseWorkloadIdentity<TExtended> | undefined>;
1002
+ upsert(identity: BaseWorkloadIdentity<TExtended>): Promise<BaseWorkloadIdentity<TExtended>>;
1003
+ delete(id: string): Promise<number>;
1004
+ list(options?: ListOptions5): Promise<ListResult5<BaseWorkloadIdentity<TExtended>>>;
1005
+ }
1006
+ import { CachedWorkloadToken, WorkloadTokenStore as WorkloadTokenStore2 } from "@ionite/core";
1007
+ /**
1008
+ * In-memory workload token store implementation using Maps.
1009
+ */
1010
+ declare class InMemoryWorkloadTokenStore<TExtended = object> implements WorkloadTokenStore2<TExtended> {
1011
+ private tokens;
1012
+ set(token: CachedWorkloadToken<TExtended>): Promise<void>;
1013
+ get(workload_id: string): Promise<CachedWorkloadToken<TExtended> | null>;
1014
+ delete(workload_id: string): Promise<void>;
1015
+ isValid(workload_id: string): Promise<boolean>;
1016
+ cleanup(): Promise<void>;
1017
+ }
1018
+ /**
1019
+ * Config object for ionite(source, config). Includes FrameworkConfig plus
1020
+ * server-only builder hints and lifecycle/routing (stripped before passing to module constructors).
1021
+ */
1022
+ type IoniteConfig<C extends FrameworkConfigInput = FrameworkConfigInput> = C & {
1023
+ basePath?: string;
1024
+ stores?: FrameworkStores2;
1025
+ beforeChange?: ConfigChangeCallback;
1026
+ afterChange?: (ion: Ionite5, config: RemoteConfig2, frameworkConfig: ModifiableFrameworkConfig, oldConfig: RemoteConfig2 | undefined) => void;
1027
+ routing?: RoutingOptions2;
1028
+ configRetry?: RemoteConfigRetryOptions;
1029
+ onConfigLoadError?: RemoteConfigRetryHook;
1030
+ };
1031
+ declare function ionite<const C extends FrameworkConfigInput = FrameworkConfigInput>(source: ConfigSource9, config: IoniteConfig<C> | FrameworkConfigInput): Ionite5;
1032
+ export { workloadHandler, workload, withSetCookies, withDefaultExternalId, verifyUser, vaultSocketDiagnostics, vaultConfig, validateWorkloadToken, tenantManager, tenantConfigSource, subscribeSecretPathWithHttpPolling, sso, setVaultWebSocketCommandTimeout, sendTenantWebhook, secretConfigSource, sameOriginRedirectRules, routeRequest, rewriteRequestPath, rewriteRequestForTenantPath, rewriteRequestForDefaultTenantUi, rewriteRequestForDefaultTenantApi, revokeWorkloadToken, resolveTenantFromRequest, resolveRedirect, resolveDefaultRoute, resolveCapturedRedirect, resolveAfterRedirect, registerConfigLocatorAssert, reconcileSubjectAccess, openbaoVault, listSsoClientIdsFromCookies, listGenerator, lfvVault, isVaultConfigWaitingForSecretError, isVaultConfigInvalidPayloadError, isRedirectAllowed, ionite, initiateLogin, iamDeltaIdMapKey, iam, hasScope, hasRole2 as hasRole, hasPermission2 as hasPermission, hasGroup2 as hasGroup, hasAnyScope, hasAny2 as hasAny, hasAllScope, hasAll2 as hasAll, has, getWorkloadToken, getWorkload, getUser, getCustomer, generateULID, gcpConfig, expandRoot, escapeScimFilterValue, envConfig, enforceEndpointAuthz, discoverSessions, discoverSessionRecords, deriveIonUrls, defaultRedirectPolicies, createTenantIonResolver, createTenantIonHandler, createSessionReadCache, createManagedIamDelta, configSourceFromLocator, configSourceFromEnvValues, configLocatorFromEnvValues, collectReferencedDefinitions, closeAllVaultSockets, ciam, captureRedirect, callback, blockDangerousUrls, azureConfig, awsConfig, allowSameOrigin, allowRelativePaths, allowOrigins, allowHosts, VerifyUserOptions, VaultSubscribeNotSupportedError, VaultConfigWaitingForSecretError, VaultConfigSourceOptions, VaultConfigInvalidPayloadError, VAULT_MIN_POLLING_TTL_MS, UrlMap, UpdateTenantRequest2 as UpdateTenantRequest, TenantWebhookPayload, TenantValidators2 as TenantValidators, TenantStatus, TenantResponse, TenantResolutionContext, TenantManagerOptions, TenantManager, TenantIonUrls, TenantIonResolver, SubjectType3 as SubjectType, SubjectScope, SessionResponse, SessionReadCacheOptions, SessionDiscoveryOptions, ScimOutboundCreate, ScimOutboundClientOptions, ScimOutboundClient, ResolveTenantFromRequestOptions, ReferencedDefinitions, RedirectVerdict, RedirectRuleContext, RedirectRule, RedirectResult, RedirectPolicy, RedirectContext, ReconcileStores, OtelSignalConfig, OtelProviderType, OtelOAuthClientCredentialsConfig, OtelMetricsSignalConfig, OtelLogRecord, OtelLogLevel, OtelLevels, OtelConfig, Otel, OpenbaoVaultOptions, LfvSecretsSource, IoniteErrorOptions, IoniteError, IoniteConfig, InMemoryWorkloadTokenStore, InMemoryWorkloadIdentityStore, InMemoryUserStore, InMemoryTenantStore, InMemorySessionStore, InMemoryResourceStore, InMemoryMagicLinkStore, InMemoryIamDeltaIdMapStore, InMemoryCustomerStore, InMemoryAccessIndex, InMemoryAccessCatalogStore, InMemoryAccessAssignmentStore, IamDeltaDeps, IamDeltaConfig, ION_CAPABILITY_CATALOG, HttpPollingHooks, GcpConfigSourceOptions, FrameworkStores3 as FrameworkStores, FrameworkOtelConfig, EnvironmentType, EnvConfigOptions, CreateTenantRequest2 as CreateTenantRequest, CreateTenantIonResolverOptions, CreateTenantIonHandlerOptions, ConfigLocatorContext, ConfigLocatorAssert, CatalogView, CatalogAdjacencyCache, CapabilityKey2 as CapabilityKey, CapabilityDescriptor, BeforeLogoutHook, BeforeLogoutContext, BeforeLoginHook, BeforeLoginContext, AzureConfigSourceOptions, AwsConfigSourceOptions, AfterLogoutHook, AfterLogoutContext, AfterLoginHook, AfterLoginContext, AccessCheckResult, AccessCheckReason };