@objectstack/core 17.0.0 → 17.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Logger, LifecycleEventName, IServiceRegistry, AudienceBindingSuggestionFilter, IObjectQLEngine } from '@objectstack/spec/contracts';
2
- export { EngineSchemaRegistryView, EngineTransactionInfo, EngineTransactionOptions, IDataDriver, IDataEngine, IHttpRequest, IHttpResponse, IHttpServer, IObjectQLEngine, Logger, Middleware, RouteHandler } from '@objectstack/spec/contracts';
2
+ export { EngineSchemaRegistryView, EngineTransactionInfo, EngineTransactionOptions, HttpResponseObservation, HttpResponseObserver, IDataDriver, IDataEngine, IHttpRequest, IHttpResponse, IHttpServer, IObjectQLEngine, Logger, Middleware, RouteHandler, UNMATCHED_ROUTE_PATTERN } from '@objectstack/spec/contracts';
3
3
  import { z } from 'zod';
4
4
  import { LoggerConfig, MigrationOnCrashPolicy, MigrationJournalEvent } from '@objectstack/spec/system';
5
5
  import { ObjectLogger } from './logger.cjs';
@@ -7,7 +7,7 @@ export { createLogger } from './logger.cjs';
7
7
  import * as QA from '@objectstack/spec/qa';
8
8
  import { KeyObject } from 'node:crypto';
9
9
  import { PluginCapability, PluginPermissions as PluginPermissions$1, PluginPermissionSet, ResourceType, PermissionAction, PluginPermission, SandboxConfig, KernelSecurityScanResult, KernelSecurityVulnerability, ExecutionContext, PluginHealthCheckParsed, PluginHealthStatus as PluginHealthStatus$1, PluginHealthReport, HotReloadConfigParsed, VersionConstraint, DependencyConflict, SemanticVersion, CompatibilityLevel } from '@objectstack/spec/kernel';
10
- import { AuthzPosture } from '@objectstack/spec/security';
10
+ import { TenancyPosture, AuthzPosture } from '@objectstack/spec/security';
11
11
  export { nextUtcCalendarDay, utcInstantMs } from '@objectstack/spec/data';
12
12
 
13
13
  /**
@@ -222,6 +222,14 @@ declare class ObjectKernel {
222
222
  constructor(config?: ObjectKernelConfig);
223
223
  /**
224
224
  * Register a plugin with enhanced validation
225
+ *
226
+ * Duplicate names OVERWRITE, with one `warn` naming both versions — the
227
+ * declared contract in `plugin-registration.ts`, applied identically by
228
+ * `LiteKernel.use()` (#9864, maintainer ruling 2026-08-19). The overwrite
229
+ * itself is unchanged: it is what lets an app config's `plugins` entry
230
+ * supersede a plugin the CLI auto-registered earlier in the same boot
231
+ * (#9863). What changes is that it is no longer silent, and no longer
232
+ * disagrees with the other kernel.
225
233
  */
226
234
  use(plugin: Plugin): Promise<this>;
227
235
  /**
@@ -312,12 +320,14 @@ declare class ObjectKernel {
312
320
  * as well: if the hook never settles and nothing else keeps the loop alive,
313
321
  * Node exits before the timer can fire and the timeout is never reported.
314
322
  * The guard has to stay ref'd exactly as long as the race is undecided,
315
- * which is what `clearTimeout` in a `finally` expresses.
323
+ * which is what clearing on settle expresses.
316
324
  *
317
- * `operation` is widened to `T | PromiseLike<T>` because the Plugin
318
- * contract permits a synchronous hook (`init`/`start` return
319
- * `void | Promise<void>`); such a hook wins the race immediately and the
320
- * guard is reclaimed on the same turn.
325
+ * Clearing the timer was only half of it, though (#10604): the promise the
326
+ * race still holds a reaction on has to SETTLE, or it and that reaction are
327
+ * retained past the end of the run two leaking promises per boot, which
328
+ * is what `vitest --detectAsyncLeaks` names here. Both halves now live in
329
+ * `TimeoutGuard.reclaim()`, shared with `shutdown()`, so the two sites
330
+ * cannot drift into doing one half each again.
321
331
  */
322
332
  private raceStartupTimeout;
323
333
  /**
@@ -811,6 +821,17 @@ declare class LiteKernel extends ObjectKernelBase {
811
821
  /**
812
822
  * Register a plugin
813
823
  * @param plugin - Plugin instance
824
+ *
825
+ * Duplicate names OVERWRITE, with one `warn` naming both versions — the
826
+ * declared contract in `plugin-registration.ts`, applied identically by
827
+ * `ObjectKernel.use()` (#9864, maintainer ruling 2026-08-19).
828
+ *
829
+ * This method used to `throw` `[Kernel] Plugin '<name>' already
830
+ * registered` here while `ObjectKernel` overwrote silently, so one input
831
+ * had two meanings depending on which kernel was running — and the kernel
832
+ * that runs in production was the silent one. The ruling converged them on
833
+ * the behaviour that already works (an app config superseding a plugin the
834
+ * CLI auto-registered, #9863) and made it audible rather than removing it.
814
835
  */
815
836
  use(plugin: Plugin): this;
816
837
  /**
@@ -882,11 +903,67 @@ declare class TestRunner {
882
903
  declare class HttpTestAdapter implements TestExecutionAdapter {
883
904
  private baseUrl;
884
905
  private authToken?;
906
+ /**
907
+ * The single discovery probe of a run, memoised as the in-flight promise so
908
+ * concurrent record actions share one request rather than racing N.
909
+ *
910
+ * `os test` builds ONE adapter for the whole run (`packages/cli/src/commands/
911
+ * test.ts`) and hands it to every suite, so instance scope IS run scope.
912
+ */
913
+ private mountPromise?;
885
914
  constructor(baseUrl: string, authToken?: string | undefined);
886
- /** `{baseUrl}{apiBasePath}{dataPrefix}/{object}` the collection URL. */
915
+ /** The resolved data mount; probes at most once per adapter. */
916
+ private dataMount;
917
+ /**
918
+ * Ask the server where it serves the Data Protocol, and fall back to the
919
+ * convention — loudly — when it cannot say.
920
+ *
921
+ * ## [#7983] What the probe recovers, measured rather than assumed
922
+ *
923
+ * `@objectstack/client` answers the same question through discovery
924
+ * (`getRoute`, `packages/client/src/index.ts`), and this follows it: prefer
925
+ * the server's own `routes.data`, fall back to the convention. Measured on a
926
+ * booted stack (REST generator + dispatcher bridge, three configs):
927
+ *
928
+ * | deployment | `{apiBase}/discovery` | serves |
929
+ * |--------------------------------|-----------------------|---------------|
930
+ * | stock | 200 `/api/v1/data` | `/api/v1/data`|
931
+ * | `crud.dataPrefix: '/objects'` | 200 `/api/v1/objects` | `/api/v1/objects` |
932
+ * | `api.apiPath: '/api/2026-01'` | **404** | `/api/2026-01/data` |
933
+ *
934
+ * So the probe closes the `dataPrefix` row exactly: `RestServer`'s discovery
935
+ * handler substitutes the configured prefix into `routes.data`, and reading
936
+ * it is strictly better than recomputing it here. The `apiPath` row it cannot
937
+ * close, and the reason is structural rather than an oversight — `apiPath`
938
+ * moves the base that discovery itself is mounted under, so the document that
939
+ * would name the new mount is behind the very prefix we are missing.
940
+ *
941
+ * ⛔ And the one discovery document at a FIXED path does not rescue it:
942
+ * `/.well-known/objectstack` is mounted at the site root by the dispatcher
943
+ * bridge, but its `routes.data` is the DISPATCHER's own `${prefix}/data` —
944
+ * measured as `/api/v1/data` under all three configs above, including the two
945
+ * where the server serves elsewhere. Falling back to it would turn "we could
946
+ * not resolve the mount" into "discovery told us `/api/v1/data`": the same
947
+ * 404, now with a false provenance attached. Not probed, deliberately.
948
+ *
949
+ * Hence: one probe, then a diagnostic that NAMES the mount, the evidence and
950
+ * the remedy. `api_call` takes the path it is given and is unaffected either
951
+ * way — it stays the escape hatch for a host this cannot reach.
952
+ */
953
+ private resolveDataMount;
954
+ /** `{baseUrl}{dataMount}/{object}` — the collection URL. */
887
955
  private collectionUrl;
888
956
  /** `{collection}/{id}` — the single-record URL. */
889
957
  private recordUrl;
958
+ /**
959
+ * The provenance clause appended to a failed record action's error.
960
+ *
961
+ * The card this closes is about a 404 that reads like the author's own URL
962
+ * mistake; the mount is the one fact that distinguishes the two, so it rides
963
+ * on the failure itself rather than only on a warning printed earlier in the
964
+ * transcript.
965
+ */
966
+ private mountNote;
890
967
  execute(action: QA.TestAction, _context: Record<string, unknown>): Promise<unknown>;
891
968
  private createRecord;
892
969
  private updateRecord;
@@ -1712,9 +1789,74 @@ declare function isExpired(value: unknown, nowMs: number): boolean;
1712
1789
  /** The principal resolved from a valid `sys_api_key`. */
1713
1790
  interface ApiKeyPrincipal {
1714
1791
  userId: string;
1792
+ /**
1793
+ * The organization this key authenticates INTO — read from the row's
1794
+ * `active_organization_id` and adopted by `resolveAuthzContext` as the
1795
+ * request's active organization (`ExecutionContext.tenantId`), which is what
1796
+ * lets the ADR-0105 Layer 0 wall match. `undefined` for a key minted before
1797
+ * #8287, or one minted under the `single` posture where there is no
1798
+ * organization to inherit.
1799
+ */
1715
1800
  tenantId?: string;
1716
1801
  scopes: string[];
1717
1802
  }
1803
+ /**
1804
+ * [#8287] Why a key was refused. Distinct from "no key present" and from "this
1805
+ * key is unknown/revoked/expired": a refusal means the credential is real and
1806
+ * intact but cannot be admitted under this deployment's tenancy posture.
1807
+ */
1808
+ type ApiKeyRefusalReason = 'organization_required' | 'organization_membership_ended';
1809
+ /**
1810
+ * The verdict on an inbound API key. Three outcomes, deliberately distinct:
1811
+ *
1812
+ * - `none` — no key header, or a key that is unknown / revoked / expired /
1813
+ * owner-less. Indistinguishable by design (never tell a prober which), and
1814
+ * the caller MAY fall through to the session path exactly as before.
1815
+ * - `admitted` — a usable principal.
1816
+ * - `refused` — a real, intact key the posture cannot admit. The caller must
1817
+ * NOT fall through to the session path: falling through would be more
1818
+ * permissive than today's behaviour (an API key already outranks a session),
1819
+ * and the whole point of the refusal is that it is LOUD at call time.
1820
+ */
1821
+ type ApiKeyAdmission = {
1822
+ outcome: 'none';
1823
+ } | {
1824
+ outcome: 'admitted';
1825
+ principal: ApiKeyPrincipal;
1826
+ } | {
1827
+ outcome: 'refused';
1828
+ reason: ApiKeyRefusalReason;
1829
+ message: string;
1830
+ };
1831
+ /**
1832
+ * The shape of the kernel's `tenancy` service this module reads a posture from.
1833
+ * Structural on purpose: `@objectstack/core` must not depend on the plugin that
1834
+ * provides it, and an embedding without that plugin simply supplies nothing.
1835
+ */
1836
+ interface TenancyPostureSource {
1837
+ posture?: string;
1838
+ isolationActive?: boolean;
1839
+ }
1840
+ /**
1841
+ * [#8287] Resolve the EFFECTIVE tenancy posture from the kernel's `tenancy`
1842
+ * service — the same reconciliation `plugin-security` performs before handing a
1843
+ * posture to `computeTenantLayer0Filter`, so the wall and the API-key admission
1844
+ * can never disagree about which posture is in force.
1845
+ *
1846
+ * ⚠️ Deliberately NOT `resolveTenancyPosture()` from `@objectstack/types`, which
1847
+ * reads `OS_TENANCY_POSTURE` directly. That answers what the operator ASKED
1848
+ * for, not what is ENFORCED: under ADR-0093 D4/D5 a deployment that requests
1849
+ * `isolated` without the enterprise `@objectstack/organizations` runtime
1850
+ * resolves to `single` and runs with NO organization wall. Reading the env
1851
+ * there would refuse org-less API keys on a deployment whose wall is not even
1852
+ * active — breaking working automation to enforce a boundary that does not
1853
+ * exist. The `tenancy` service is the one place that already knows the
1854
+ * difference.
1855
+ *
1856
+ * Returns `undefined` when no service is available, which callers must treat as
1857
+ * "no posture-conditional refusal" — see {@link resolveApiKeyAdmission}.
1858
+ */
1859
+ declare function effectiveTenancyPosture(tenancy: TenancyPostureSource | undefined | null): TenancyPosture | undefined;
1718
1860
  /**
1719
1861
  * Verify an inbound API key against `sys_api_key` and resolve its principal.
1720
1862
  * This is the ONE verify path shared by the dispatcher/MCP and REST surfaces.
@@ -1726,7 +1868,21 @@ interface ApiKeyPrincipal {
1726
1868
  * @param headers Request headers (Web `Headers` or a plain object).
1727
1869
  * @param nowMs Clock for expiry checks (injectable for tests).
1728
1870
  */
1729
- declare function resolveApiKeyPrincipal(ql: any, headers: any, nowMs?: number): Promise<ApiKeyPrincipal | undefined>;
1871
+ declare function resolveApiKeyPrincipal(ql: any, headers: any, nowMs?: number, tenancyPosture?: TenancyPosture): Promise<ApiKeyPrincipal | undefined>;
1872
+ /**
1873
+ * [#8287] The full verdict behind {@link resolveApiKeyPrincipal} — same lookup,
1874
+ * but it distinguishes a POSTURE REFUSAL from "no principal".
1875
+ *
1876
+ * `resolveApiKeyPrincipal` collapses `refused` into `undefined` so every
1877
+ * existing caller keeps working and keeps failing closed; a caller that can
1878
+ * report WHY (the shared `resolveAuthzContext`) uses this instead.
1879
+ *
1880
+ * The only refusal decided here is the org-less one, because it needs nothing
1881
+ * but the row and the posture. The ex-member refusal needs the caller's
1882
+ * membership set and is decided in `resolveAuthzContext`, where that set is
1883
+ * already resolved.
1884
+ */
1885
+ declare function resolveApiKeyAdmission(ql: any, headers: any, nowMs?: number, tenancyPosture?: TenancyPosture): Promise<ApiKeyAdmission>;
1730
1886
 
1731
1887
  /** The transport-agnostic authorization envelope produced from a request. */
1732
1888
  interface ResolvedAuthzContext {
@@ -1758,6 +1914,24 @@ interface ResolvedAuthzContext {
1758
1914
  * anonymous requests carry no rung.
1759
1915
  */
1760
1916
  posture?: AuthzPosture;
1917
+ /**
1918
+ * [#8287] Set when an inbound API key was REFUSED — a real, intact
1919
+ * credential this deployment's tenancy posture cannot admit. The context is
1920
+ * otherwise EMPTY (no `userId`), so every transport already fails it closed
1921
+ * to 401 with no change; this field only lets a transport that wants to say
1922
+ * WHY do so, instead of answering the operator with a bare "unauthenticated"
1923
+ * for a key they can see is neither revoked nor expired.
1924
+ *
1925
+ * ⚠️ `reason` is NOT an `error.code`. The wire vocabulary is closed
1926
+ * (ADR-0112: `StandardErrorCode ∪ ERROR_CODE_LEDGER`, both in `packages/spec`)
1927
+ * and a refused credential's standard member is `UNAUTHENTICATED`. This is a
1928
+ * diagnostic discriminator for the message, deliberately lowercase so it can
1929
+ * never be mistaken for one.
1930
+ */
1931
+ authRefusal?: {
1932
+ reason: ApiKeyRefusalReason;
1933
+ message: string;
1934
+ };
1761
1935
  }
1762
1936
  interface ResolveAuthzInput {
1763
1937
  /** Data engine (ObjectQL) exposing `find(object, { where, limit, context })`. */
@@ -1772,6 +1946,18 @@ interface ResolveAuthzInput {
1772
1946
  getSession?: (headers: any) => Promise<any> | any;
1773
1947
  /** Clock injection for API-key expiry (tests). */
1774
1948
  nowMs?: number;
1949
+ /**
1950
+ * [#8287] The deployment's EFFECTIVE tenancy posture, as resolved from the
1951
+ * kernel's `tenancy` service (`effectiveTenancyPosture`) — never from
1952
+ * `OS_TENANCY_POSTURE`, which reports what was requested rather than what is
1953
+ * enforced (ADR-0093 D4/D5).
1954
+ *
1955
+ * Supplied by the transport because this resolver is deliberately
1956
+ * kernel-agnostic. OMITTING it disables the two posture-conditional API-key
1957
+ * refusals and leaves behaviour exactly as it was — so an unwired caller is
1958
+ * never made WORSE, only less strict.
1959
+ */
1960
+ tenancyPosture?: TenancyPosture;
1775
1961
  }
1776
1962
  /**
1777
1963
  * Resolve the authorization context for an inbound request. Always resolves —
@@ -1837,17 +2023,27 @@ interface ResolveLocalizationInput {
1837
2023
  tenantId?: string;
1838
2024
  userId?: string;
1839
2025
  }
2026
+ type LocalizationResult = {
2027
+ timezone: string;
2028
+ locale: string;
2029
+ currency?: string;
2030
+ };
1840
2031
  /**
1841
2032
  * Resolve workspace localization defaults (reference `timezone` / `locale` /
1842
2033
  * `currency`). Canonical path is the `localization` SettingsManifest (cascade:
1843
2034
  * platform default → global → tenant); falls back to direct tenant-scoped
1844
2035
  * `sys_setting` rows, then the built-ins `UTC` / `en-US`. Never throws.
2036
+ *
2037
+ * A read that fails outright (backend fault — table missing, connection
2038
+ * refused, etc.) is memoized for {@link LOCALIZATION_FAILURE_CACHE_TTL_MS}
2039
+ * per `(ql, tenantId, userId)` so the failing query — and the driver's log
2040
+ * line for it — does not repeat every request (#10221). A successful read,
2041
+ * including a legitimate "no settings configured yet" empty result, is NEVER
2042
+ * cached: the next call always re-reads, so a settings write takes effect
2043
+ * immediately (see the cache doc above for why — the dogfood analytics
2044
+ * bucketing test pins this).
1845
2045
  */
1846
- declare function resolveLocalizationContext(input: ResolveLocalizationInput): Promise<{
1847
- timezone: string;
1848
- locale: string;
1849
- currency?: string;
1850
- }>;
2046
+ declare function resolveLocalizationContext(input: ResolveLocalizationInput): Promise<LocalizationResult>;
1851
2047
 
1852
2048
  /**
1853
2049
  * ADR-0069 — authentication-policy session gate.
@@ -2230,12 +2426,16 @@ declare const ANONYMOUS_DENY_CODE: "UNAUTHENTICATED";
2230
2426
  /** Human-facing message. */
2231
2427
  declare const ANONYMOUS_DENY_MESSAGE = "Authentication is required to access this endpoint.";
2232
2428
  /**
2233
- * The **REST seam's** 401 body — flat `{ error, message }`. NOT the platform's
2234
- * only one; see the two-envelope table below before you reuse this shape.
2429
+ * The **REST seam's** 401 body — flat `{ error, code, message }`. NOT the
2430
+ * platform's only one; see the two-envelope table below before you reuse this
2431
+ * shape.
2235
2432
  *
2236
- * Exactly one consumer writes it: `@objectstack/rest`'s `enforceAuth`
2237
- * (`rest-server.ts` — `res.status(ANONYMOUS_DENY_STATUS).json(ANONYMOUS_DENY_BODY)`),
2238
- * which owns the `/data/*` and `/meta` surfaces.
2433
+ * Two consumers write it verbatim, both flat-family seams: `@objectstack/rest`'s
2434
+ * `enforceAuth` (`rest-server.ts` —
2435
+ * `res.status(ANONYMOUS_DENY_STATUS).json(ANONYMOUS_DENY_BODY)`), which owns the
2436
+ * `/data/*` and `/meta` surfaces, and `@objectstack/runtime`'s
2437
+ * `mountRouteOnServer` (`dispatcher-plugin.ts` — the endpoint-route 401 arm,
2438
+ * #9823), which answers declared routes mounted on the HTTP server.
2239
2439
  *
2240
2440
  * ## Two live envelopes, one denial (#5632)
2241
2441
  *
@@ -2244,8 +2444,11 @@ declare const ANONYMOUS_DENY_MESSAGE = "Authentication is required to access thi
2244
2444
  * {@link ANONYMOUS_DENY_MESSAGE}). What differs is the **wrapper**:
2245
2445
  *
2246
2446
  * - **REST seam** — `@objectstack/rest` `enforceAuth`, this constant, verbatim:
2247
- * `{ error: 'UNAUTHENTICATED', message: '…' }`. The code is the value of the
2248
- * top-level `error` key; there is no `success` key and no nesting.
2447
+ * `{ error: 'UNAUTHENTICATED', code: 'UNAUTHENTICATED', message: '…' }`.
2448
+ * The machine code lives in the top-level `code` key the same documented
2449
+ * key every other REST error family answers (#9487, maintainer-ruled
2450
+ * ADDITIVE: `error` keeps carrying the code value it always has, so no
2451
+ * existing reader breaks). There is no `success` key and no nesting.
2249
2452
  * - **Dispatcher seams** — the five runtime domains `domains/ai.ts`,
2250
2453
  * `domains/meta.ts`, `domains/security.ts`, `domains/actions.ts` and
2251
2454
  * `domains/automation.ts` do NOT use this constant. Each calls
@@ -2257,7 +2460,10 @@ declare const ANONYMOUS_DENY_MESSAGE = "Authentication is required to access thi
2257
2460
  * (#4007) records the flat and wrapped envelopes as the two live ones, and
2258
2461
  * assigns retiring one of them to the envelope-convergence line (#3843 family).
2259
2462
  * Converging them is a breaking wire change; it is not this module's to make,
2260
- * and this constant must not be read as if it had already happened.
2463
+ * and this constant must not be read as if it had already happened. The #9487
2464
+ * `code` key does NOT settle that question either way (ADR-0112 D5 stays
2465
+ * open): it aligns the flat family to the `{ error, code }` shape the other
2466
+ * flat REST error families already answer, without moving or removing a key.
2261
2467
  *
2262
2468
  * ## Reading this from a consumer (human or AI author)
2263
2469
  *
@@ -2274,6 +2480,7 @@ declare const ANONYMOUS_DENY_MESSAGE = "Authentication is required to access thi
2274
2480
  */
2275
2481
  declare const ANONYMOUS_DENY_BODY: {
2276
2482
  readonly error: "UNAUTHENTICATED";
2483
+ readonly code: "UNAUTHENTICATED";
2277
2484
  readonly message: "Authentication is required to access this endpoint.";
2278
2485
  };
2279
2486
  interface AnonymousDenyInput {
@@ -2316,6 +2523,114 @@ declare function isGrantActive(row: GrantValidityWindow | null | undefined, nowM
2316
2523
  */
2317
2524
  declare function isGrantExpired(row: GrantValidityWindow | null | undefined, nowMs: number): boolean;
2318
2525
 
2526
+ /** A catalogue row that may carry the `active` flag (`sys_permission_set`, `sys_position`). */
2527
+ interface ActivatableRow {
2528
+ active?: unknown;
2529
+ }
2530
+ /**
2531
+ * True unless the row carries an `active` column that is explicitly OFF.
2532
+ *
2533
+ * The ONE predicate every reader of `sys_permission_set.active` /
2534
+ * `sys_position.active` uses, so the resolver that enforces the flag and the
2535
+ * break-glass guard that simulates a write to it can never disagree about what
2536
+ * "deactivated" means.
2537
+ */
2538
+ declare function isRowActive(row: ActivatableRow | null | undefined): boolean;
2539
+
2540
+ /**
2541
+ * ADMIN_STANDING_SURFACE — what `resolveAuthzContext` READS when it decides
2542
+ * who is an administrator, declared beside the resolver that reads it.
2543
+ *
2544
+ * ## Why this file exists (#8734)
2545
+ *
2546
+ * `plugin-auth`'s break-glass guard (`last-admin-guard.ts`, ADR-0024 D5.2)
2547
+ * decides whether a pending write can empty the administrator population by
2548
+ * testing the payload against three standing-key lists — `MEMBER_STANDING_KEYS`,
2549
+ * `GRANT_STANDING_KEYS`, `PERMISSION_SET_STANDING_KEYS`. Those lists are not an
2550
+ * independent design artifact: they are a CACHE of the columns this resolver
2551
+ * consumes. A payload touching none of them is skipped without any reads, so a
2552
+ * column this resolver starts reading and the guard's list omits is a write
2553
+ * class the guard silently stops judging — the one write class that can lock an
2554
+ * installation out of its own administration, with no in-product recovery.
2555
+ *
2556
+ * Nothing bound the two together. The correspondence was carried by a comment,
2557
+ * and it had already gone false once: #6084 wrote, beside the list, that
2558
+ * everything a permission-set write touches other than `name` — naming `active`
2559
+ * explicitly — is invisible to "who is an administrator". That was true when
2560
+ * written. #8613 made `active` a resolution-time predicate (a DEACTIVATED
2561
+ * `admin_full_access` set confers nothing, §6b below), and the sentence became
2562
+ * false. It was caught by one agent reading the comment closely enough to
2563
+ * notice it contradicted the code being written. Nothing mechanical would have
2564
+ * caught it: the guard's own tests stay green, because the guard is simply never
2565
+ * consulted for that write.
2566
+ *
2567
+ * ## What this file is, and what it is NOT
2568
+ *
2569
+ * It is a MEASUREMENT, not a wish. Its column lists are asserted equal to what
2570
+ * the resolver actually reads at runtime, by
2571
+ * `admin-standing-surface.test.ts`, which drives the real
2572
+ * `resolveAuthzContext` over a recording engine and collects every property
2573
+ * access and every `where` key per table. That is deliberate: a hand-written
2574
+ * list of "columns the derivation reads" is the same artifact as the comment
2575
+ * that went stale, one indirection along. Observation is also the only reading
2576
+ * that survives the derivation moving INTO a helper — `active` is read by
2577
+ * `isRowActive(ps)` and the window bounds by `isGrantActive(row, now)`, neither
2578
+ * of which names a column at the resolver's own call site.
2579
+ *
2580
+ * It is NOT a projection the resolver consumes. `ql.find` here returns whole
2581
+ * rows and the reads are ordinary property accesses on untyped rows, so nothing
2582
+ * in this file can FORCE the resolver to read only what it declares. The force
2583
+ * comes from the observation test: add a read, and this declaration is red
2584
+ * until it is updated; update this declaration, and `plugin-auth`'s
2585
+ * correspondence test is red until every new column is either in a standing-key
2586
+ * list or explicitly excluded with a reason.
2587
+ *
2588
+ * ## Reading the entries
2589
+ *
2590
+ * Every table this resolution path reads is listed — including the ones that
2591
+ * CANNOT confer administrator standing, each with the reason it cannot. That is
2592
+ * the table-level half of the same guarantee: a resolver that starts deriving
2593
+ * administrator standing from a new table would otherwise be invisible to a
2594
+ * column-set comparison, because the new table appears in neither side's list.
2595
+ */
2596
+ /** How a table this resolver reads relates to "who is an administrator". */
2597
+ interface AdminStandingTable {
2598
+ /**
2599
+ * `derives` — a write to this table can change the administrator population,
2600
+ * so `last-admin-guard.ts` must carry a standing-key list for it.
2601
+ * `reads-only` — this resolver reads the table for something else entirely.
2602
+ */
2603
+ readonly role: 'derives' | 'reads-only';
2604
+ /** Why the row above is the right classification. Prose, but pinned to a measured table. */
2605
+ readonly reason: string;
2606
+ /**
2607
+ * Every column this resolver reads on the table — property accesses and
2608
+ * `where` keys alike, in every spelling it actually touches. Declared for
2609
+ * `derives` tables only; asserted equal to the observed set.
2610
+ */
2611
+ readonly columns?: readonly string[];
2612
+ }
2613
+ /**
2614
+ * The measured read surface of the administrator derivation.
2615
+ *
2616
+ * Scope, stated so the gate cannot be read as claiming more than it measures:
2617
+ * this is the SESSION/user-id resolution path — `resolveAuthzContext` with a
2618
+ * principal, and therefore all of `resolveUserAuthzGrants`. The API-key
2619
+ * ADMISSION path (`resolveApiKeyAdmission`) is outside it on purpose: it
2620
+ * authenticates a principal and seeds `permissions` with the key's scopes, and
2621
+ * confers no administrator standing of its own — `hasPlatformAdminGrant` (§6b)
2622
+ * is set only from a `sys_permission_set` row reached through an UNSCOPED
2623
+ * `sys_user_permission_set` grant, never from a scope string.
2624
+ */
2625
+ declare const ADMIN_STANDING_SURFACE: Readonly<Record<string, AdminStandingTable>>;
2626
+ /** The tables a write to which can change who is an administrator. */
2627
+ declare function adminStandingTables(): string[];
2628
+ /**
2629
+ * The columns this resolver reads on `table`, or `undefined` when the table is
2630
+ * not part of the administrator derivation.
2631
+ */
2632
+ declare function adminStandingColumns(table: string): readonly string[] | undefined;
2633
+
2319
2634
  /**
2320
2635
  * [#7678] The `?status=` vocabulary of the audience-binding suggestion list
2321
2636
  * (ADR-0090 D5/D9) — ONE owner for a rule that had exactly one implementation
@@ -2512,6 +2827,20 @@ interface CalendarParts {
2512
2827
  month: number;
2513
2828
  day: number;
2514
2829
  }
2830
+ /**
2831
+ * A wall clock as a human writes it — calendar day plus an optional
2832
+ * time-of-day, with **no zone attached**. `2026-08-01 06:00:00` is this shape:
2833
+ * it names a reading on a clock, and only a reference timezone turns it into an
2834
+ * instant. Omitted time components default to 0, so {@link CalendarParts} alone
2835
+ * is midnight.
2836
+ */
2837
+ interface WallClockParts extends CalendarParts {
2838
+ /** 0-23. */
2839
+ hour?: number;
2840
+ minute?: number;
2841
+ second?: number;
2842
+ millisecond?: number;
2843
+ }
2515
2844
  /**
2516
2845
  * The year/month/day an instant falls on in `tz`. Throws if `tz` is not a
2517
2846
  * valid IANA zone (callers treat that as a fall-through to UTC).
@@ -2536,8 +2865,52 @@ declare function calendarPartsInTzOrUtc(d: Date, tz?: string): CalendarParts;
2536
2865
  *
2537
2866
  * Used by date-bucket drill ranges (#1752): a `datetime` field buckets on the
2538
2867
  * reference-tz calendar, so its bucket boundary is that tz's midnight instant.
2868
+ *
2869
+ * Date-only by contract: a `YYYY-MM-DD HH:mm:ss` argument is still `NaN` here.
2870
+ * Callers holding a wall clock with a time-of-day want
2871
+ * {@link zonedWallClockToUtcMs}, which this delegates its zone arithmetic to.
2539
2872
  */
2540
2873
  declare function zonedDateStartToUtcMs(ymd: string, tz?: string): number;
2874
+ /**
2875
+ * The UTC instant (epoch ms) at which a **wall clock** reading happens in
2876
+ * reference timezone `tz` — the general inverse of {@link calendarPartsInTz},
2877
+ * of which {@link zonedDateStartToUtcMs} is the midnight special case.
2878
+ *
2879
+ * `2026-08-01 06:00:00` in `Asia/Shanghai` is `2026-07-31T22:00:00Z`: a
2880
+ * different day, month and quarter. That gap is why this direction exists as a
2881
+ * shared primitive at all — bulk import (#8485) reads offset-free spreadsheet
2882
+ * cells, which are wall clocks and nothing more, and `new Date(cell)` resolves
2883
+ * them against the **process** `TZ`, i.e. a host setting rather than the
2884
+ * tenant's configured zone.
2885
+ *
2886
+ * DST-safe: the zone offset is read from the platform tz database via
2887
+ * `Intl.DateTimeFormat` (never hand-computed), and a two-pass resolution settles
2888
+ * the case where the offset differs side-to-side of the target instant. Two
2889
+ * wall clocks are not a bijection with instants, and this function resolves
2890
+ * both degenerate cases to the **earlier candidate instant** — in both, the
2891
+ * final pass reads the offset on the DST side of the transition (measured, not
2892
+ * merely intended — `datetime.test.ts` pins both):
2893
+ * - a clock reading the zone **skips** (spring forward: `02:30` on a US
2894
+ * spring-forward day) settles on the *post*-transition offset (EDT, −04),
2895
+ * which places the instant just **before** the gap: it reads `01:30` EST
2896
+ * locally, not `03:30` EDT. Note this is the opposite of Temporal's
2897
+ * `'compatible'` disambiguation, which pushes a gap reading forward;
2898
+ * - a clock reading that happens **twice** (fall back: `01:30` on a US
2899
+ * fall-back day) resolves to its first occurrence, the one still on the
2900
+ * pre-transition DST offset (EDT, −04).
2901
+ *
2902
+ * A spreadsheet cell naming a wall clock that its zone never had is ambiguous
2903
+ * by construction; what matters for an import is that the answer is
2904
+ * deterministic and host-independent, which both branches above are.
2905
+ *
2906
+ * FALLBACK — an unset, `'UTC'`, or invalid `tz` reads the wall clock **as UTC**,
2907
+ * never as the process-local clock. Every caller of this family already degrades
2908
+ * that way ({@link zonedDateStartToUtcMs}, and the export renderer's cell path),
2909
+ * and a host `TZ` fallback would reintroduce exactly the deployment-dependent
2910
+ * instant this primitive exists to remove. A parts object that produces an
2911
+ * invalid date (`NaN` components) returns `NaN`, as `Date.UTC` does.
2912
+ */
2913
+ declare function zonedWallClockToUtcMs(parts: WallClockParts, tz?: string): number;
2541
2914
 
2542
2915
  /**
2543
2916
  * Granularity of a canonical date-bucket key. Mirrors `@objectstack/spec`'s
@@ -3006,6 +3379,34 @@ declare function resolveFilterTokens<T>(filter: T, ctx?: FilterTokenResolutionCo
3006
3379
  */
3007
3380
  declare function filterTokenContextFrom(execCtx: ExecutionContextLike | undefined, now?: Date): FilterTokenResolutionContext;
3008
3381
 
3382
+ /** Which temporal storage rule a declared field takes. */
3383
+ type TemporalComparandKind = 'datetime' | 'date' | 'time';
3384
+ /**
3385
+ * The kind a declared field's `type` takes, or `null` for every non-temporal
3386
+ * field.
3387
+ *
3388
+ * The same three-way split `driver-memory`'s `indexTemporalFields` and
3389
+ * `SqlDriver.temporalFieldKind` make, so the door and the drivers cannot
3390
+ * disagree about which fields are temporal at all.
3391
+ */
3392
+ declare function temporalComparandKind(fieldType: unknown): TemporalComparandKind | null;
3393
+ /**
3394
+ * Is `value` a comparand that a `kind` column's storage rule cannot read?
3395
+ *
3396
+ * `true` ONLY for a non-empty, non-placeholder STRING that the kind's rule
3397
+ * would hand back unchanged. Everything else — a number, a `Date`, `null`, a
3398
+ * `{ $field }` reference, filter structure, the empty string, a `{token}` —
3399
+ * answers `false`, each for a reason recorded in the module note or below.
3400
+ *
3401
+ * A `{placeholder}` is stepped around rather than judged because it is another
3402
+ * layer's vocabulary and that layer already refuses the unknown ones loudly
3403
+ * (`FILTER_TOKEN_UNKNOWN` / 400, with the resolvable tokens listed). Both doors
3404
+ * that call this run BEFORE token resolution, so judging a placeholder here
3405
+ * would refuse `{30_days_ago}` — the platform's own correct spelling, and the
3406
+ * positive control this fix is pinned against.
3407
+ */
3408
+ declare function isUninterpretableTemporalComparand(kind: TemporalComparandKind, value: unknown): boolean;
3409
+
3009
3410
  /**
3010
3411
  * [#4435] The 404 a single-record operation answers when the id names no row.
3011
3412
  *
@@ -3121,11 +3522,15 @@ declare function createMemoryQueue(): {
3121
3522
  };
3122
3523
 
3123
3524
  /**
3124
- * In-memory job scheduler fallback.
3525
+ * In-memory job registry — schedule/cancel/trigger bookkeeping with NO timer.
3125
3526
  *
3126
- * Implements the IJobService contract with basic schedule/cancel/trigger
3127
- * operations. Used by ObjectKernel as an automatic fallback when no real
3128
- * job plugin (e.g. Agenda / BullMQ) is registered.
3527
+ * [#10746] NOT pre-injected by ObjectKernel any more (it used to be, via
3528
+ * `CORE_FALLBACK_FACTORIES`): a fallback must not fake capability (maintainer
3529
+ * ruling 2026-08-22). Advertising a `schedule()` that records and never fires
3530
+ * made every "prefer the platform job service, else own a timer" consumer
3531
+ * take the job-service branch and then silently never run. The export remains
3532
+ * for embedders who deliberately want a manual-trigger job registry — e.g. in
3533
+ * tests that drive handlers via `trigger()` — and have read this docblock.
3129
3534
  *
3130
3535
  * [#4058] `degraded` (ADR-0076 D12), with the missing half named in the
3131
3536
  * message rather than left for a deployer to discover: `trigger()` really runs
@@ -3277,8 +3682,26 @@ declare function wireAuthoredTranslationSync(ctx: MinimalCtx): void;
3277
3682
 
3278
3683
  /**
3279
3684
  * Map of core-criticality service names to their in-memory fallback factories.
3280
- * Used by ObjectKernel.validateSystemRequirements() to auto-inject fallbacks
3281
- * when no real plugin provides the service.
3685
+ * This IS the kernel's pre-injection list: `ObjectKernel.preInjectCoreFallbacks()`
3686
+ * registers an entry for every unprovided `core` service before Phase 2, and
3687
+ * `validateSystemRequirements()` consults the same map as its final check.
3688
+ *
3689
+ * [#10746] `job` is deliberately ABSENT — a fallback must not fake capability
3690
+ * (maintainer ruling 2026-08-22). `createMemoryJob()`'s `schedule()` records a
3691
+ * job and never fires it, so pre-injecting it made every "prefer the platform
3692
+ * job service, else own a timer" consumer take the job-service branch and then
3693
+ * silently never run: `plugin-reports` logged `dispatcher registered with job
3694
+ * service` and dispatched nothing, ever (measured: 0 reads of
3695
+ * `sys_report_schedule` in 5600 ms with the success line present). With no
3696
+ * entry here, `getService('job')` throws when no job plugin is installed,
3697
+ * every consumer's documented no-job-service path becomes reachable (they all
3698
+ * already run on `LiteKernel`, which injects no fallbacks), and the kernel
3699
+ * says the absence out loud at boot: `validateSystemRequirements()` warns
3700
+ * "Core service missing, functionality may be degraded: job". Do NOT re-add
3701
+ * the entry to quiet that warning — install `@objectstack/service-job`, or
3702
+ * register a real scheduler, instead. `createMemoryJob` stays exported below
3703
+ * for embedders who deliberately want a manual-trigger job registry and have
3704
+ * read its docblock.
3282
3705
  */
3283
3706
  declare const CORE_FALLBACK_FACTORIES: Record<string, () => Record<string, any>>;
3284
3707
 
@@ -3630,4 +4053,4 @@ declare class NamespaceResolver {
3630
4053
  private suggestAlternative;
3631
4054
  }
3632
4055
 
3633
- export { ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, AUDIENCE_BINDING_SUGGESTION_STATUSES, AUDIENCE_BINDING_SUGGESTION_STATUS_VALUES, type AnonymousDenyInput, type ApiKeyPrincipal, type AudienceBindingSuggestionStatus, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, ENTRY_EXECUTION_CONTEXT_FIELDS, type EngineWithTransaction, type EntryExecutionContextField, type EntryLocalization, type ExecutionContextAssemblyInput, type ExecutionContextEntryFields, type ExecutionContextLike, type FilterTokenResolutionContext, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type InterruptedRun, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type MigrationChunk, type MigrationChunkContext, MigrationJournalRefusal, type MigrationPlan, type MigrationPlanProvider, MigrationPlanRegistry, type MigrationPlanStep, type MigrationRunResult, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, type OAuthTokenProvenance, OPERATION_PRIVATE_KEY_PREFIX, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type OrderablePlugin, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, type RunMigrationJournalOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, UnknownFilterTokenError, UnresolvedFilterTokenError, type UserAuthzGrants, type VersionCompatibility, assembleExecutionContext, assembleExecutionContextOrGuest, assertInitServiceRequirements, assertMetadataRegisterContract, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, canonicalMetadataServiceType, collectInternalWriteResponseFields, counterSignPayload, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, describeInitOrderFault, engineCanRollBack, evaluateAuthGate, extractApiKey, filterTokenContextFrom, findInterruptedRuns, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, hashMigrationPlan, isAudienceBindingSuggestionStatus, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, normalizeAuthGate, omitInternalFieldsFromWriteResponse, parseScopes, parseSignature, planChunks, postureVisibleRows, readAuthoredTranslationLayer, readRunJournal, recordNotFoundError, resolveApiKeyPrincipal, resolveAuthzContext, resolveFilterToken, resolveFilterTokens, resolveLocale, resolveLocalizationContext, resolvePluginOrder, resolveUserAuthzGrants, resumeMigrationJournal, runMigrationJournal, safeExit, shouldDenyAnonymous, signPayload, unknownAudienceBindingSuggestionStatusMessage, validateInitServiceContract, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, withoutOperationPrivateKeys, zonedDateStartToUtcMs };
4056
+ export { ADMIN_STANDING_SURFACE, ANONYMOUS_DENY_BODY, ANONYMOUS_DENY_CODE, ANONYMOUS_DENY_MESSAGE, ANONYMOUS_DENY_STATUS, API_KEY_PREFIX, AUDIENCE_BINDING_SUGGESTION_STATUSES, AUDIENCE_BINDING_SUGGESTION_STATUS_VALUES, type ActivatableRow, type AdminStandingTable, type AnonymousDenyInput, type ApiKeyAdmission, type ApiKeyPrincipal, type ApiKeyRefusalReason, type AudienceBindingSuggestionStatus, type AuthGate, type BucketGranularity, type BulkWriteOptions, type BulkWriteRowResult, CORE_FALLBACK_FACTORIES, type CalendarParts, DependencyResolver, ENTRY_EXECUTION_CONTEXT_FIELDS, type EngineWithTransaction, type EntryExecutionContextField, type EntryLocalization, type ExecutionContextAssemblyInput, type ExecutionContextEntryFields, type ExecutionContextLike, type FilterTokenResolutionContext, type GeneratedApiKey, type GrantValidityWindow, HotReloadManager, type InterruptedRun, type KernelState, type KeyInput, type LadderPrincipal, type LadderRow, LiteKernel, type MigrationChunk, type MigrationChunkContext, MigrationJournalRefusal, type MigrationPlan, type MigrationPlanProvider, MigrationPlanRegistry, type MigrationPlanStep, type MigrationRunResult, type NamespaceCheckResult, type NamespaceConflict, type NamespaceEntry, NamespaceResolver, type OAuthTokenProvenance, OPERATION_PRIVATE_KEY_PREFIX, ObjectKernel, ObjectKernelBase, type ObjectKernelConfig, ObjectLogger, type OrderablePlugin, POSTURE_INJECTION_RULE, POSTURE_LADDER, POSTURE_RANK, type ParsedSignature, type PermissionCheckResult$1 as PermissionCheckResult, type PermissionGrant, type Plugin, type PluginArtifactVerifyResult, PluginConfigValidator, type PluginContext, PluginHealthMonitor, type PluginHealthStatus, type PluginLoadResult, PluginLoader, type PluginMetadata, type PermissionCheckResult as PluginPermissionCheckResult, PluginPermissionEnforcer, PluginPermissionManager, type PluginPermissions, PluginSandboxRuntime, PluginSecurityScanner, type PluginSignatureConfig, PluginSignatureVerifier, type PluginStartupResult, type PostureEvidence, type PublisherVerifyResult, index as QA, type ResolveAuthzInput, type ResolveLocalizationInput, type ResolveUserAuthzGrantsOptions, type ResolvedAuthzContext, type ResourceUsage, type RetryOptions, type RunMigrationJournalOptions, SIGNATURE_ALG, type SandboxContext, type ScanTarget, SecurePluginContext, type SecurityIssue, SemanticVersionManager, type ServiceFactory, ServiceLifecycle, type ServiceRegistration, type SignatureVerificationResult, type TemporalComparandKind, type TenancyPostureSource, UnknownFilterTokenError, UnresolvedFilterTokenError, type UserAuthzGrants, type VersionCompatibility, type WallClockParts, adminStandingColumns, adminStandingTables, assembleExecutionContext, assembleExecutionContextOrGuest, assertInitServiceRequirements, assertMetadataRegisterContract, bucketKeyToCalendarRange, buildPermissionsFromGrants, bulkWrite, calendarPartsInTz, calendarPartsInTzOrUtc, canonicalMetadataServiceType, collectInternalWriteResponseFields, counterSignPayload, createMemoryCache, createMemoryI18n, createMemoryJob, createMemoryMetadata, createMemoryQueue, createPluginConfigValidator, createPluginPermissionEnforcer, deepMerge, defaultIsTransientError, derivePosture, describeInitOrderFault, effectiveTenancyPosture, engineCanRollBack, evaluateAuthGate, extractApiKey, filterTokenContextFrom, findInterruptedRuns, generateApiKey, generateEd25519KeyPair, getEnv, getMemoryUsage, hashApiKey, hashMigrationPlan, isAudienceBindingSuggestionStatus, isAuthGateAllowlisted, isExpired, isGrantActive, isGrantExpired, isNode, isRowActive, isUninterpretableTemporalComparand, normalizeAuthGate, omitInternalFieldsFromWriteResponse, parseScopes, parseSignature, planChunks, postureVisibleRows, readAuthoredTranslationLayer, readRunJournal, recordNotFoundError, resolveApiKeyAdmission, resolveApiKeyPrincipal, resolveAuthzContext, resolveFilterToken, resolveFilterTokens, resolveLocale, resolveLocalizationContext, resolvePluginOrder, resolveUserAuthzGrants, resumeMigrationJournal, runMigrationJournal, safeExit, shouldDenyAnonymous, signPayload, temporalComparandKind, unknownAudienceBindingSuggestionStatusMessage, validateInitServiceContract, verifyPayload, verifyPlatformSignature, verifyPluginArtifact, verifyPublisherSignature, wireAuthoredTranslationSync, withTransientRetry, withoutOperationPrivateKeys, zonedDateStartToUtcMs, zonedWallClockToUtcMs };