@voltro/protocol 0.86.0 → 0.88.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/THIRD-PARTY-NOTICES.md +3 -227
  3. package/dist/apiRoleEndpoints.js +107 -0
  4. package/dist/apiRoleObservation.js +67 -0
  5. package/dist/apikey.d.ts +10 -0
  6. package/dist/apikey.js +1 -2
  7. package/dist/approval.js +1 -2
  8. package/dist/approvalDescriptors.js +1 -2
  9. package/dist/auth.js +40 -21
  10. package/dist/boundedRing.js +1 -2
  11. package/dist/businessRule.js +1 -2
  12. package/dist/canonicalJson.js +13 -0
  13. package/dist/connection.js +1 -2
  14. package/dist/devActivity.js +1 -2
  15. package/dist/errorSchema.js +23 -0
  16. package/dist/errorValue.js +1 -2
  17. package/dist/event.js +52 -47
  18. package/dist/formCapture.js +11 -0
  19. package/dist/httpStreamMaxDuration.js +8 -0
  20. package/dist/idempotency.js +89 -35
  21. package/dist/idempotencyInput.js +122 -0
  22. package/dist/identity.js +1 -2
  23. package/dist/index.d.ts +876 -124
  24. package/dist/index.js +51 -40
  25. package/dist/inspect.d.ts +1 -1
  26. package/dist/inspect.js +1 -2
  27. package/dist/inspectBackfill.js +1 -2
  28. package/dist/inspectCommandRejection.js +1 -2
  29. package/dist/inspectProjection.js +1 -2
  30. package/dist/inspectStorageProjection.js +1 -2
  31. package/dist/inspectValueIdentity.js +1 -2
  32. package/dist/inspectWorkflowExecution.js +1 -2
  33. package/dist/inspectWorkflowQueuedStart.js +1 -2
  34. package/dist/jsonSchema.js +1 -2
  35. package/dist/jwt.d.ts +10 -0
  36. package/dist/jwt.js +2 -3
  37. package/dist/outbox.js +1 -2
  38. package/dist/pairing.js +1 -2
  39. package/dist/plugin.js +44 -19
  40. package/dist/pluginAccountMerge.js +87 -0
  41. package/dist/pluginNaming.js +1 -2
  42. package/dist/pluginPrivacy.js +165 -0
  43. package/dist/procedure.js +115 -98
  44. package/dist/publicApi.js +191 -211
  45. package/dist/queryOptionalSources.js +13 -0
  46. package/dist/reactivityChannel.js +1 -2
  47. package/dist/rest.d.ts +227 -64
  48. package/dist/rest.js +296 -194
  49. package/dist/restFormat.js +1 -2
  50. package/dist/restReceipt.js +42 -0
  51. package/dist/revalidate.js +12 -0
  52. package/dist/rowEquality.js +1 -2
  53. package/dist/rowPatch.js +1 -2
  54. package/dist/schemaShape.js +1 -2
  55. package/dist/scopes.js +72 -40
  56. package/dist/secretWindows.js +70 -0
  57. package/dist/serverErrorBus.js +1 -2
  58. package/dist/serviceCall.js +1 -2
  59. package/dist/session.d.ts +89 -6
  60. package/dist/session.js +56 -56
  61. package/dist/sessionHeader.js +1 -2
  62. package/dist/ssrfPolicy.js +29 -0
  63. package/dist/storageAccess.js +1 -2
  64. package/dist/storeErrors.js +1 -2
  65. package/dist/storeScreenshots.js +1 -2
  66. package/dist/strictInput.js +1 -2
  67. package/dist/subscriptionEvent.js +1 -2
  68. package/dist/tenantSelection.js +1 -2
  69. package/dist/time.js +1 -2
  70. package/dist/undo.js +1 -2
  71. package/dist/validation.js +1 -2
  72. package/dist/webSocketGateway.js +8 -3
  73. package/dist/workflow.js +1 -2
  74. package/dist/workflowAccess.js +1 -2
  75. package/dist/workflowRestart.js +1 -2
  76. package/dist/workflowRetry.js +1 -2
  77. package/dist/workflowStartOutcome.js +1 -2
  78. package/package.json +7 -5
package/dist/index.d.ts CHANGED
@@ -1,13 +1,23 @@
1
+ import { ConnectionKind as ConnectionKind_2 } from '@voltro/kv/connection';
2
+ import { ConnectionRef } from '@voltro/kv/connection';
3
+ import { ConnectionRegistryShape } from '@voltro/kv/connection';
4
+ import { ConnectionUnavailablePolicy } from '@voltro/kv/connection';
1
5
  import { Context } from 'effect';
2
6
  import { DataStore } from '@voltro/database';
3
7
  import { Effect } from 'effect';
8
+ import { HotStoreShape } from '@voltro/kv/hot/contract';
4
9
  import { Layer } from 'effect';
5
10
  import { Option } from 'effect';
6
11
  import { Rpc } from 'effect/rpc';
7
12
  import { RpcMiddleware } from 'effect/rpc';
8
13
  import { Schema } from 'effect';
9
14
  import { SchemaAST } from 'effect';
15
+ import { Scope } from 'effect';
10
16
  import { SqlClient } from 'effect/sql';
17
+ import { StorageAddress } from '@voltro/kv/storage';
18
+ import { StorageDestinationRouter } from '@voltro/kv/storage';
19
+ import { StorageError } from '@voltro/kv/storage';
20
+ import { StorageProvider } from '@voltro/kv/storage';
11
21
  import { Stream } from 'effect/rpc/RpcSchema';
12
22
  import { VersionConflict } from '@voltro/database/wire';
13
23
  import { YieldableError } from 'effect/Cause';
@@ -137,7 +147,7 @@ export declare interface AnyApprovalPolicy {
137
147
 
138
148
  /** A guard entry is a scope check, a relationship check, an identity check, a
139
149
  * declared no-check-needed decision, or a service-only decision. */
140
- export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec | ServiceAccessSpec;
150
+ export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | UpgradedCheckSpec | OpenAccessSpec | ServiceAccessSpec;
141
151
 
142
152
  /** Any declared event, with the generics erased — for registries and audits. */
143
153
  /** The erased descriptor every registry holds. `guards` reads the key type
@@ -147,7 +157,10 @@ export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | Authentica
147
157
  export declare type AnyEventDescriptor = EventDescriptor<string, Schema.Top, Schema.Top, DeclaredAccess<never>>;
148
158
 
149
159
  /** A guard is a scope check, a relationship check, or an identity check. */
150
- export declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input> | AuthenticatedGuardSpec;
160
+ export declare type AnyGuardSpec<Input = unknown> = GuardSpec<Input> | PolicyGuardSpec<Input> | AuthenticatedGuardSpec | UpgradedGuardSpec;
161
+
162
+ /** Work performed by an API process; schema and descriptor loading is separate. */
163
+ export declare const API_WORKLOADS: readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"];
151
164
 
152
165
  export declare const APIKEY_ISSUE_ORG_SCOPE = "apikeys:issue:org";
153
166
 
@@ -180,6 +193,118 @@ export declare const APIKEY_ISSUE_OTHER_SCOPE = "apikeys:issue:other";
180
193
  */
181
194
  export declare const APIKEY_ISSUE_SELF_SCOPE = "apikeys:issue:self";
182
195
 
196
+ export declare interface ApiRoleEndpoint {
197
+ readonly role: string;
198
+ readonly routes: ReadonlyArray<string> | null;
199
+ readonly workloads: ReadonlyArray<string>;
200
+ readonly httpPath: string | null;
201
+ readonly webSocketPath: string;
202
+ }
203
+
204
+ export declare interface ApiRoleEndpoints {
205
+ readonly version: 1;
206
+ readonly transport: 'compose-nginx' | 'kubernetes-nginx';
207
+ readonly roles: ReadonlyArray<ApiRoleEndpoint>;
208
+ }
209
+
210
+ export declare const apiRoleEndpointUrl: (baseUrl: string, endpoint: ApiRoleEndpoint, transport: "http" | "websocket") => string;
211
+
212
+ export declare interface ApiRoleObservationReport {
213
+ readonly replicaId: string;
214
+ readonly startedAt: number;
215
+ readonly role: string;
216
+ readonly workloads: ReadonlyArray<ApiWorkload>;
217
+ readonly capturedAt: number;
218
+ readonly publisher: {
219
+ readonly ready: boolean;
220
+ readonly stopped: boolean;
221
+ readonly pendingBuckets: number;
222
+ readonly droppedBuckets: number;
223
+ readonly rejectedSegments: number;
224
+ readonly writeFailures: number;
225
+ readonly observationErrors: number;
226
+ };
227
+ readonly currentMeasuredSeconds: ReadonlyArray<{
228
+ readonly role: string;
229
+ readonly workload: ApiWorkload;
230
+ readonly seconds: number;
231
+ }>;
232
+ readonly history: {
233
+ readonly state: 'pending' | 'available' | 'unavailable';
234
+ readonly assessment: ApiRoleWindowAssessment | null;
235
+ /** Measured portions only; no share is inferred from incomplete coverage. */
236
+ readonly measuredSeconds: ReadonlyArray<{
237
+ readonly workload: ApiWorkload;
238
+ readonly seconds: number;
239
+ }>;
240
+ };
241
+ }
242
+
243
+ /** Browser-safe wire contract; no runtime or database imports. */
244
+ export declare const ApiRoleObservationSchema: Schema.Struct<{
245
+ readonly replicaId: Schema.NonEmptyString;
246
+ readonly startedAt: Schema.Number;
247
+ readonly role: Schema.NonEmptyString;
248
+ readonly workloads: Schema.$Array<Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>>;
249
+ readonly capturedAt: Schema.Number;
250
+ readonly publisher: Schema.Struct<{
251
+ readonly ready: Schema.Boolean;
252
+ readonly stopped: Schema.Boolean;
253
+ readonly pendingBuckets: Schema.Number;
254
+ readonly droppedBuckets: Schema.Number;
255
+ readonly rejectedSegments: Schema.Number;
256
+ readonly writeFailures: Schema.Number;
257
+ readonly observationErrors: Schema.Number;
258
+ }>;
259
+ readonly currentMeasuredSeconds: Schema.$Array<Schema.Struct<{
260
+ readonly role: Schema.NonEmptyString;
261
+ readonly workload: Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>;
262
+ readonly seconds: Schema.Number;
263
+ }>>;
264
+ readonly history: Schema.Struct<{
265
+ readonly state: Schema.Literals<readonly ["pending", "available", "unavailable"]>;
266
+ readonly measuredSeconds: Schema.$Array<Schema.Struct<{
267
+ readonly workload: Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>;
268
+ readonly seconds: Schema.Number;
269
+ }>>;
270
+ readonly assessment: Schema.NullOr<Schema.Struct<{
271
+ readonly from: Schema.Number;
272
+ readonly until: Schema.Number;
273
+ readonly complete: Schema.Boolean;
274
+ readonly missingBuckets: Schema.Number;
275
+ readonly incompleteWorkloads: Schema.$Array<Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>>;
276
+ readonly reason: Schema.Literals<readonly ["invalid-observation", "incomplete", "idle", "observed"]>;
277
+ readonly totals: Schema.$Array<Schema.Struct<{
278
+ readonly workload: Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>;
279
+ readonly seconds: Schema.Number;
280
+ readonly share: Schema.Number;
281
+ }>>;
282
+ readonly splitCandidates: Schema.$Array<Schema.Literals<readonly ["rpc", "subscriptions", "events", "presence", "workflows", "schedules", "directors", "drainers", "reapers", "retention", "rest", "webhooks", "inspect", "cdc"]>>;
283
+ }>>;
284
+ }>;
285
+ }>;
286
+
287
+ /** Reserved web proxy base; the API name isolates identical role names. */
288
+ export declare const apiRoleProxyBasePath: (apiName: string) => string;
289
+
290
+ export declare type ApiRoleRpcWorkload = 'rpc' | 'subscriptions' | 'events';
291
+
292
+ export declare interface ApiRoleWindowAssessment {
293
+ readonly from: number;
294
+ readonly until: number;
295
+ readonly complete: boolean;
296
+ readonly missingBuckets: number;
297
+ readonly incompleteWorkloads: ReadonlyArray<ApiWorkload>;
298
+ readonly reason: 'invalid-observation' | 'incomplete' | 'idle' | 'observed';
299
+ readonly totals: ReadonlyArray<{
300
+ readonly workload: ApiWorkload;
301
+ readonly seconds: number;
302
+ readonly share: number;
303
+ }>;
304
+ /** Advisory evidence only: no topology mutation and no acceptance claim. */
305
+ readonly splitCandidates: ReadonlyArray<ApiWorkload>;
306
+ }
307
+
183
308
  export declare class ApiUnavailable extends ApiUnavailable_base {
184
309
  }
185
310
 
@@ -193,6 +318,8 @@ declare const ApiUnavailable_base: Schema.Class<ApiUnavailable, Schema.TaggedStr
193
318
  readonly message: Schema.String;
194
319
  }>, YieldableError>;
195
320
 
321
+ export declare type ApiWorkload = typeof API_WORKLOADS[number];
322
+
196
323
  /** The subject a decision produces, plus what the decision took away. `removed`
197
324
  * is never non-empty for a `grant`. */
198
325
  export declare interface AppliedScopes {
@@ -625,6 +752,13 @@ export declare interface AuthStrategy {
625
752
  export declare interface AuthStrategyInput {
626
753
  readonly headers: Readonly<Record<string, string | undefined>>;
627
754
  readonly clientId: number;
755
+ /** HTTP request target, supplied by REST transports. Request selectors may
756
+ * read an explicit tenant hint here; it is never authentication or authority.
757
+ * Absent for connection transports and non-HTTP callers. */
758
+ readonly http?: {
759
+ readonly path: string;
760
+ readonly query: string;
761
+ };
628
762
  /**
629
763
  * The app's DataStore, for a strategy that must READ to identify the caller.
630
764
  *
@@ -673,8 +807,9 @@ export declare interface AuthStrategyWithCallback extends AuthStrategy {
673
807
  export declare type AuthSubjectGuard = (subject: Subject, ctx: AuthSubjectGuardContext) => Promise<AuthSubjectGuardVerdict> | AuthSubjectGuardVerdict;
674
808
 
675
809
  export declare interface AuthSubjectGuardContext {
676
- /** `'request'` for a live call, `'workflow'` for a durable run being executed. */
677
- readonly origin: 'request' | 'workflow';
810
+ /** `'request'` for a live call, `'workflow'` for a durable run being executed,
811
+ * `'room'` for a Room seat redeemed on a Room host (no request headers). */
812
+ readonly origin: 'request' | 'workflow' | 'room';
678
813
  /** The request's headers. EMPTY for `origin: 'workflow'`. */
679
814
  readonly headers: Readonly<Record<string, string | undefined>>;
680
815
  /** `undefined` for `origin: 'workflow'` and for transports with no connection. */
@@ -724,7 +859,7 @@ export declare type AuthSubjectGuardVerdict = {
724
859
  * `fresh` the caller runs the handler then calls `finish`/`fail`; on
725
860
  * `replay`/`conflict` it returns immediately without touching the handler.
726
861
  */
727
- export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, ttlMs: number, now: number, inFlightLeaseMs?: number) => Promise<IdempotencyOutcome>;
862
+ export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, inputHash: string, ttlMs: number, now: number, inFlightLeaseMs?: number) => Promise<IdempotencyOutcome>;
728
863
 
729
864
  /** Bind a process-local authority refresh to an independent subject snapshot.
730
865
  * Neither the resolver nor its credentials become serializable subject fields.
@@ -776,6 +911,24 @@ export declare interface CachedScopeDecision {
776
911
  readonly fresh: boolean;
777
912
  }
778
913
 
914
+ /**
915
+ * Canonical JSON — object keys sorted recursively, so two structurally equal
916
+ * inputs that differ only in key order digest identically. A retry from a
917
+ * different client build must not mint a second approval.
918
+ * Preserve own JSON fields such as `__proto__`; a prototype setter must never
919
+ * discard part of the content that an approval identity binds.
920
+ *
921
+ * Lives here, not in `@voltro/runtime`, so identity digests in modules that
922
+ * must not load the server runtime (storage metadata, browser-safe code) use
923
+ * the same encoding.
924
+ */
925
+ export declare const canonicalJson: (value: unknown) => string;
926
+
927
+ export declare const captureApiRoleEndpoints: (value: unknown) => ApiRoleEndpoints;
928
+
929
+ /** Capture optional integration observations without changing reactive triggers. */
930
+ export declare const captureOptionalQuerySources: (name: string, source: string | ReadonlyArray<string> | undefined, optional: unknown) => ReadonlyArray<string> | undefined;
931
+
779
932
  /**
780
933
  * Validate `plugin.framework` (npm-semver range) against the running
781
934
  * voltro version. Returns either `{ ok: true }` or
@@ -850,6 +1003,9 @@ export declare interface ClientDescriptor {
850
1003
  * (`<DataTable>` derives its columns from it) + the capability map can
851
1004
  * introspect it. Same browser-safety rationale as `input`. */
852
1005
  readonly output?: Schema.Top | undefined;
1006
+ /** The procedure's own declared error schema, by reference like input/output.
1007
+ * This is not the widened framework/plugin wire error union. */
1008
+ readonly error?: Schema.Top | undefined;
853
1009
  }
854
1010
 
855
1011
  /**
@@ -1020,6 +1176,8 @@ export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrat
1020
1176
  readonly getStore?: () => unknown;
1021
1177
  }) => ((input: AuthStrategyInput) => Promise<SubjectResolution>);
1022
1178
 
1179
+ export declare const composeReplayAuthorityBindings: (plugins: ReadonlyArray<unknown>) => RpcReplayAuthorityBinding | undefined;
1180
+
1023
1181
  /**
1024
1182
  * Compose a list of Effect-native interceptors into ONE function the
1025
1183
  * runtime calls. Reduces right-to-left so the array order reads
@@ -1090,6 +1248,11 @@ export declare class ConnectionInfoMiddleware extends ConnectionInfoMiddleware_b
1090
1248
  declare const ConnectionInfoMiddleware_base: RpcMiddleware.ServiceClass<ConnectionInfoMiddleware, "@voltro/ConnectionInfoMiddleware", ConnectionInfo, Schema.Never, never, never, false>;
1091
1249
 
1092
1250
  export declare interface ConnectionInfoValue {
1251
+ /** The effective per-call Cookie header, including current connection
1252
+ * credential patches. Server-only input for app-specific signed-cookie
1253
+ * verification; not a verified identity. Never serialize it into responses
1254
+ * or public page state. Absent when the caller presented no cookies. */
1255
+ readonly cookieHeader?: string;
1093
1256
  /** Per-connection identifier assigned by the rpc transport. Stable for the
1094
1257
  * lifetime of the underlying WebSocket connection; reused as the key for
1095
1258
  * the per-connection subject override map. */
@@ -1353,8 +1516,9 @@ declare const ConstraintViolation_base: Schema.Class<ConstraintViolation, Schema
1353
1516
  * plugin wants to cancel EARLY (before shutdown).
1354
1517
  */
1355
1518
  export declare interface CoordinatedScheduleHandle {
1356
- /** Stop the periodic task. Idempotent. */
1357
- readonly stop: () => void;
1519
+ /** Stop future admission immediately; await completion before releasing the
1520
+ * task dependencies. Idempotent; synchronous handles finish immediately. */
1521
+ readonly stop: () => void | Promise<void>;
1358
1522
  /** The task name (for logging / dedup diagnostics). */
1359
1523
  readonly name: string;
1360
1524
  /**
@@ -1413,6 +1577,13 @@ export declare interface CoordinatedTickOutcome {
1413
1577
  readonly nextDueInMs?: number | null;
1414
1578
  }
1415
1579
 
1580
+ /** Capture the complete participant list once. Undeclared SQL tables refuse
1581
+ * before any hook; erasure and merge are distinct policies and never substitute. */
1582
+ export declare const createPluginAccountMergeRegistry: (plugins: ReadonlyArray<VoltroPlugin>) => PluginAccountMergeRegistry;
1583
+
1584
+ /** Captures hooks once. Does not read a store, schedule work, retry or grant authority. */
1585
+ export declare const createPluginPrivacyRegistry: (plugins: ReadonlyArray<VoltroPlugin>) => PluginPrivacyRegistry;
1586
+
1416
1587
  /** Index reuse for one subscription's immutable snapshots and returned rows.
1417
1588
  * Arbitrary mutable input must use applyRowPatch. A different base rebuilds
1418
1589
  * the index; reset releases it on snapshot replacement or owner disposal. */
@@ -1436,6 +1607,9 @@ export declare const currentSubjectAuthority: (subject: Subject) => Subject;
1436
1607
  */
1437
1608
  export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec | ServiceAccessSpec>;
1438
1609
 
1610
+ /** The `_tag` literals a descriptor's `error:` union declares (empty for none). */
1611
+ export declare const declaredErrorTags: (error: Schema.Top | undefined) => ReadonlySet<string>;
1612
+
1439
1613
  /** Every channel declared in this process, by routing key. */
1440
1614
  export declare const declaredReactivityChannelKeys: () => ReadonlySet<string>;
1441
1615
 
@@ -1608,10 +1782,9 @@ export declare const defineEvent: <const Name extends string, Key extends Schema
1608
1782
  * resource-scoped rule ("may this terminal watch this arena") stays
1609
1783
  * declarative. `ScopeError` is merged into the wire error union automatically.
1610
1784
  *
1611
- * Re-checked when the SUBJECT changes (a revoked role ends the stream), not on
1612
- * every delivery. Per-delivery authorization is the design a comparable
1613
- * product measured into a scaling wall: one check per subscriber per message
1614
- * makes throughput scale with the audience instead of the publish rate.
1785
+ * Checked before subscription and each delivery. Resource resolvers can observe
1786
+ * live revocation; captured subject scopes remain bounded by credential expiry.
1787
+ * Additional private payload filtering uses the app-owned clientPolicy.
1615
1788
  */
1616
1789
  readonly guards?: Guards<Schema.Schema.Type<Key>>;
1617
1790
  /**
@@ -1672,6 +1845,8 @@ export declare const defineEvent: <const Name extends string, Key extends Schema
1672
1845
  * to miss one?" — not "is this event frequent?".
1673
1846
  */
1674
1847
  readonly delivery?: EventDeliverySemantics;
1848
+ /** Required private-client policy ID, resolved from the installing app. */
1849
+ readonly clientPolicy?: string;
1675
1850
  }) => EventDescriptor<Name, Key, Payload>;
1676
1851
 
1677
1852
  export declare const defineMutation: <const Name extends string, Input extends Schema.Top, Output extends Schema.Top, Error extends Schema.Top = typeof Schema.Never>(options: {
@@ -1704,6 +1879,13 @@ export declare const defineMutation: <const Name extends string, Input extends S
1704
1879
  * same way. Not a durable outbox, a distributed lock or a cross-tab merge.
1705
1880
  */
1706
1881
  readonly serialize?: MutationSerialization<Schema.Schema.Type<Input>>;
1882
+ /** Exclude mutations without a valid row-image inverse (for example a
1883
+ * derived-index rebuild) from automatic undo. Omitted means the normal
1884
+ * environment-controlled capture. Transactions, guards and audit still run. */
1885
+ readonly undo?: {
1886
+ readonly capture: false;
1887
+ readonly reason: string;
1888
+ };
1707
1889
  /** Optional declarative target(s) — drives auto-optimistic. */
1708
1890
  readonly target?: Target<Schema.Schema.Type<Input>, Schema.Schema.Type<Output>>;
1709
1891
  /** Declarative authorization guard(s) — the caller must hold the named
@@ -1857,6 +2039,9 @@ export declare const defineQuery: <const Name extends string, Input extends Sche
1857
2039
  * import edge is what makes a channel `source:` impossible to leave stale,
1858
2040
  * which a table name (a bare string) can always be. */
1859
2041
  readonly source?: ReactivitySource | ReadonlyArray<ReactivitySource>;
2042
+ /** Names in source that observe an optional integration. These remain in
2043
+ * the complete trigger set; unavailable optional tables warn in check. */
2044
+ readonly optionalSource?: ReadonlyArray<string>;
1860
2045
  /** Opt into server-side snapshot caching with auto-invalidation. */
1861
2046
  readonly cache?: QueryCacheConfig;
1862
2047
  /** Declarative authorization guard(s) — the caller must hold the named
@@ -2091,6 +2276,8 @@ export declare const effectiveScopes: (subject: Subject) => ReadonlyArray<string
2091
2276
  */
2092
2277
  export declare const encodeEventKey: (key: unknown) => string;
2093
2278
 
2279
+ export declare const encodeNativeFormCapture: (selector: string, presentationDigest: string) => string;
2280
+
2094
2281
  /** Encode a failure under `format`: the tagged body the desugar always wrote
2095
2282
  * (`{ error, …fields }`), Problem Details, or the format's own encoder. */
2096
2283
  export declare const encodeRestError: (format: RestFormat, error: RestFormatError, ctx: RestFormatContext) => RestFormatResponse;
@@ -2109,6 +2296,10 @@ export declare interface EnqueueOptions {
2109
2296
  readonly delayMs?: number;
2110
2297
  }
2111
2298
 
2299
+ /** The member of an error union that declares `tag`, as a schema of its own —
2300
+ * for the spec's per-status responses. */
2301
+ export declare const errorMemberSchema: (error: Schema.Top | undefined, tag: string) => Schema.Top | undefined;
2302
+
2112
2303
  /**
2113
2304
  * The text to SHOW for a failure value — its `message`, else its `_tag`, else
2114
2305
  * `String(value)`.
@@ -2169,6 +2360,34 @@ export declare const eventAttached: Schema.Struct<{
2169
2360
  readonly _tag: Schema.Literal<"attached">;
2170
2361
  }>;
2171
2362
 
2363
+ /** App-owned, server-only callbacks. Descriptors carry only the policy ID. */
2364
+ export declare interface EventClientPolicy {
2365
+ readonly id: string;
2366
+ /** Before bus subscription, including reattachment/resume. Only true admits. */
2367
+ readonly admit: (input: {
2368
+ readonly subject: Subject;
2369
+ readonly event: string;
2370
+ readonly key: unknown;
2371
+ }) => Effect.Effect<boolean, unknown>;
2372
+ /** Recheck current membership AND payload visibility. Deny ends the stream;
2373
+ * drop withholds this payload but keeps the subscription. No client filtering. */
2374
+ /** Register revocation before initial admission. The returned effect ends when
2375
+ * access may have changed; completion or failure ends the client stream.
2376
+ * Acquire resources in the supplied Scope; no detached background owner. */
2377
+ readonly monitor?: (input: {
2378
+ readonly subject: Subject;
2379
+ readonly event: string;
2380
+ readonly key: unknown;
2381
+ }) => Effect.Effect<Effect.Effect<void, unknown>, unknown, Scope.Scope>;
2382
+ readonly deliver: (input: {
2383
+ readonly subject: Subject;
2384
+ readonly event: string;
2385
+ readonly key: unknown;
2386
+ readonly payload: unknown;
2387
+ readonly emittedAt: number;
2388
+ }) => Effect.Effect<'allow' | 'drop' | 'deny', unknown>;
2389
+ }
2390
+
2172
2391
  /**
2173
2392
  * What it means for a subscriber to fall behind.
2174
2393
  *
@@ -2205,9 +2424,8 @@ export declare interface EventDescriptor<Name extends string, Key extends Schema
2205
2424
  readonly key: Key;
2206
2425
  readonly payload: Payload;
2207
2426
  /** Who may LISTEN — the author's guards, or the erased form of their
2208
- * `openAccess:` decision, never both (see `DeclaredAccess`). Re-checked when
2209
- * the subject changes, not per delivery — see `EventDefinition.guards` for
2210
- * why that distinction is deliberate. */
2427
+ * `openAccess:` decision, never both (see `DeclaredAccess`). Resource guards are checked before subscription and delivery; credential
2428
+ * expiry bounds captured subject authority. */
2211
2429
  readonly guards: Guards | undefined;
2212
2430
  /** The declared `openAccess:` reason, when there is one — what the boot gate
2213
2431
  * and `voltro doctor` read. The enforcement paths read `guards`. */
@@ -2218,6 +2436,8 @@ export declare interface EventDescriptor<Name extends string, Key extends Schema
2218
2436
  readonly delivery: EventDeliverySemantics | undefined;
2219
2437
  /** Opt this event into outbound HTTP delivery. See `EventWebhookSpec`. */
2220
2438
  readonly webhook: EventWebhookSpec | undefined;
2439
+ /** Required app-owned server policy, in addition to guards. No server imports in descriptors. */
2440
+ readonly clientPolicy?: string;
2221
2441
  }
2222
2442
 
2223
2443
  /**
@@ -2298,6 +2518,9 @@ export declare const eventResumePoint: Schema.Struct<{
2298
2518
 
2299
2519
  export declare const eventRoute: (tenantId: string | null, event: string, key: unknown) => string;
2300
2520
 
2521
+ /** Browser-safe execution ownership marker; wire schemas remain unchanged. */
2522
+ export declare const EventRpcWorkload: Context.Service<true, true>;
2523
+
2301
2524
  export declare type EventStreamEvent<P> = {
2302
2525
  readonly _tag: 'attached';
2303
2526
  } | {
@@ -2353,15 +2576,6 @@ export declare const eventSubscribeInput: <K extends Schema.Top>(key: K) => Sche
2353
2576
  }>>>;
2354
2577
  }>;
2355
2578
 
2356
- /**
2357
- * Lift an event descriptor into its rpc.
2358
- *
2359
- * A `stream: true` rpc, like a query — the framework's push transport is the
2360
- * WebSocket a subscription already holds open, and reusing it is why `useEvent`
2361
- * shares one connection lifecycle, one reconnect policy and one devtools view
2362
- * with `useSubscription` instead of being a second realtime concept in the same
2363
- * app.
2364
- */
2365
2579
  export declare const eventToRpc: <Name extends string, Key extends Schema.Top, Payload extends Schema.Top>(descriptor: EventDescriptor<Name, Key, Payload, ReadonlyArray<unknown>>, extraErrors?: ReadonlyArray<Schema.Top>) => Rpc.Rpc<Name, Schema.Struct<{
2366
2580
  readonly key: Key;
2367
2581
  readonly resume: Schema.optional<Schema.$Array<Schema.Struct<{
@@ -2444,8 +2658,8 @@ export declare interface ExposeAsToolSpec {
2444
2658
  */
2445
2659
  export declare type ExtraErrors = ReadonlyArray<Schema.Top>;
2446
2660
 
2447
- /** Release a claim after the handler errored, so a retry can re-process. */
2448
- export declare const failIdempotent: (store: IdempotencyStore, scope: string, key: string) => Promise<void>;
2661
+ /** Release this failed execution's running claim without deleting a successor. */
2662
+ export declare const failIdempotent: (store: IdempotencyStore, claim: IdempotencyClaim) => Promise<boolean>;
2449
2663
 
2450
2664
  /**
2451
2665
  * The field-addressed issues carried by a wire error, or `[]` when the error
@@ -2483,8 +2697,8 @@ export declare const findAdvisoryResourceGuards: (procedures: ReadonlyArray<{
2483
2697
  readonly guards?: ReadonlyArray<AnyCheckSpec>;
2484
2698
  }>) => ReadonlyArray<string>;
2485
2699
 
2486
- /** Record a completed response for replay. */
2487
- export declare const finishIdempotent: (store: IdempotencyStore, scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
2700
+ /** Record a response only while this original execution owns the claim. */
2701
+ export declare const finishIdempotent: (store: IdempotencyStore, claim: IdempotencyClaim, response: IdempotencyResponse, now: number) => Promise<boolean>;
2488
2702
 
2489
2703
  /**
2490
2704
  * The schema as the generator should see it: every `Number` node checked
@@ -2516,9 +2730,16 @@ export declare const finiteNumberView: (ast: SchemaAST.AST, memo?: Map<SchemaAST
2516
2730
  */
2517
2731
  export declare const firstEventValue: (tag: string, value: unknown, label: string) => unknown;
2518
2732
 
2733
+ export declare const FORM_CAPTURE_FIELD = "__voltro_capture";
2734
+
2735
+ export declare const FORM_CAPTURE_META_NAME = "voltro:form-source";
2736
+
2519
2737
  /** A route rendered for humans — logs, the inspect surface, the dashboard. */
2520
2738
  export declare const formatEventRoute: (route: string) => string;
2521
2739
 
2740
+ /** Build selector (snapshot/input/execution) followed by its public class digest. */
2741
+ export declare const formCaptureSource: (value: unknown) => string | undefined;
2742
+
2522
2743
  /**
2523
2744
  * The framework errors EVERY procedure of a kind carries on its wire union —
2524
2745
  * the unconditional members `wireErrorUnion` adds. The conditional ones (a
@@ -2533,8 +2754,8 @@ export declare const formatEventRoute: (route: string) => string;
2533
2754
  export declare const FRAMEWORK_WIRE_ERRORS: {
2534
2755
  readonly query: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused];
2535
2756
  readonly stream: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused];
2536
- readonly action: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused, typeof ValidationError, typeof ValidationErrors];
2537
- readonly mutation: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused, typeof BusinessRuleViolation, typeof MutationInFlight, typeof VersionConflict, typeof ValidationError, typeof ValidationErrors];
2757
+ readonly action: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused, typeof MutationInFlight, typeof MutationIdempotencyRefused, typeof ValidationError, typeof ValidationErrors];
2758
+ readonly mutation: readonly [typeof ScopeError, typeof Unauthenticated, typeof RequestedTenantRefused, typeof BusinessRuleViolation, typeof MutationInFlight, typeof MutationIdempotencyRefused, typeof VersionConflict, typeof ValidationError, typeof ValidationErrors];
2538
2759
  };
2539
2760
 
2540
2761
  /** The error values {@link FRAMEWORK_WIRE_ERRORS} admits for `Kind`. */
@@ -2619,35 +2840,17 @@ export declare interface GuardSpec<Input = unknown> {
2619
2840
  /** How an array of scopes combines. `'all'` (default) = AND (hold every
2620
2841
  * scope); `'any'` = OR (hold at least one). Ignored for a single scope. */
2621
2842
  readonly mode?: 'all' | 'any';
2622
- /**
2623
- * PURE `input → resource id` extractor. Browser-safe (no DB, no server
2624
- * import) — exactly like `target.identify`. Omit for a plain subject-scope
2625
- * guard.
2626
- *
2627
- * **On a `GuardSpec` this id is ADVISORY.** A scope guard answers "what may
2628
- * this subject do at all", against the subject's global scope set; the id is
2629
- * carried for logging and for a future subject-scope resolver that narrows by
2630
- * resource. It does not, on its own, make the check per-resource.
2631
- *
2632
- * **If your authority is per-resource, you want {@link PolicyGuardSpec}, not
2633
- * this field** — `guards: [{ action, resourceType, resource }]`, backed by
2634
- * `defineResourcePolicy` + a tuple source you register. That is built, wired
2635
- * on both boot paths, fail-closed without a resolver, and documented under
2636
- * *Authentication → Authorization*. An app whose relationships already live
2637
- * in its own tables (a `teamMembers` row, say) registers its own tuple source
2638
- * rather than copying data across; see `policyGuardResolver.ts`.
2639
- *
2640
- * That paragraph is here because its absence cost a deployment their access
2641
- * gate. This comment used to describe the resolver as "a future ReBAC /
2642
- * `accessPolicy()` resolver" — written before the ReBAC path shipped and
2643
- * never updated. They read the type, quoted the sentence, concluded there was
2644
- * "nothing in between" declaring an untruth and turning the gate off, and set
2645
- * `security: { defaultDeny: false }` on an app with 565 undecided procedures.
2646
- * The capability they needed was two fields away. A doc comment that says
2647
- * "future" about something shipped is not a small inaccuracy: it is the only
2648
- * thing a careful reader has, and it argued them out of a feature.
2649
- */
2843
+ /** Pure browser-safe input → target resource id selector. With a registered
2844
+ * setResourceScopeResolver, scopes can be checked on this resource. Without
2845
+ * resourceOnly, an existing global scope or admin:full may satisfy the guard
2846
+ * without consulting that resolver. For per-resource authority over relationships use
2847
+ * PolicyGuardSpec: `defineResourcePolicy` plus a registered tuple source. That is built, wired
2848
+ * on both boot paths and fail-closed without a resolver — an earlier comment called it "future"
2849
+ * and argued a deployment out of its access gate. */
2650
2850
  readonly resource?: (input: Input) => string | undefined;
2851
+ /** Require an explicit current resource verdict, including for globally held
2852
+ * scopes or admin:full. Missing resolver/resource refuses access. */
2853
+ readonly resourceOnly?: boolean;
2651
2854
  }
2652
2855
 
2653
2856
  /** Does this descriptor declare an access decision — a guard, or a deliberate
@@ -2677,8 +2880,9 @@ export declare const hasEnforcedGuard: (guards: DeclaredAccess | undefined) => b
2677
2880
  * the RAW subject scopes — use `hasEffectiveScope` to include rbac roles. */
2678
2881
  export declare const hasScope: (subject: Subject, scope: string) => boolean;
2679
2882
 
2680
- /** Renew a fresh claim's lease while its handler runs; returns the stop. */
2681
- export declare const holdIdempotentClaim: (store: IdempotencyStore, scope: string, key: string, inFlightLeaseMs?: number, now?: () => number) => (() => void);
2883
+ /** Renew only the captured execution identity; ownership loss stops renewal.
2884
+ * Stopping this timer never proves a remote business operation has stopped. */
2885
+ export declare const holdIdempotentClaim: (store: IdempotencyStore, claim: IdempotencyClaim, inFlightLeaseMs?: number, now?: () => number) => (() => void);
2682
2886
 
2683
2887
  /**
2684
2888
  * Short-circuit response shape. Returning a `HttpInterceptResponse`
@@ -2749,11 +2953,35 @@ export declare type HttpRequestInterceptor = (next: () => Promise<HttpInterceptR
2749
2953
  /** Fire-and-forget observer of plugin HTTP route calls — see {@link PluginHttpRouteEvent}. */
2750
2954
  export declare type HttpRouteObserver = (event: PluginHttpRouteEvent) => void;
2751
2955
 
2956
+ /** Identity of one acquired execution, never reused when a key is reclaimed.
2957
+ * This is receipt ownership, not authority to perform a business side effect. */
2958
+ export declare interface IdempotencyClaim {
2959
+ readonly scope: string;
2960
+ readonly key: string;
2961
+ readonly id: string;
2962
+ readonly inputHash: string;
2963
+ }
2964
+
2965
+ export declare type IdempotencyClaimResult = {
2966
+ readonly kind: 'claimed';
2967
+ readonly claim: IdempotencyClaim;
2968
+ } | {
2969
+ readonly kind: 'existing';
2970
+ readonly record: IdempotencyRecord;
2971
+ } | {
2972
+ readonly kind: 'conflict';
2973
+ };
2974
+
2752
2975
  export declare type IdempotencyOutcome = {
2753
2976
  readonly kind: 'fresh';
2977
+ readonly claim: IdempotencyClaim;
2754
2978
  } | {
2755
2979
  readonly kind: 'replay';
2756
2980
  readonly response: IdempotencyResponse;
2981
+ } | {
2982
+ readonly kind: 'input-conflict';
2983
+ } | {
2984
+ readonly kind: 'input-unbound';
2757
2985
  } | {
2758
2986
  readonly kind: 'conflict';
2759
2987
  };
@@ -2761,6 +2989,8 @@ export declare type IdempotencyOutcome = {
2761
2989
  export declare interface IdempotencyRecord {
2762
2990
  readonly scope: string;
2763
2991
  readonly key: string;
2992
+ /** Null means a retained receipt predates declared-input binding. */
2993
+ readonly inputHash: string | null;
2764
2994
  readonly status: 'in_flight' | 'completed';
2765
2995
  readonly response: IdempotencyResponse | null;
2766
2996
  /** epoch ms */
@@ -2770,38 +3000,41 @@ export declare interface IdempotencyRecord {
2770
3000
  export declare interface IdempotencyResponse {
2771
3001
  readonly status: number;
2772
3002
  readonly body: unknown;
3003
+ /** Framework-owned REST qualification metadata. Custom stores preserve it
3004
+ * unchanged, separately from the arbitrary application output in body. */
3005
+ readonly rest?: unknown;
2773
3006
  }
2774
3007
 
2775
3008
  /** Build the scope key — keep keys from colliding across tenants + endpoints. */
2776
3009
  export declare const idempotencyScope: (tenantId: string | null | undefined, method: string, path: string) => string;
2777
3010
 
2778
3011
  export declare interface IdempotencyStore {
2779
- /** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
2780
3012
  readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
2781
- /**
2782
- * Atomically claim (scope, key) IF absent OR stale — a COMPLETED record older
2783
- * than `ttlMs` (the replay window), or an IN-FLIGHT one whose lease
2784
- * (`inFlightLeaseMs`, renewed by the live handler) lapsed:
2785
- * write a fresh `in_flight` row and return `'claimed'`. Otherwise return the
2786
- * EXISTING (still-fresh) record. The atomicity here is the whole game — two
2787
- * concurrent same-key requests both hit this, exactly one gets `'claimed'`,
2788
- * the loser reads the winner's record. In SQL this is one
2789
- * `INSERT … ON CONFLICT DO UPDATE … WHERE existing.createdAt < now - ttl`.
2790
- */
2791
- readonly claim: (scope: string, key: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<'claimed' | IdempotencyRecord>;
2792
- /**
2793
- * Extend a RUNNING claim's lease: bump its timestamp while it is still
2794
- * `in_flight` (a completed or released claim is left alone). The server calls
2795
- * it while the handler runs; a process that dies stops calling it, and its
2796
- * claim goes stale after `inFlightLeaseMs` instead of after the whole replay
2797
- * window.
2798
- */
2799
- readonly renew: (scope: string, key: string, now: number) => Promise<void>;
2800
- /** Flip the claim to `completed` and store the response. */
2801
- readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
2802
- /** Drop the claim (handler errored → a retry may re-process). */
2803
- readonly release: (scope: string, key: string) => Promise<void>;
2804
- }
3013
+ /** Atomically acquire an absent/stale key with a new, never-reused identity.
3014
+ * Otherwise return the existing receipt, or conflict if contention prevents
3015
+ * observing a stable record. Expiry does not prove an old executor stopped. */
3016
+ readonly claim: (scope: string, key: string, inputHash: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<IdempotencyClaimResult>;
3017
+ /** Atomically match original scope/key/id AND in_flight status. False means
3018
+ * this execution no longer owns a running receipt; never touch its successor. */
3019
+ readonly renew: (claim: IdempotencyClaim, now: number) => Promise<boolean>;
3020
+ /** Complete only the original running claim and start its replay window now. */
3021
+ readonly complete: (claim: IdempotencyClaim, response: IdempotencyResponse, now: number) => Promise<boolean>;
3022
+ /** Release only the original running claim. Completed receipts are retained. */
3023
+ readonly release: (claim: IdempotencyClaim) => Promise<boolean>;
3024
+ }
3025
+
3026
+ /** What an identity's authority resolved to: the subject to run as, or why not. */
3027
+ export declare type IdentityAuthorityOutcome = {
3028
+ readonly kind: 'resolved';
3029
+ readonly subject: Subject;
3030
+ } | {
3031
+ readonly kind: 'unavailable';
3032
+ readonly reason: string;
3033
+ } | {
3034
+ readonly kind: 'vetoed';
3035
+ readonly code: string;
3036
+ readonly message: string;
3037
+ };
2805
3038
 
2806
3039
  /** `/<id>` — the op path for a row id. Ids are JSON-pointer-escaped
2807
3040
  * (`~` → `~0`, `/` → `~1`) so a slash or tilde inside a string id can't
@@ -2947,6 +3180,10 @@ export declare const isSystemSubject: (subject: Subject) => boolean;
2947
3180
 
2948
3181
  export declare const isTenantSelectable: (subject: Subject) => subject is TenantSelectableSubject;
2949
3182
 
3183
+ export declare const isUpgradedCheck: (g: AnyCheckSpec) => g is UpgradedCheckSpec;
3184
+
3185
+ export declare const isUpgradedGuard: <I>(g: AnyGuardSpec<I>) => g is UpgradedGuardSpec;
3186
+
2950
3187
  /**
2951
3188
  * Is this descriptor allowed on the wire at all?
2952
3189
  *
@@ -3024,7 +3261,7 @@ export declare interface Membership {
3024
3261
  * project from wherever it keeps them. `input.store` is the app's DataStore. */
3025
3262
  export declare type MembershipsSource = (subject: Subject, input: AuthStrategyInput) => Promise<ReadonlyArray<Membership>> | ReadonlyArray<Membership>;
3026
3263
 
3027
- /** In-memory store — the default for single-process dev + the test double. */
3264
+ /** In-memory store — one process only, with unique ownership per acquisition. */
3028
3265
  export declare const memoryIdempotencyStore: () => IdempotencyStore;
3029
3266
 
3030
3267
  /** The refusal a missing access decision produces. Exported so the boot gate
@@ -3065,6 +3302,18 @@ declare const MutationConnectionRequired_base: Schema.Class<MutationConnectionRe
3065
3302
  readonly traceId: Schema.String;
3066
3303
  }>, YieldableError>;
3067
3304
 
3305
+ /** Input refusal is terminal for this key, not an in-flight retry invitation.
3306
+ * No stored hash, input or response is disclosed. Check the original operation
3307
+ * before deliberately starting another business action. */
3308
+ export declare class MutationIdempotencyRefused extends MutationIdempotencyRefused_base {
3309
+ get message(): string;
3310
+ }
3311
+
3312
+ declare const MutationIdempotencyRefused_base: Schema.Class<MutationIdempotencyRefused, Schema.TaggedStruct<"MutationIdempotencyRefused", {
3313
+ readonly traceId: Schema.String;
3314
+ readonly reason: Schema.Literals<readonly ["input-conflict", "input-unbound", "input-unavailable"]>;
3315
+ }>, YieldableError>;
3316
+
3068
3317
  /** Ordinary writes deduplicate across reconnects. A connection-side effect
3069
3318
  * (for example joining a room) must execute once for EACH native connection. */
3070
3319
  export declare type MutationIdempotencyScope = 'subject' | 'connection';
@@ -3092,6 +3341,12 @@ export declare interface MutationProcedureDescriptor<Name extends string, Input
3092
3341
  readonly idempotencyScope?: MutationIdempotencyScope | undefined;
3093
3342
  /** Same-key calls run in invocation order on the client — see `defineMutation`. */
3094
3343
  readonly serialize?: MutationSerialization<Schema.Schema.Type<Input>> | undefined;
3344
+ /** Exclude this invocation from automatic row-image undo capture, with an
3345
+ * explicit domain reason. Does not disable transactions, guards or audit. */
3346
+ readonly undo?: {
3347
+ readonly capture: false;
3348
+ readonly reason: string;
3349
+ } | undefined;
3095
3350
  /** Declarative target(s) — drives auto-optimistic on the client AND
3096
3351
  * surfaces which tables this mutation touches (debug, future
3097
3352
  * query-invalidation analytics). Mutations without a target run
@@ -3153,6 +3408,12 @@ export declare interface MutationSerialization<Input = unknown> {
3153
3408
 
3154
3409
  export declare const mutationToRpc: <Name extends string, Input extends Schema.Top, Output extends Schema.Top, Err extends Schema.Top>(descriptor: MutationProcedureDescriptor<Name, Input, Output, Err>, extraErrors?: ExtraErrors) => Rpc.Rpc<Name, Input extends Schema.Struct.Fields ? Schema.Struct<Input> : Input, Output, Schema.Top, never, never>;
3155
3410
 
3411
+ export declare interface NativeFormCaptureDocument {
3412
+ readonly querySelectorAll: (selector: string) => ArrayLike<{
3413
+ readonly getAttribute: (name: string) => string | null;
3414
+ }>;
3415
+ }
3416
+
3156
3417
  export declare interface NestedTargetFields<Input = unknown> {
3157
3418
  /** Dot-path to the nested array within the query VALUE to patch (e.g.
3158
3419
  * `'snapshot.projects'`). Absent → patch the top-level row array (default). */
@@ -3390,6 +3651,21 @@ export declare const parseEventRoute: (route: string) => {
3390
3651
  /** The record from a link, or `null` for anything that is not a pairing link. */
3391
3652
  export declare const parsePairingLink: (url: string) => Pairing | null;
3392
3653
 
3654
+ /**
3655
+ * Parse a `revalidate` page export into a duration in milliseconds.
3656
+ *
3657
+ * Accepted shapes:
3658
+ * - number → seconds (Next.js compatibility)
3659
+ * - '<N> seconds' / 's'
3660
+ * - '<N> minutes' / 'm'
3661
+ * - '<N> hours' / 'h'
3662
+ * - '<N> days' / 'd'
3663
+ *
3664
+ * Returns null if the value is missing or unrecognised — caller treats
3665
+ * `null` as "don't cache" (every request re-renders, same as plain SSR).
3666
+ */
3667
+ export declare const parseRevalidateMs: (value: unknown) => number | null;
3668
+
3393
3669
  export declare const patchCellSchema: Schema.decodeTo<Schema.Unknown, Schema.Unknown, never, never>;
3394
3670
 
3395
3671
  /** A single row in a subscription result. Must carry an `id`; everything
@@ -3435,6 +3711,49 @@ export declare type PendingApproval = Schema.Schema.Type<typeof PendingApproval>
3435
3711
  /** The normalisation above, applied to any JSON Schema fragment. */
3436
3712
  export declare const plainJsonSchema: (value: unknown) => unknown;
3437
3713
 
3714
+ export declare interface PluginAccountMergeContext extends PluginAccountMergeRequest {
3715
+ /** Borrow the coordinator's existing transaction. No external I/O, separate
3716
+ * pool, nested transaction or session grant belongs in a data hook. */
3717
+ readonly store: DataStore;
3718
+ }
3719
+
3720
+ export declare interface PluginAccountMergeCoverage {
3721
+ readonly participants: ReadonlyArray<{
3722
+ readonly name: string;
3723
+ readonly tables: ReadonlyArray<string>;
3724
+ }>;
3725
+ readonly missing: ReadonlyArray<string>;
3726
+ }
3727
+
3728
+ export declare class PluginAccountMergeFailure extends PluginAccountMergeFailure_base {
3729
+ }
3730
+
3731
+ declare const PluginAccountMergeFailure_base: Schema.Class<PluginAccountMergeFailure, Schema.TaggedStruct<"PluginAccountMergeFailure", {
3732
+ readonly code: Schema.Literals<readonly ["invalid_request", "coverage_missing", "hook_failed"]>;
3733
+ readonly requestId: Schema.String;
3734
+ readonly plugin: Schema.NullOr<Schema.String>;
3735
+ /** Hooks that returned before failure; never evidence of SQL commit. */
3736
+ readonly returned: Schema.$Array<Schema.String>;
3737
+ readonly missing: Schema.$Array<Schema.String>;
3738
+ }>, YieldableError>;
3739
+
3740
+ export declare type PluginAccountMergeHook = (context: PluginAccountMergeContext) => Effect.Effect<void, unknown>;
3741
+
3742
+ export declare interface PluginAccountMergeRegistry {
3743
+ readonly coverage: (kind: 'user' | 'guest') => PluginAccountMergeCoverage;
3744
+ /** The caller owns both-account authorization, confirmation, the transaction
3745
+ * and durable receipt. This registry neither commits nor withdraws identity. */
3746
+ readonly mergeInTransaction: (context: PluginAccountMergeContext) => Effect.Effect<ReadonlyArray<string>, PluginAccountMergeFailure>;
3747
+ }
3748
+
3749
+ export declare interface PluginAccountMergeRequest {
3750
+ readonly requestId: string;
3751
+ readonly tenantId: string;
3752
+ readonly sourceId: string;
3753
+ readonly sourceKind: 'user' | 'guest';
3754
+ readonly targetId: string;
3755
+ }
3756
+
3438
3757
  /**
3439
3758
  * Called once at app boot, after `voltro dev` resolves the plugin
3440
3759
  * list and before the rpc server starts accepting connections. Use
@@ -3457,6 +3776,37 @@ export declare type PluginActivateHook = (ctx: PluginLifecycleContext) => Effect
3457
3776
  export declare const pluginAlias: (pluginName: string) => string;
3458
3777
 
3459
3778
  export declare interface PluginBindContext {
3779
+ /** Selected API work; absent custom-host selection means all workloads. */
3780
+ readonly workloads?: ReadonlyArray<ApiWorkload>;
3781
+ /** Starts the app's workflows from server work that has no request, such as
3782
+ * Room results. Present when the app declares workflows. */
3783
+ readonly workflows?: PluginWorkflowStarter;
3784
+ /** Execute an already-provided Effect, preserving typed failures as rejection
3785
+ * values. This does not inject request identity, app services or a new scope. */
3786
+ readonly runEffect?: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>;
3787
+ /** Execute server work with the actual shared app services, including user
3788
+ * Layer overrides, in a scope owned by this call. Available after the host
3789
+ * assembles its service layer, never during store binding or shutdown. No
3790
+ * request Subject, request-local store or transaction is injected. Consume
3791
+ * borrowed services inside the Effect; do not return them beyond its scope.
3792
+ * The host drains admitted calls before plugin/pool teardown. */
3793
+ readonly runWithAppServices?: <A, E, R>(effect: Effect.Effect<A, E, R>) => Promise<A>;
3794
+ /** App-owned subject hook registry. The server caller owns authorization and withdrawal. */
3795
+ readonly privacyRegistry?: PluginPrivacyRegistry;
3796
+ /** App-owned plugin namespace; tenant acquisition remains explicit and isolated. */
3797
+ readonly hotStore?: (context?: {
3798
+ readonly tenantId: string;
3799
+ }) => Promise<HotStoreShape>;
3800
+ /** Same borrowed registry as lifecycle hooks; the API host owns its pools. */
3801
+ readonly connectionRegistry?: ConnectionRegistryShape;
3802
+ readonly connectionReference?: PluginConnectionReference;
3803
+ readonly connectionAvailability?: PluginConnectionAvailability;
3804
+ /** Resolve the registered owner's retained destination for physical reference
3805
+ * protection. Borrowed app wiring; never substitutes the caller's provider. */
3806
+ readonly storageReferenceProvider?: (input: {
3807
+ readonly storageInstance: string;
3808
+ readonly address: StorageAddress;
3809
+ }) => Effect.Effect<StorageProvider, StorageError>;
3460
3810
  /**
3461
3811
  * This process's replica identity — the same value the event bus stamps as
3462
3812
  * its publish `origin` and the membership registry announces under.
@@ -3476,6 +3826,16 @@ export declare interface PluginBindContext {
3476
3826
  * who is on the channel, and an instance that dies simply goes quiet.
3477
3827
  */
3478
3828
  readonly membership?: {
3829
+ /** Current receiver-observed members from the same app-owned registry.
3830
+ * Optional for custom hosts providing only change notifications. Borrowed:
3831
+ * plugins must not start, stop or replace the membership owner. */
3832
+ readonly list?: () => ReadonlyArray<{
3833
+ readonly instanceId: string;
3834
+ readonly startedAt: number;
3835
+ readonly meta: Readonly<Record<string, string | number>> | undefined;
3836
+ readonly lastHeardAt: number;
3837
+ readonly self: boolean;
3838
+ }>;
3479
3839
  readonly onChange: (listener: (event: {
3480
3840
  readonly kind: 'joined' | 'left' | 'restarted';
3481
3841
  readonly instanceId: string;
@@ -3583,6 +3943,14 @@ export declare interface PluginCacheHandle {
3583
3943
 
3584
3944
  export declare interface PluginCapabilities {
3585
3945
  readonly mediaPersist?: PluginMediaPersistCapability;
3946
+ /** Borrow this app instance's bound object transport; the caller owns no pools. */
3947
+ readonly storageProvider?: () => StorageProvider;
3948
+ /** Address-aware object ownership; request tenant resolution is never global. */
3949
+ readonly storageDestinations?: {
3950
+ readonly router: StorageDestinationRouter;
3951
+ readonly directDestination: string | undefined;
3952
+ readonly registry: () => ConnectionRegistryShape | undefined;
3953
+ };
3586
3954
  }
3587
3955
 
3588
3956
  /**
@@ -3704,6 +4072,36 @@ export declare interface PluginCodegenContext {
3704
4072
  }>;
3705
4073
  }
3706
4074
 
4075
+ /** Resolves only this plugin's admitted role; omitted policy means fail. */
4076
+ export declare type PluginConnectionAvailability = (role: string) => ConnectionUnavailablePolicy;
4077
+
4078
+ export declare interface PluginConnectionNeed {
4079
+ readonly onUnavailable?: ConnectionUnavailablePolicy;
4080
+ readonly kind: ConnectionKind_2;
4081
+ readonly role: string;
4082
+ readonly crossNode?: boolean;
4083
+ /** Use the plugin's direct transport only when no declaration or binding selects this role. */
4084
+ readonly optional?: boolean;
4085
+ }
4086
+
4087
+ /**
4088
+ * Per-app context handed to lifecycle hooks. The shape is intentionally
4089
+ * thin — the framework's deeper services (DataStore, Logger, etc.) are
4090
+ * available via the per-request `AppContext` plugins see in their rpc
4091
+ * interceptors. Lifecycle hooks run OUTSIDE any request, so they
4092
+ * receive a minimal slice:
4093
+ *
4094
+ * - `app`: the resolved app-config metadata
4095
+ * (`{ name, type, voltroVersion }`).
4096
+ * - `logger`: a scoped `@voltro/logger` instance keyed to the
4097
+ * plugin's `name`, so log lines are attributable.
4098
+ * - `env`: the process env (alias of `process.env` for typing).
4099
+ * - `config`: the plugin's own validated config (if `configSchema`
4100
+ * declared) — already decoded against the plugin's Schema, ready
4101
+ * for the hook to consume. Unknown otherwise.
4102
+ */
4103
+ export declare type PluginConnectionReference = <K extends ConnectionKind_2>(role: string, kind: K) => ConnectionRef<K> | '';
4104
+
3707
4105
  /**
3708
4106
  * A remote-mounted dashboard surface a plugin contributes. The
3709
4107
  * dashboard-host (voltro-cloud / voltro-devtools) renders the mount
@@ -3814,6 +4212,8 @@ export declare interface PluginEnvVar {
3814
4212
  readonly generate?: 'base64url' | 'hex' | 'p256';
3815
4213
  }
3816
4214
 
4215
+ export declare type PluginEraseHook = (subjectId: string, context: PluginPrivacyHookContext) => Effect.Effect<void, unknown>;
4216
+
3817
4217
  /**
3818
4218
  * A cross-cutting error schema a plugin merges into EVERY procedure's
3819
4219
  * wire error union. Use when a plugin interceptor can fail with a typed
@@ -3852,8 +4252,26 @@ export declare interface PluginErrorSchema {
3852
4252
  };
3853
4253
  }
3854
4254
 
4255
+ /**
4256
+ * Per-call context handed to a plugin-contributed rpc route's
4257
+ * executor. Mirror of the user-handler `AppContext` shape but typed
4258
+ * loosely so the plugin doesn't need to depend on `@voltro/runtime`.
4259
+ */
4260
+ /** Structural view of the existing request-scoped typed publisher. No runtime import. */
4261
+ export declare interface PluginEventPublisher {
4262
+ publish<Name extends string, Key extends Schema.Top, Payload extends Schema.Top>(descriptor: EventDescriptor<Name, Key, Payload>, key: Schema.Schema.Type<Key>, payload: Schema.Schema.Type<Payload>): Effect.Effect<unknown, unknown>;
4263
+ }
4264
+
4265
+ export declare type PluginExportHook = (subjectId: string, context: PluginPrivacyHookContext) => Effect.Effect<unknown, unknown>;
4266
+
3855
4267
  /** A public raw-HTTP route a plugin serves on the framework listener. */
3856
4268
  export declare interface PluginHttpRoute {
4269
+ /** Executable API workload owning this route. Omitted means `rpc`.
4270
+ * Discovery and descriptor generation remain independent of role selection. */
4271
+ readonly workload?: ApiWorkload;
4272
+ /** The already-mounted REST projection, retained for SDK/schema inspection.
4273
+ * Do not remount it through generic REST auth or idempotency bindings. */
4274
+ readonly restContract?: RestRouteDescriptor<any, any>;
3857
4275
  /** HTTP method, or `'*'` for any (the handler decides). The server mounts the
3858
4276
  * path for exactly the methods its routes declare; `'*'` mounts it for every
3859
4277
  * method the router knows (34), so keep that for a route that really serves
@@ -3882,6 +4300,13 @@ export declare interface PluginHttpRoute {
3882
4300
  */
3883
4301
  readonly match?: 'prefix' | 'exact';
3884
4302
  readonly handle: (req: PluginHttpRouteRequest) => Promise<PluginHttpRouteResult>;
4303
+ /** Lazy, backpressured input instead of a buffered rawBody on real HTTP.
4304
+ * Requires match: 'exact' and exclusive ownership of the router-equivalent
4305
+ * path (including other methods). The required handle remains the buffered
4306
+ * in-process entry. Both entries must apply the same authorization/codec.
4307
+ * maxBodyBytes counts consumed bytes; overflow overrides the handler's
4308
+ * response with 413 even when the handler catches the read error. */
4309
+ readonly handleStream?: (req: PluginHttpRouteStreamRequest) => Promise<PluginHttpRouteResult>;
3885
4310
  /** Per-route body cap override (bytes) — wins over the listener's shared
3886
4311
  * `maxBodyBytes`. NOTE: routes SHARING a path share one body read, so the
3887
4312
  * widest override on the path's group applies to the whole group. */
@@ -4108,11 +4533,23 @@ export declare interface PluginHttpRouteResult {
4108
4533
  */
4109
4534
  export declare interface PluginHttpRouteStream {
4110
4535
  readonly subscribe: (emit: (chunk: string) => void) => () => void;
4536
+ /** Per-connection lifetime in milliseconds (1..2147483647). Ending the
4537
+ * response releases the subscription and keep-alive timer. Absent: no cap. */
4538
+ readonly maxDurationMs?: number;
4111
4539
  /** Keep-alive comment interval in ms (default 15000; `0` disables). Without
4112
4540
  * it an idle SSE connection is dropped by proxies after ~30–60s. */
4113
4541
  readonly keepAliveMs?: number;
4114
4542
  }
4115
4543
 
4544
+ /** A lazy binary request on the framework listener. No body bytes are read
4545
+ * before the handler runs. Consume once, through EOF, before accepting an
4546
+ * upload; returning early drains the unread body without validating it.
4547
+ * The signal ends on disconnect, interruption or handler completion. */
4548
+ export declare interface PluginHttpRouteStreamRequest extends Omit<PluginHttpRouteRequest, 'rawBody'> {
4549
+ readonly body: AsyncIterable<Uint8Array>;
4550
+ readonly signal: AbortSignal;
4551
+ }
4552
+
4116
4553
  export declare interface PluginInspectEndpoint {
4117
4554
  /** HTTP method. */
4118
4555
  readonly method: 'GET' | 'POST' | 'DELETE';
@@ -4241,23 +4678,9 @@ export declare const pluginInstanceName: (args: {
4241
4678
  readonly instance?: string | undefined;
4242
4679
  }) => string;
4243
4680
 
4244
- /**
4245
- * Per-app context handed to lifecycle hooks. The shape is intentionally
4246
- * thin — the framework's deeper services (DataStore, Logger, etc.) are
4247
- * available via the per-request `AppContext` plugins see in their rpc
4248
- * interceptors. Lifecycle hooks run OUTSIDE any request, so they
4249
- * receive a minimal slice:
4250
- *
4251
- * - `app`: the resolved app-config metadata
4252
- * (`{ name, type, voltroVersion }`).
4253
- * - `logger`: a scoped `@voltro/logger` instance keyed to the
4254
- * plugin's `name`, so log lines are attributable.
4255
- * - `env`: the process env (alias of `process.env` for typing).
4256
- * - `config`: the plugin's own validated config (if `configSchema`
4257
- * declared) — already decoded against the plugin's Schema, ready
4258
- * for the hook to consume. Unknown otherwise.
4259
- */
4260
4681
  export declare interface PluginLifecycleContext {
4682
+ /** Selected API work; absent custom-host selection means all workloads. */
4683
+ readonly workloads?: ReadonlyArray<ApiWorkload>;
4261
4684
  readonly app: {
4262
4685
  readonly name: string;
4263
4686
  readonly type: 'api' | 'web';
@@ -4272,6 +4695,11 @@ export declare interface PluginLifecycleContext {
4272
4695
  readonly env: NodeJS.ProcessEnv;
4273
4696
  /** Validated plugin config (if `configSchema` was declared). */
4274
4697
  readonly config: unknown;
4698
+ /** App-owned command registry, available before API plugin activation. Never dispose it. */
4699
+ readonly connectionRegistry?: ConnectionRegistryShape;
4700
+ /** Resolve only this plugin's declared role through the app's binding/default. */
4701
+ readonly connectionReference?: PluginConnectionReference;
4702
+ readonly connectionAvailability?: PluginConnectionAvailability;
4275
4703
  }
4276
4704
 
4277
4705
  /** Where the offloaded-inference dispatcher puts a media artifact a provider
@@ -4356,7 +4784,13 @@ export declare type PluginMigrationSqlClient = SqlClient.SqlClient;
4356
4784
  * boot log + `/_voltro/inspect/plugins` so operators audit what each
4357
4785
  * plugin asks for before installing.
4358
4786
  */
4359
- export declare type PluginPermission = 'rpc:intercept:mutation' | 'rpc:intercept:query' | 'rpc:intercept:action' | 'rpc:intercept:workflow' | 'schedule:fire' | 'schedule:read' | 'workflow:step' | 'http:intercept' | 'inspect:read' | 'inspect:write' | 'dashboard:mount' | 'outbox:handle' | 'store:write' | 'migration:run' | 'store:changes:read' | `secrets:read:${string}` | `network:outbound:${string}` | `plugin:hook:${string}`;
4787
+ export declare type PluginPermission = 'rpc:authority:replay' | 'rpc:intercept:mutation' | 'rpc:intercept:query' | 'rpc:intercept:action' | 'rpc:intercept:workflow' | 'rpc:intercept:stream' | 'schedule:fire' | 'schedule:read' | 'workflow:step' | 'http:intercept' | 'inspect:read' | 'inspect:write' | 'dashboard:mount' | 'outbox:handle' | 'store:write' | 'migration:run' | 'store:changes:read' | `secrets:read:${string}` | `network:outbound:${string}` | `plugin:hook:${string}`;
4788
+
4789
+ export declare interface PluginPrivacyCoverage {
4790
+ readonly participants: ReadonlyArray<PluginPrivacyParticipant>;
4791
+ /** Plugin declarations only. App-owned tables require their own declared hooks. */
4792
+ readonly missing: ReadonlyArray<string>;
4793
+ }
4360
4794
 
4361
4795
  /** What a plugin collects and uses — folded by `voltro compliance generate`
4362
4796
  * into PrivacyInfo.xcprivacy and the Play Data-safety answers. A plugin that
@@ -4366,12 +4800,76 @@ export declare interface PluginPrivacyDeclaration {
4366
4800
  readonly requiredReasonApis?: ReadonlyArray<PrivacyRequiredReasonApi>;
4367
4801
  }
4368
4802
 
4369
- /**
4370
- * Per-call context handed to a plugin-contributed rpc route's
4371
- * executor. Mirror of the user-handler `AppContext` shape but typed
4372
- * loosely so the plugin doesn't need to depend on `@voltro/runtime`.
4373
- */
4803
+ export declare type PluginPrivacyErasePhase = typeof PluginPrivacyErasePhaseSchema.Type;
4804
+
4805
+ /** Resource/actor withdrawal must finish before destructive data hooks. */
4806
+ export declare const PluginPrivacyErasePhaseSchema: Schema.Literals<readonly ["prepare", "data", "identity", "finalize"]>;
4807
+
4808
+ export declare interface PluginPrivacyErasure {
4809
+ readonly requestId: string;
4810
+ readonly target: PluginPrivacyTarget;
4811
+ readonly completed: ReadonlyArray<string>;
4812
+ }
4813
+
4814
+ export declare interface PluginPrivacyExport {
4815
+ readonly requestId: string;
4816
+ readonly target: PluginPrivacyTarget;
4817
+ readonly plugins: ReadonlyArray<{
4818
+ readonly name: string;
4819
+ readonly data: unknown;
4820
+ }>;
4821
+ }
4822
+
4823
+ /** Never carries a driver's error, credentials, exported rows or the hook's cause. */
4824
+ export declare class PluginPrivacyFailure extends PluginPrivacyFailure_base {
4825
+ }
4826
+
4827
+ declare const PluginPrivacyFailure_base: Schema.Class<PluginPrivacyFailure, Schema.TaggedStruct<"PluginPrivacyFailure", {
4828
+ readonly code: Schema.Literals<readonly ["invalid_request", "coverage_missing", "hook_failed", "unavailable"]>;
4829
+ readonly operation: Schema.Literals<readonly ["export", "erase"]>;
4830
+ readonly requestId: Schema.String;
4831
+ readonly plugin: Schema.NullOr<Schema.String>;
4832
+ readonly completed: Schema.$Array<Schema.String>;
4833
+ readonly missing: Schema.$Array<Schema.String>;
4834
+ }>, YieldableError>;
4835
+
4836
+ export declare interface PluginPrivacyHookContext {
4837
+ readonly requestId: string;
4838
+ readonly tenantId: string | null;
4839
+ readonly kind: 'user' | 'guest';
4840
+ /** Only the finalizer receives these immutable, successfully completed hook names. */
4841
+ readonly completed?: ReadonlyArray<string>;
4842
+ }
4843
+
4844
+ export declare interface PluginPrivacyParticipant {
4845
+ readonly name: string;
4846
+ readonly tables: ReadonlyArray<string>;
4847
+ readonly erasePhase: PluginPrivacyErasePhase;
4848
+ }
4849
+
4850
+ /** Borrowed app-owned hook port. Authorization and durable withdrawal belong to the caller. */
4851
+ export declare interface PluginPrivacyRegistry {
4852
+ readonly coverage: (kind: PluginPrivacyTarget['kind']) => PluginPrivacyCoverage;
4853
+ readonly exportSubject: (request: PluginPrivacyRequest) => Effect.Effect<PluginPrivacyExport, PluginPrivacyFailure>;
4854
+ readonly eraseSubject: (request: PluginPrivacyRequest) => Effect.Effect<PluginPrivacyErasure, PluginPrivacyFailure>;
4855
+ }
4856
+
4857
+ /** Reuse the request identity on recovery; hooks must be idempotent. */
4858
+ export declare interface PluginPrivacyRequest {
4859
+ readonly requestId: string;
4860
+ readonly target: PluginPrivacyTarget;
4861
+ }
4862
+
4863
+ /** A server-authorized subject, never an identity accepted from a client body. */
4864
+ export declare interface PluginPrivacyTarget {
4865
+ readonly subjectId: string;
4866
+ readonly tenantId: string | null;
4867
+ readonly kind: 'user' | 'guest';
4868
+ }
4869
+
4374
4870
  export declare interface PluginRouteContext {
4871
+ /** Borrows the host request publisher, preserving tenant and transaction ownership. */
4872
+ readonly events?: PluginEventPublisher;
4375
4873
  /** Request-scoped transaction for mutation routes. Reuse it instead of opening
4376
4874
  * a transaction on the plugin's bound root store. Absent on direct calls. */
4377
4875
  readonly mutationStore?: DataStore;
@@ -4474,6 +4972,8 @@ export declare interface PluginRpcRoute {
4474
4972
  * checking.
4475
4973
  */
4476
4974
  readonly source?: ReactivitySourceValue | ReadonlyArray<ReactivitySourceValue> | undefined;
4975
+ /** Optional integration observers; each name must also occur in source. */
4976
+ readonly optionalSource?: ReadonlyArray<string> | undefined;
4477
4977
  /**
4478
4978
  * The route's ACCESS DECISION — the same two-field vocabulary user
4479
4979
  * procedures carry, because a plugin route is dispatched through the exact
@@ -4540,6 +5040,23 @@ export declare interface PluginSchemaContribution {
4540
5040
  */
4541
5041
  export declare const pluginSlug: (pluginName: string) => string;
4542
5042
 
5043
+ export declare interface PluginSubjectMerge {
5044
+ readonly tables: ReadonlyArray<string>;
5045
+ /** Source kinds whose data this implementation can reconcile. */
5046
+ readonly kinds: ReadonlyArray<'user' | 'guest'>;
5047
+ }
5048
+
5049
+ /** SQL tables owned by this hook pair. External resources belong in the hooks too. */
5050
+ export declare interface PluginSubjectPrivacy {
5051
+ readonly tables: ReadonlyArray<string>;
5052
+ readonly kinds: ReadonlyArray<PluginPrivacyTarget['kind']>;
5053
+ /** Preparation withdraws active work before data hooks. Identity/session
5054
+ * deletion follows every data hook, regardless of plugin order. */
5055
+ /** Terminal request bookkeeping follows every data and identity hook.
5056
+ * At most one finalizer may own completion for each subject kind. */
5057
+ readonly erasePhase?: PluginPrivacyErasePhase;
5058
+ }
5059
+
4543
5060
  /**
4544
5061
  * The wire tag a plugin's own client code must call for one of its routes.
4545
5062
  *
@@ -4622,6 +5139,20 @@ export declare interface PluginTemplate {
4622
5139
  */
4623
5140
  export declare type PluginUninstallHook = (ctx: PluginLifecycleContext) => Effect.Effect<void, unknown> | Promise<void> | void;
4624
5141
 
5142
+ /** Starts app workflows from plugin server work outside a request. */
5143
+ export declare interface PluginWorkflowStarter {
5144
+ /** Resolves once the run is accepted. Runs as the plugin's system subject
5145
+ * (`plugin:<name>`); rejects while the host's workflow runtime is not ready. */
5146
+ start(workflowName: string, payload: unknown): Promise<unknown>;
5147
+ }
5148
+
5149
+ export declare type PluginWorkloadHook = (context: PluginLifecycleContext) => Effect.Effect<PluginWorkloadResource, unknown> | Promise<PluginWorkloadResource> | PluginWorkloadResource;
5150
+
5151
+ export declare interface PluginWorkloadResource {
5152
+ /** Await drainage; rejection retains ownership and must be retryable. */
5153
+ stop(): Effect.Effect<void, unknown> | Promise<void> | void;
5154
+ }
5155
+
4625
5156
  /** The runtime-erased form of `PolicyGuardSpec` — a relationship check. */
4626
5157
  export declare interface PolicyCheckSpec {
4627
5158
  readonly action: string | ((input: unknown) => string);
@@ -4678,6 +5209,17 @@ export declare interface PolicyGuardSpec<Input = unknown> {
4678
5209
  readonly resource: (input: Input) => string | undefined;
4679
5210
  }
4680
5211
 
5212
+ export declare interface PreparedIdempotentInput {
5213
+ readonly input: unknown;
5214
+ readonly fingerprint: string;
5215
+ }
5216
+
5217
+ /** Capture the declared schema's wire input before waiting for a receipt.
5218
+ * Decode an independent snapshot for execution and require an equal round trip.
5219
+ * The principal comes from server authentication, never a client digest/header.
5220
+ * This binds identity and input, not current permissions or a business lease. */
5221
+ export declare const prepareIdempotentInput: (schema: Schema.Top, offered: unknown, subject: Subject, transportInput?: unknown) => Promise<PreparedIdempotentInput>;
5222
+
4681
5223
  /**
4682
5224
  * An environment variable a plugin reads. DECLARATION ONLY — metadata, not a
4683
5225
  * read path: the plugin still reads the value itself (typically
@@ -4772,6 +5314,9 @@ export declare interface PublicApiClientConfig {
4772
5314
  readonly baseUrl?: string;
4773
5315
  }
4774
5316
 
5317
+ /** A descriptor kind that can be projected to REST (streams are not, in v1). */
5318
+ declare type PublicApiDescriptor = QueryProcedureDescriptor<string, Schema.Top, Schema.Top, Schema.Top> | MutationProcedureDescriptor<string, Schema.Top, Schema.Top, Schema.Top> | ActionProcedureDescriptor<string, Schema.Top, Schema.Top, Schema.Top>;
5319
+
4775
5320
  /** The `publicApi:` block of an api `app.config.ts`, and the shape of a
4776
5321
  * descriptor's `publicApi.format` override (every field optional there). */
4777
5322
  export declare interface PublicApiProfileConfig {
@@ -4848,6 +5393,10 @@ export declare interface PublicApiSpec {
4848
5393
  /** Streaming queries over request/response: 'snapshot' (default — first
4849
5394
  * snapshot, like POST /rpc) | 'sse' (Server-Sent Events of snapshot+deltas). */
4850
5395
  readonly stream?: 'snapshot' | 'sse';
5396
+ /** Per-connection SSE lifetime in milliseconds (1..2147483647). Requires
5397
+ * an SSE query projection. Clients may reconnect for a freshly authorized
5398
+ * snapshot; expiry does not cancel a domain operation. */
5399
+ readonly streamMaxDurationMs?: number;
4851
5400
  /**
4852
5401
  * HTTP status per DECLARED typed error tag, for the projection. A typed
4853
5402
  * error the handler fails with — one of the descriptor's `error:` union —
@@ -4859,6 +5408,11 @@ export declare interface PublicApiSpec {
4859
5408
  * publicApi: { errorStatus: { AccessDeniedError: 403, NotFoundError: 404 } }
4860
5409
  */
4861
5410
  readonly errorStatus?: Readonly<Record<string, number>>;
5411
+ /** Additional HTTP failure statuses for the same declared error schema.
5412
+ * Documents dedicated handlers that choose a status from the error's fields;
5413
+ * does not change `errorStatus` or the generic projection's runtime mapping.
5414
+ * Values must be integer HTTP error statuses (400–599); duplicates are omitted. */
5415
+ readonly additionalErrorStatuses?: Readonly<Record<string, ReadonlyArray<number>>>;
4862
5416
  /**
4863
5417
  * The spec's tag group(s) for this operation. Default: the rpc tag's prefix
4864
5418
  * (`teams.list` → `teams`) — the first path segment, what a consumer looks
@@ -4960,6 +5514,16 @@ export declare interface QueryCacheConfig {
4960
5514
  * cardinality of a cache the author sized per org.
4961
5515
  */
4962
5516
  readonly scope: 'subject' | 'tenant' | 'global';
5517
+ /**
5518
+ * The handler's result also depends on something no table write
5519
+ * invalidates — the clock, randomness, `ctx.connections`, an outgoing HTTP
5520
+ * call, `ctx.kv`. Applies to a COMPUTED query (a handler that returns a
5521
+ * value): without it such a result is computed but NOT kept, and the dev log
5522
+ * names what was used. With it, `ttl` is the bound on how stale that part may
5523
+ * be. `Date.now()`, `Math.random()` and a `fetch` outside the framework's
5524
+ * HTTP client are not observable — a handler that uses them must say so here.
5525
+ */
5526
+ readonly volatile?: boolean | undefined;
4963
5527
  }
4964
5528
 
4965
5529
  export declare interface QueryProcedureDescriptor<Name extends string, Input extends Schema.Top, Output extends Schema.Top, Error extends Schema.Top> {
@@ -4974,6 +5538,9 @@ export declare interface QueryProcedureDescriptor<Name extends string, Input ext
4974
5538
  * listed table changes (pass an array to depend on several). Optional:
4975
5539
  * queries without a source never receive auto-patches (joins, aggregates). */
4976
5540
  readonly source: string | ReadonlyArray<string> | undefined;
5541
+ /** Optional integration observers, all also present in source. They keep
5542
+ * invalidation active when installed; absence is a visible graph warning. */
5543
+ readonly optionalSource?: ReadonlyArray<string>;
4977
5544
  /** Server-side snapshot cache config. Absent → never cached (the
4978
5545
  * default; the dispatcher already keeps live subscriptions fresh). */
4979
5546
  readonly cache: QueryCacheConfig | undefined;
@@ -5128,6 +5695,8 @@ export declare type ReactivitySource = TableName | ReactivityChannel;
5128
5695
  */
5129
5696
  export declare type ReactivitySourceValue = string | ReactivityChannel;
5130
5697
 
5698
+ export declare const readFormCaptureSource: (document?: NativeFormCaptureDocument | undefined) => string | undefined;
5699
+
5131
5700
  export declare const readPairing: (storage: PairingStorage) => Promise<Pairing | null>;
5132
5701
 
5133
5702
  /**
@@ -5199,9 +5768,25 @@ export declare type RequestedTenantSource = (subject: Subject, input: AuthStrate
5199
5768
  * Checks the EFFECTIVE set so role-derived scopes count. */
5200
5769
  export declare const requireScope: (subject: Subject, scope: string) => Effect.Effect<void, ScopeError>;
5201
5770
 
5771
+ /** Browser-safe descriptor guard for capabilities that require registration. */
5772
+ export declare const requireUpgraded: () => UpgradedGuardSpec;
5773
+
5202
5774
  /** Drop every registration. Tests only — production registers once at module load. */
5203
5775
  export declare const resetPluginAliases: () => void;
5204
5776
 
5777
+ /**
5778
+ * Put authority back onto an IDENTITY that arrived without it — a resumed
5779
+ * durable run, a redeemed Room seat — through the app's own `resolveScopes`
5780
+ * and `subjectGuards`, the same seam a request uses. Credentials never carry
5781
+ * scopes (`subjectIdentity`), so every non-request consumer resolves here
5782
+ * rather than trusting authority frozen at mint time. With no resolver the
5783
+ * identity keeps what it carries (nothing), the fail-closed default.
5784
+ */
5785
+ export declare const resolveIdentityAuthority: (identity: Subject, ctx: AuthSubjectGuardContext, authority: {
5786
+ readonly resolveScopes?: ((subject: Subject, ctx: AuthSubjectGuardContext) => Promise<ScopeResolverResult> | ScopeResolverResult) | undefined;
5787
+ readonly subjectGuards?: ReadonlyArray<AuthSubjectGuard> | undefined;
5788
+ } | undefined) => Promise<IdentityAuthorityOutcome>;
5789
+
5205
5790
  /** Resolve the input-selected action once. Invalid selectors deny rather than
5206
5791
  * widening to any action the subject happens to hold. Also used when an
5207
5792
  * approval persists the resolved guard for its later recheck. */
@@ -5398,7 +5983,7 @@ export declare interface RestFormatResponse {
5398
5983
  readonly contentType?: string;
5399
5984
  }
5400
5985
 
5401
- declare type RestGuard = (ctx: RestRouteContext) => RestGuardRejection | undefined | Promise<RestGuardRejection | undefined>;
5986
+ declare type RestGuard = (ctx: RestRouteContext, request?: RestGuardRequest) => RestGuardRejection | undefined | Promise<RestGuardRejection | undefined>;
5402
5987
 
5403
5988
  /**
5404
5989
  * A guard runs after input decode + ctx resolution, before the handler.
@@ -5410,6 +5995,13 @@ declare interface RestGuardRejection {
5410
5995
  readonly message: string;
5411
5996
  }
5412
5997
 
5998
+ /** Guard input is the decoded execution snapshot. Guards are read-only
5999
+ * admission checks and may run again after awaited admission/storage work. */
6000
+ declare interface RestGuardRequest {
6001
+ readonly input: unknown;
6002
+ readonly defaultDeny: boolean;
6003
+ }
6004
+
5413
6005
  /** What a REST handler may return: the decoded output, a `response(...)` with
5414
6006
  * its own status and headers, an `sse(...)` stream, or `bytes(...)` for a
5415
6007
  * non-JSON body (a file, an export, an XML document). */
@@ -5449,6 +6041,8 @@ declare interface RestResponse<O> {
5449
6041
  * stays free of the database dep).
5450
6042
  */
5451
6043
  declare interface RestRouteContext {
6044
+ /** Server-owned invocation trace, stable through readmission and projection. */
6045
+ readonly traceId: string;
5452
6046
  readonly subject: Subject;
5453
6047
  readonly headers: Readonly<Record<string, string>>;
5454
6048
  /** Request metadata and the byte-exact body as received by the HTTP
@@ -5473,6 +6067,9 @@ declare interface RestRouteContext {
5473
6067
  }
5474
6068
 
5475
6069
  declare interface RestRouteDescriptor<I, O> {
6070
+ /** Original schemas for client generation. Metadata only: never mounts a
6071
+ * second handler or selects authentication/idempotency bindings. */
6072
+ readonly publicApiDescriptor?: PublicApiDescriptor;
5476
6073
  readonly method: RestMethod;
5477
6074
  /** Absolute path, e.g. `/v1/customers`. Matches that path exactly. */
5478
6075
  readonly path: string;
@@ -5486,6 +6083,12 @@ declare interface RestRouteDescriptor<I, O> {
5486
6083
  * encodes it to JSON for the `200` response. */
5487
6084
  readonly output: Schema.Codec<O, any, never, never>;
5488
6085
  readonly handler: (input: I, ctx: RestRouteContext) => Promise<RestHandlerResult<O>> | RestHandlerResult<O>;
6086
+ /** Pure synchronous normalization of thrown admission/handler failures.
6087
+ * Return a mapped thrown value (for example RestRouteError), not an HTTP
6088
+ * response; undefined keeps the original failure. Available before authentication,
6089
+ * so only request metadata is given.
6090
+ * Public projections supply their existing typed/profile error mapper here. */
6091
+ readonly mapFailure?: (error: unknown, request: Pick<PluginHttpRouteRequest, 'method' | 'path' | 'query'>) => unknown;
5489
6092
  readonly summary?: string;
5490
6093
  readonly description?: string;
5491
6094
  readonly example?: RestRouteExample;
@@ -5595,14 +6198,23 @@ declare interface RestRouteProjection {
5595
6198
  * auth subject + the bound DataStore.
5596
6199
  */
5597
6200
  declare interface RestServeBindings {
6201
+ /** Pure scope-refresher registration, shared with native RPC admission. */
6202
+ readonly bindReplayAuthority?: RpcReplayAuthorityBinding;
6203
+ /** Actual host policy for projected procedure guards; raw routes own guards. */
6204
+ readonly defaultDeny?: boolean;
6205
+ /** Full runtime hosts supply their existing trace generator. */
6206
+ readonly traceId?: () => string;
5598
6207
  /** Bound once from the app's mounted rate-limit plugin. */
5599
6208
  readonly rateLimit?: (policy: RestRateLimit, ctx: RestRouteContext, route: RestRouteDescriptor<any, any>) => Effect.Effect<void, unknown>;
5600
- /** Resolve the calling subject from the request headers. */
5601
- readonly resolveSubject?: (headers: Readonly<Record<string, string>>) => Subject | Promise<Subject>;
6209
+ /** Resolve the calling subject and request-local selection before guards. */
6210
+ readonly resolveSubject?: (headers: Readonly<Record<string, string>>, request: {
6211
+ readonly path: string;
6212
+ readonly query: string;
6213
+ }) => Subject | Promise<Subject>;
5602
6214
  /** The bound framework DataStore. */
5603
6215
  readonly store?: unknown;
5604
6216
  /** Build the full per-request app context after authentication. */
5605
- readonly buildContext?: (subject: Subject, headers: Readonly<Record<string, string>>) => unknown | Promise<unknown>;
6217
+ readonly buildContext?: (subject: Subject, headers: Readonly<Record<string, string>>, request: Pick<RestRouteContext, 'traceId' | 'remoteAddr'>) => unknown | Promise<unknown>;
5606
6218
  /**
5607
6219
  * HTTP idempotency — when set, a mutating request (POST/PUT/PATCH/DELETE)
5608
6220
  * carrying the `header` is deduplicated: the first call runs + caches its
@@ -5772,7 +6384,7 @@ export declare interface RpcInterceptorContext {
5772
6384
  readonly enqueueOutcome?: OutboxEnqueueFacade['enqueue'];
5773
6385
  /** Procedure tag, e.g. `todos.create`. */
5774
6386
  readonly tag: string;
5775
- /** Discriminator — `'mutation' | 'query' | 'action' | 'workflow'`. */
6387
+ /** Procedure kind, including finite stream admission. */
5776
6388
  readonly kind: RpcKind;
5777
6389
  /** The validated input the executor is about to (or already did) receive. */
5778
6390
  readonly input: unknown;
@@ -5808,10 +6420,20 @@ export declare interface RpcInterceptorContext {
5808
6420
  * Which kind of rpc the interceptor is wrapping. The runtime passes
5809
6421
  * `kind: 'mutation'` to interceptors registered as `interceptMutation`,
5810
6422
  * `kind: 'query'` to `interceptQuery`, `kind: 'action'` to
5811
- * `interceptAction`, and `kind: 'workflow'` to `interceptWorkflow` — one
6423
+ * `interceptAction`, `kind: 'workflow'` to `interceptWorkflow`, and
6424
+ * `kind: 'stream'` to `interceptStream` — one
5812
6425
  * plugin can implement any combination.
5813
6426
  */
5814
- export declare type RpcKind = 'mutation' | 'query' | 'action' | 'workflow';
6427
+ export declare type RpcKind = 'mutation' | 'query' | 'action' | 'workflow' | 'stream';
6428
+
6429
+ export declare type RpcReplayAuthorityBinding = (context: RpcReplayAuthorityContext) => undefined;
6430
+
6431
+ /** Pure synchronous authority-refresher binding for RPC and REST admission.
6432
+ * No business work, I/O, grants or side effects belong in this hook. The host
6433
+ * calls the existing guard rechecker after every binding. */
6434
+ export declare interface RpcReplayAuthorityContext extends Pick<RpcInterceptorContext, 'tag' | 'input' | 'subject' | 'traceId' | 'surface' | 'publicApi' | 'remoteAddr'> {
6435
+ readonly kind: RpcKind | 'rest';
6436
+ }
5815
6437
 
5816
6438
  /**
5817
6439
  * Per-firing context passed to schedule-fire interceptors. Shape
@@ -5960,6 +6582,10 @@ declare const ScopeError_base: Schema.Class<ScopeError, Schema.TaggedStruct<"Sco
5960
6582
  * resolver already has. */
5961
6583
  export declare type ScopeResolverResult = ReadonlyArray<string> | ScopeDecision;
5962
6584
 
6585
+ /** Exact tags outrank namespace filters; deeper namespaces outrank broader ones.
6586
+ * Equal specificity is an ambiguity, never an implicit failover/credential hop. */
6587
+ export declare const selectApiRoleEndpoint: (endpoints: ApiRoleEndpoints, tag: string, workload: ApiRoleRpcWorkload) => ApiRoleEndpoint;
6588
+
5963
6589
  /**
5964
6590
  * The subject with `tenantId` selected — or a refusal.
5965
6591
  *
@@ -6196,6 +6822,33 @@ export declare const shapeDigest: (text: string) => string;
6196
6822
  */
6197
6823
  export declare const sourceKeys: (source: ReactivitySourceValue | ReadonlyArray<ReactivitySourceValue> | undefined) => ReadonlyArray<string>;
6198
6824
 
6825
+ export declare interface SsrfPolicy {
6826
+ /**
6827
+ * Hosts that bypass the guard. An entry may be an exact hostname
6828
+ * (`billing.internal`), a suffix wildcard (`*.svc.cluster.local`), or a
6829
+ * host:port (`127.0.0.1:8787`) when only one port should be reachable.
6830
+ *
6831
+ * This is the one escape hatch, and it is deliberately per-HOST rather than a
6832
+ * boolean: "we call one internal service" and "we do not check URLs" are very
6833
+ * different postures, and a boolean cannot tell them apart six months later.
6834
+ */
6835
+ readonly allowHosts?: ReadonlyArray<string>;
6836
+ /** Max redirect hops to follow. Each one is revalidated. Default 5. */
6837
+ readonly maxRedirects?: number;
6838
+ }
6839
+
6840
+ /**
6841
+ * Decide whether a URL may be fetched. Returns `null` when allowed, else the
6842
+ * reason. Fails CLOSED: a URL that will not parse is blocked.
6843
+ *
6844
+ * DNS is deliberately NOT resolved — this is a synchronous check on the literal
6845
+ * target, so a hostname that RESOLVES to a private address (DNS rebinding) is not
6846
+ * caught here. Blocking the direct-IP vector is the high-severity, cheap half;
6847
+ * rebinding needs network-layer egress control and is called out in the docs
6848
+ * rather than silently implied.
6849
+ */
6850
+ export declare const ssrfReason: (rawUrl: string, policy?: SsrfPolicy) => string | null;
6851
+
6199
6852
  export declare type StorageAccessPolicy = typeof StorageAccessPolicySchema.Type;
6200
6853
 
6201
6854
  export declare const StorageAccessPolicySchema: Schema.$Array<Schema.Struct<{
@@ -6374,6 +7027,9 @@ export declare const strictInput: <S extends Schema.Top>(input: S, procedure?: s
6374
7027
 
6375
7028
  export declare const Subject: Schema.Union<readonly [Schema.Struct<{
6376
7029
  readonly type: Schema.Literal<"user">;
7030
+ /** A guest has a stable identity but has not linked a registered account.
7031
+ * This identity mark survives session signing; it never grants authority. */
7032
+ readonly kind: Schema.optional<Schema.Literal<"guest">>;
6377
7033
  readonly id: Schema.String;
6378
7034
  readonly tenantId: Schema.String;
6379
7035
  readonly scopes: Schema.optional<Schema.$Array<Schema.String>>;
@@ -6440,6 +7096,7 @@ export declare const subjectAuditActorId: (subject: Subject) => string | null;
6440
7096
  export declare const SubjectIdentity: Schema.Union<readonly [Schema.Struct<{
6441
7097
  readonly type: Schema.Literal<"user">;
6442
7098
  readonly id: Schema.String;
7099
+ readonly kind: Schema.optional<Schema.Literal<"guest">>;
6443
7100
  readonly tenantId: Schema.String;
6444
7101
  readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
6445
7102
  }>, Schema.Struct<{
@@ -6482,9 +7139,9 @@ export declare const SubjectIdentity: Schema.Union<readonly [Schema.Struct<{
6482
7139
  }>, Schema.Struct<{
6483
7140
  readonly type: Schema.Literal<"service">;
6484
7141
  readonly id: Schema.String;
7142
+ readonly service: Schema.String;
6485
7143
  readonly tenantId: Schema.Null;
6486
7144
  readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
6487
- readonly service: Schema.String;
6488
7145
  }>]>;
6489
7146
 
6490
7147
  export declare type SubjectIdentity = typeof SubjectIdentity.Type;
@@ -6523,6 +7180,7 @@ declare const SubjectService_base: Context.ServiceClass<SubjectService, "@voltro
6523
7180
  readonly type: "user";
6524
7181
  readonly id: string;
6525
7182
  readonly tenantId: string;
7183
+ readonly kind?: "guest" | undefined;
6526
7184
  readonly scopes?: readonly string[] | undefined;
6527
7185
  readonly metadata?: {
6528
7186
  readonly [x: string]: unknown;
@@ -6560,8 +7218,8 @@ declare const SubjectService_base: Context.ServiceClass<SubjectService, "@voltro
6560
7218
  } | {
6561
7219
  readonly type: "service";
6562
7220
  readonly id: string;
6563
- readonly tenantId: null;
6564
7221
  readonly service: string;
7222
+ readonly tenantId: null;
6565
7223
  readonly scopes?: readonly string[] | undefined;
6566
7224
  readonly metadata?: {
6567
7225
  readonly [x: string]: unknown;
@@ -7054,6 +7712,26 @@ export declare interface UpdateTarget<Input = unknown, Row = unknown, Item = Rec
7054
7712
  readonly shapeItem?: ((input: Input, current: Item) => Item) | undefined;
7055
7713
  }
7056
7714
 
7715
+ /** Runtime form of the registered-human identity requirement. */
7716
+ export declare interface UpgradedCheckSpec {
7717
+ readonly upgraded: true;
7718
+ }
7719
+
7720
+ /** Reject an upgraded marker combined with another guard variant in one entry.
7721
+ * Structural guard unions allow such objects to compile. Keep independent
7722
+ * requirements in separate entries so dispatch and persistence retain both. */
7723
+ export declare const upgradedGuardShapeError: (guard: unknown) => ScopeError | null;
7724
+
7725
+ /** Require a registered human identity. Guest and machine credentials cannot
7726
+ * satisfy this requirement, including credentials holding admin scopes. */
7727
+ export declare interface UpgradedGuardSpec {
7728
+ readonly upgraded: true;
7729
+ }
7730
+
7731
+ /** Validate an optional per-connection SSE lifetime before acquiring resources.
7732
+ * The timer ceiling also prevents millisecond overflow becoming a 1ms timer. */
7733
+ export declare const validateHttpStreamMaxDuration: (value: unknown) => number | undefined;
7734
+
7057
7735
  /** Refuse malformed runtime contributions before starting a worker. */
7058
7736
  export declare function validateOutboxHandlerDefinition(value: unknown, owner: string): asserts value is OutboxHandlerDefinition<unknown>;
7059
7737
 
@@ -7100,6 +7778,26 @@ export declare const ValidationIssue: Schema.Struct<{
7100
7778
  export declare type ValidationIssueShape = Schema.Schema.Type<typeof ValidationIssue>;
7101
7779
 
7102
7780
  export declare interface VoltroPlugin {
7781
+ /** Server callbacks for explicitly policy-bound event client streams. */
7782
+ readonly eventClientPolicies?: ReadonlyArray<EventClientPolicy>;
7783
+ /** Declared background/listener work. Omitted means rpc-only for role admission.
7784
+ * Metadata alone does not suppress hooks; hosts must apply the role plan. */
7785
+ readonly workloads?: ReadonlyArray<ApiWorkload>;
7786
+ /** Separately owned API work, started after shared activation. A failed start
7787
+ * must release partial acquisitions before rejecting. Returned resources drain
7788
+ * before shared deactivation; stop failures retain them for retry. */
7789
+ readonly workloadHooks?: Readonly<Partial<Record<ApiWorkload, PluginWorkloadHook>>>;
7790
+ /** App-supplied connection references for this configured plugin instance.
7791
+ * Registry bindings override these selections. Library plugins must not set
7792
+ * topology defaults here; populate only from explicit app options. */
7793
+ readonly connectionBindings?: Readonly<Record<string, ConnectionRef>>;
7794
+ /** Backend-independent requirements. Cross-node HotStore requires Redis; broker roles require their native transport. */
7795
+ readonly needs?: {
7796
+ readonly hot?: 'local' | 'crossNode';
7797
+ readonly connections?: ReadonlyArray<PluginConnectionNeed>;
7798
+ readonly pubsub?: boolean;
7799
+ readonly atomicMulti?: boolean;
7800
+ };
7103
7801
  /**
7104
7802
  * Plugin identifier — printed in `voltro dev` boot logs + surfaced in
7105
7803
  * the inspect manifest. Use a stable string scoped to the package
@@ -7198,6 +7896,16 @@ export declare interface VoltroPlugin {
7198
7896
  readonly declaredEnv?: ReadonlyArray<PluginEnvVar>;
7199
7897
  /** What this plugin collects and uses — see `PluginPrivacyDeclaration`. */
7200
7898
  readonly privacy?: PluginPrivacyDeclaration;
7899
+ /** Subject-data ownership; distinct from the platform privacy disclosure above. */
7900
+ readonly subjectPrivacy?: PluginSubjectPrivacy;
7901
+ /** Idempotent server hook. Identity hooks run after all declared data hooks. */
7902
+ readonly onErase?: PluginEraseHook;
7903
+ /** Server-authorized JSON export. Omit credentials, hashes and session tokens. */
7904
+ readonly onExport?: PluginExportHook;
7905
+ /** Transactional account-data reconciliation; distinct from privacy erasure. */
7906
+ readonly subjectMerge?: PluginSubjectMerge;
7907
+ /** Trusted coordinator must authenticate both accounts and confirmation. */
7908
+ readonly onMergeAccounts?: PluginAccountMergeHook;
7201
7909
  /** Where this plugin sends data outside the installation — see `PluginEgressDeclaration`. Omit when it sends nothing. */
7202
7910
  readonly egress?: ReadonlyArray<PluginEgressDeclaration>;
7203
7911
  /**
@@ -7239,6 +7947,10 @@ export declare interface VoltroPlugin {
7239
7947
  * Absent → the plugin contributes nothing at the mutation boundary
7240
7948
  * (schema-only / lifecycle-only plugins are fine).
7241
7949
  */
7950
+ /** Register pure authority refreshers for native and REST admission,
7951
+ * including queries and raw REST routes, before/after receipt waits. Requires rpc:authority:replay; never run the executor
7952
+ * or normal interceptors to reconstruct an authorization decision. */
7953
+ readonly bindReplayAuthority?: RpcReplayAuthorityBinding;
7242
7954
  readonly interceptMutation?: RpcInterceptor;
7243
7955
  /** Runs after successful executor and business-rule evaluation, before the
7244
7956
  * transaction commits. Use only the supplied store for required records or
@@ -7280,6 +7992,11 @@ export declare interface VoltroPlugin {
7280
7992
  * plugins carry a parity test that fails when one does.
7281
7993
  */
7282
7994
  readonly interceptWorkflow?: RpcInterceptor;
7995
+ /** Wraps finite stream admission and construction, once per subscription,
7996
+ * before guards and executor side effects. The successful value is the
7997
+ * Stream itself; element delivery retains ordinary live guard rechecks.
7998
+ * Requires rpc:intercept:stream. */
7999
+ readonly interceptStream?: RpcInterceptor;
7283
8000
  /**
7284
8001
  * Effect Layer that contributes services into the per-request
7285
8002
  * context. Anything declared here is yieldable from any handler in
@@ -7460,7 +8177,7 @@ export declare interface VoltroPlugin {
7460
8177
  */
7461
8178
  readonly extendSchema?: PluginSchemaContribution;
7462
8179
  /**
7463
- * A directory of APP-SHAPED primitives this plugin brings with it — the same
8180
+ * An absolute directory or named directories of APP-SHAPED primitives this plugin brings with it — the same
7464
8181
  * file conventions an app uses (`*.query.ts` + `.server.ts`, `*.mutation.ts`,
7465
8182
  * `*.workflow.tsx`, `*.cron.tsx`, `*.subscribe.ts`, `*.reaction.ts`,
7466
8183
  * `schema.ts` / `*.entity.ts`, …).
@@ -7479,9 +8196,12 @@ export declare interface VoltroPlugin {
7479
8196
  *
7480
8197
  * The block's procedures keep the names their descriptors declare; a name
7481
8198
  * that collides with the app's own is refused at codegen, as any collision is.
7482
- * An absolute path to a directory; a missing one refuses the boot.
8199
+ * A directory or a plain named map of directories. Named roots use stable
8200
+ * per-plugin module identities independent of declaration order; choose names
8201
+ * explicitly when optional subsystems contribute separate folders. Each value
8202
+ * must be an absolute existing directory; missing/overlapping roots refuse boot.
7483
8203
  */
7484
- readonly sources?: string;
8204
+ readonly sources?: string | Readonly<Record<string, string>>;
7485
8205
  /**
7486
8206
  * Cross-cutting error schemas merged into every procedure's wire error
7487
8207
  * union. Lets a plugin interceptor fail with a typed error (e.g.
@@ -7620,15 +8340,17 @@ export declare interface WebSocketGatewayConnection {
7620
8340
  readonly send: (data: string | Uint8Array) => void;
7621
8341
  /** Await the local transport write, NOT peer receipt. Rejects on close or
7622
8342
  * overflow. Await each send to propagate backpressure to the producer. */
7623
- readonly sendAsync: (data: string | Uint8Array) => Promise<void>;
8343
+ readonly sendAsync: (data: string | Uint8Array) => Promise<WebSocketGatewaySendResult>;
8344
+ /** Active plus queued frame bytes, excluding WebSocket framing overhead. */
8345
+ readonly queuedBytes: number;
7624
8346
  /** Aborted on close, expiry, shutdown or setup failure, including while
7625
8347
  * onConnection is still awaiting an upstream connection. */
7626
8348
  readonly signal: AbortSignal;
7627
8349
  /** Close the connection (application close codes 4000-4999 are yours). */
7628
8350
  readonly close: (code?: number, reason?: string) => void;
7629
8351
  /** Register a message listener (binary-safe; text arrives as bytes). */
7630
- readonly onMessage: (listener: (data: Uint8Array) => void) => void;
7631
- /** The authenticated subject — `null` only on an `auth: 'public'` route. */
8352
+ readonly onMessage: (listener: (data: Uint8Array, receivedAt: number) => void) => void;
8353
+ /** The verified subject — `null` only on an `auth: 'public'` route. */
7632
8354
  readonly subject: Subject | null;
7633
8355
  /** Lowercased request headers of the upgrade. */
7634
8356
  readonly headers: Readonly<Record<string, string>>;
@@ -7649,11 +8371,27 @@ export declare interface WebSocketGatewayRoute {
7649
8371
  * expires.
7650
8372
  * - `'public'` — deliberately unauthenticated (a device fleet with its own
7651
8373
  * protocol-level auth). A decision somebody wrote down, not a default.
7652
- */
7653
- readonly auth: 'subject' | 'public';
8374
+ * - `'token'` — upgrades after the origin check, then verifies the first
8375
+ * assembled frame before starting onConnection. The credential frame
8376
+ * never reaches message listeners. Clients wait for an application
8377
+ * acknowledgement before sending more frames; early frames close 1008.
8378
+ */
8379
+ readonly auth: 'subject' | 'public' | 'token';
8380
+ /** Required only for auth: 'token'. Return null to refuse (1008), or the
8381
+ * verified SubjectResolution (credentialExpiresAt is Unix SECONDS).
8382
+ * Throw/rejection closes 1011 with a generic reason. Respect context.signal:
8383
+ * timeout, disconnect and shutdown abort it, and late results are ignored. */
8384
+ readonly verify?: (firstFrame: Uint8Array, context: WebSocketGatewayVerificationContext) => SubjectResolution | null | Promise<SubjectResolution | null>;
8385
+ /** Only for auth: 'token'. Deadline from socket open, including waiting for
8386
+ * the first frame and verification; default 5000 ms, maximum 2147483647. */
8387
+ readonly verificationTimeoutMs?: number;
7654
8388
  /** Per-connection bounds. Inbound size is checked before app dispatch,
7655
8389
  * not before the platform assembles a WebSocket message. */
7656
8390
  readonly limits?: {
8391
+ /** Optional per-connection token refill rate. Omitted means no rate limit. */
8392
+ readonly maxInboundMessagesPerSecond?: number;
8393
+ /** Initial and maximum token capacity; defaults to the configured rate. */
8394
+ readonly burst?: number;
7657
8395
  /** Maximum inbound/outbound frame bytes; default 1 MiB. */
7658
8396
  readonly maxMessageBytes?: number;
7659
8397
  /** Active plus queued outbound bytes; default 4 MiB. */
@@ -7672,6 +8410,20 @@ export declare interface WebSocketGatewayRoute {
7672
8410
  readonly onConnection: (connection: WebSocketGatewayConnection) => void | (() => void) | Promise<void | (() => void)>;
7673
8411
  }
7674
8412
 
8413
+ /** Local queue pressure after the frame's transport write; not peer receipt. */
8414
+ export declare interface WebSocketGatewaySendResult {
8415
+ readonly queuedBytes: number;
8416
+ }
8417
+
8418
+ /** Cancellation and upgrade metadata for first-frame credential verification. */
8419
+ export declare interface WebSocketGatewayVerificationContext {
8420
+ readonly signal: AbortSignal;
8421
+ readonly headers: Readonly<Record<string, string>>;
8422
+ readonly path: string;
8423
+ /** Process-monotonic reception time in milliseconds, not a Unix timestamp. */
8424
+ readonly receivedAt: number;
8425
+ }
8426
+
7675
8427
  /**
7676
8428
  * Who may call a procedure over the wire: the app's own clients, or only other
7677
8429
  * apps of the project.