@voltro/runtime 0.86.0 → 0.87.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 (70) hide show
  1. package/CHANGELOG.md +5321 -0
  2. package/README.md +43 -0
  3. package/THIRD-PARTY-NOTICES.md +860 -221
  4. package/dist/analyticsSink.js +8 -14
  5. package/dist/apiKeys.js +82 -78
  6. package/dist/apiRoleBucketSampler.js +64 -0
  7. package/dist/apiRoleWorkloadHistory.js +56 -0
  8. package/dist/apiRoleWorkloadMetrics.js +78 -0
  9. package/dist/apiRoleWorkloadStorage.js +95 -0
  10. package/dist/approvals.js +67 -74
  11. package/dist/authMiddlewareLayer.js +7 -6
  12. package/dist/bindEvent.js +48 -28
  13. package/dist/bootListener.js +106 -39
  14. package/dist/bootStoreCodec.js +38 -37
  15. package/dist/circuitBreaker.js +1 -72
  16. package/dist/composeAnalytics.js +31 -10
  17. package/dist/computedQueryCache.js +231 -0
  18. package/dist/connectionsFacade.js +32 -30
  19. package/dist/coordinatedSchedule.js +84 -78
  20. package/dist/dataStore.js +154 -121
  21. package/dist/declaredTablesGuard.js +2 -1
  22. package/dist/dispatcher.js +403 -328
  23. package/dist/effectStore.js +2 -2
  24. package/dist/engineClients.js +24 -0
  25. package/dist/externalizedTasks.js +19 -6
  26. package/dist/fieldCipher.js +109 -40
  27. package/dist/fieldKeyRegistry.js +295 -0
  28. package/dist/fieldKeyRekey.js +286 -0
  29. package/dist/fieldKeyScan.js +136 -0
  30. package/dist/fieldKeyring.js +77 -0
  31. package/dist/hot.d.ts +56 -0
  32. package/dist/hot.js +3 -0
  33. package/dist/hotDataStore.js +148 -0
  34. package/dist/hotDataStoreSnapshot.js +98 -0
  35. package/dist/hotDataStoreTables.js +18 -0
  36. package/dist/http-range.d.ts +53 -0
  37. package/dist/http-range.js +52 -0
  38. package/dist/httpClientSsrf.js +12 -37
  39. package/dist/httpClientTracing.js +12 -9
  40. package/dist/httpCompression.js +17 -17
  41. package/dist/httpInputStream.js +82 -0
  42. package/dist/httpRouteMounts.js +1 -0
  43. package/dist/idempotencyStore.js +92 -52
  44. package/dist/index.d.ts +1164 -451
  45. package/dist/index.js +143 -132
  46. package/dist/kvFacade.js +23 -17
  47. package/dist/memoryQueryJoins.js +16 -16
  48. package/dist/memorySetOperations.js +43 -0
  49. package/dist/metrics.js +38 -24
  50. package/dist/outbox.js +1 -1
  51. package/dist/readRecorder.js +35 -34
  52. package/dist/readScopePredicate.js +12 -0
  53. package/dist/replicatedDataStore.js +22 -4
  54. package/dist/rowFilter.js +56 -39
  55. package/dist/rpcConnections.js +8 -8
  56. package/dist/rpcServer.js +522 -449
  57. package/dist/rpcWorkloadAdmission.js +50 -0
  58. package/dist/rywPositionStore.js +82 -47
  59. package/dist/scheduler.js +6 -1
  60. package/dist/servePipeline.js +215 -146
  61. package/dist/setReadScopes.js +28 -0
  62. package/dist/snapshotValueCodec.js +46 -0
  63. package/dist/sourceResolution.js +34 -24
  64. package/dist/storeMiddleware.js +211 -209
  65. package/dist/subscriber.js +1 -0
  66. package/dist/tenantScope.js +8 -20
  67. package/dist/traceparent.js +8 -19
  68. package/dist/webSocketGatewayRuntime.js +111 -71
  69. package/dist/workflowRuntime.js +9 -9
  70. package/package.json +19 -7
package/dist/index.d.ts CHANGED
@@ -1,11 +1,31 @@
1
+ import { AggregateQuery } from '@voltro/kv/analytics';
2
+ import { AnalyticsBucket } from '@voltro/kv/analytics';
3
+ import { AnalyticsCapabilityNotSupported } from '@voltro/kv/analytics';
4
+ import { AnalyticsError } from '@voltro/kv/analytics';
5
+ import { AnalyticsEvent } from '@voltro/kv/analytics';
6
+ import { AnalyticsFailure } from '@voltro/kv/analytics';
7
+ import { AnalyticsMetric } from '@voltro/kv/analytics';
8
+ import { AnalyticsMirrorImpl } from '@voltro/kv/analytics';
9
+ import { AnalyticsMirrorRemove } from '@voltro/kv/analytics';
10
+ import { AnalyticsMirrorUpsert } from '@voltro/kv/analytics';
11
+ import { AnalyticsSinkImpl } from '@voltro/kv/analytics';
12
+ import { AnalyticsSinkInfo } from '@voltro/kv/analytics';
13
+ import { AnalyticsTopEntry } from '@voltro/kv/analytics';
1
14
  import { AnyApprovalPolicy } from '@voltro/protocol';
2
15
  import { AnyCheckSpec } from '@voltro/protocol';
3
16
  import { AnyGuardSpec } from '@voltro/protocol';
17
+ import { ApiRoleWindowAssessment } from '@voltro/protocol';
18
+ import { ApiWorkload } from '@voltro/protocol';
4
19
  import { AuthMiddleware } from '@voltro/protocol';
5
20
  import { AuthStrategy } from '@voltro/protocol';
21
+ import { canonicalJson } from '@voltro/protocol';
6
22
  import { CaughtUpVerdict } from '@voltro/database';
7
23
  import { Cause } from 'effect';
8
24
  import { ChangeEvent } from '@voltro/database';
25
+ import { CircuitBreaker } from '@voltro/shared';
26
+ import { CircuitBreakerOptions } from '@voltro/shared';
27
+ import { CircuitOpen } from '@voltro/shared';
28
+ import { CircuitState } from '@voltro/shared';
9
29
  import { classifyConstraintViolation } from '@voltro/database';
10
30
  import { clearRetentions } from '@voltro/database';
11
31
  import { ColumnBuilder } from '@voltro/database';
@@ -28,9 +48,9 @@ import { DeclaredAccess } from '@voltro/protocol';
28
48
  import { DialectId } from '@voltro/database';
29
49
  import { DialectReplicationAdapter } from '@voltro/database';
30
50
  import { Duplex } from 'node:stream';
31
- import { Duration } from 'effect';
32
51
  import { Effect } from 'effect';
33
52
  import { EnqueueOptions } from '@voltro/protocol';
53
+ import { EventClientPolicy } from '@voltro/protocol';
34
54
  import { EventDeliverySemantics } from '@voltro/protocol';
35
55
  import { EventDescriptor } from '@voltro/protocol';
36
56
  import { EventKeyInvalid } from '@voltro/protocol';
@@ -55,6 +75,7 @@ import { InferRow } from '@voltro/database';
55
75
  import { InMemoryMetricExporter } from '@opentelemetry/sdk-metrics';
56
76
  import { InMemorySpanExporter } from '@opentelemetry/sdk-trace-base';
57
77
  import { InsertIgnoreResult } from '@voltro/database';
78
+ import { InsertManyOptions } from '@voltro/database';
58
79
  import { InspectedStep } from '@voltro/workflow';
59
80
  import { InspectedWorkflow } from '@voltro/workflow';
60
81
  import { InspectStore } from '@voltro/workflow';
@@ -62,6 +83,7 @@ import { inspectWorkflow } from '@voltro/workflow';
62
83
  import { isForeignKeyViolation } from '@voltro/database';
63
84
  import { isQueryTimeout } from '@voltro/database';
64
85
  import { Kv } from '@voltro/kv';
86
+ import { KvError } from '@voltro/kv';
65
87
  import { KvStoreShape } from '@voltro/kv';
66
88
  import { Layer } from 'effect';
67
89
  import { listRetentionConflicts } from '@voltro/database';
@@ -69,9 +91,12 @@ import { listRetentions } from '@voltro/database';
69
91
  import { Membership } from '@voltro/protocol';
70
92
  import { Metric } from 'effect';
71
93
  import { MetricReader } from '@opentelemetry/sdk-metrics';
94
+ import { MirrorVersion } from '@voltro/kv/analytics';
72
95
  import { MutationIdempotencyScope } from '@voltro/protocol';
96
+ import { newTraceId } from '@voltro/shared';
73
97
  import { NoRowFound } from '@voltro/protocol';
74
98
  import { NoSuchResource } from '@voltro/protocol';
99
+ import { Option } from 'effect';
75
100
  import { OutboxEnqueueFacade } from '@voltro/protocol';
76
101
  import { OutboxHandlerContext } from '@voltro/protocol';
77
102
  import { OutboxHandlerDefinition } from '@voltro/protocol';
@@ -81,13 +106,16 @@ import { PluginHttpRouteByteStream } from '@voltro/protocol';
81
106
  import { PluginRefOrphanPolicy } from '@voltro/database';
82
107
  import { Predicate } from '@voltro/database';
83
108
  import { ProcedureWireError } from '@voltro/protocol';
109
+ import { PropertyFilter } from '@voltro/kv/analytics';
84
110
  import { PublicApiSpec } from '@voltro/protocol';
85
111
  import { Query } from '@voltro/database';
86
112
  import { QueryDescriptor } from '@voltro/database';
87
- import { Redis } from 'ioredis';
113
+ import { RedisPubSub } from '@voltro/kv/connection';
88
114
  import { registerRetention } from '@voltro/database';
89
115
  import { RequestedTenantRefused } from '@voltro/protocol';
90
116
  import { ResourceMissing } from '@voltro/protocol';
117
+ import { RespClient } from '@voltro/kv/connection';
118
+ import { RespConnections } from '@voltro/kv/connection';
91
119
  import { RestRouteDescriptor } from '@voltro/protocol/rest';
92
120
  import { RetentionConflict } from '@voltro/database';
93
121
  import { RetentionSource } from '@voltro/database';
@@ -99,6 +127,7 @@ import { RowPatch } from '@voltro/protocol';
99
127
  import { Rpc } from 'effect/rpc';
100
128
  import { RpcGroup } from 'effect/rpc';
101
129
  import { RpcInterceptor } from '@voltro/protocol';
130
+ import { RpcReplayAuthorityBinding } from '@voltro/protocol';
102
131
  import { RuleReadStore } from '@voltro/database';
103
132
  import { Sampler } from '@opentelemetry/sdk-trace-base';
104
133
  import { Schedule } from 'effect';
@@ -110,6 +139,9 @@ import { ServerResponse } from 'node:http';
110
139
  import { SpanProcessor } from '@opentelemetry/sdk-trace-base';
111
140
  import { spawn } from 'node:child_process';
112
141
  import { SqlClient } from 'effect/sql';
142
+ import { SqlError } from 'effect/sql/SqlError';
143
+ import { SsrfPolicy } from '@voltro/protocol';
144
+ import { ssrfReason } from '@voltro/protocol';
113
145
  import { StoredTableShape } from '@voltro/database';
114
146
  import { StoreError } from '@voltro/protocol';
115
147
  import { StoreOperationFailed } from '@voltro/protocol';
@@ -130,6 +162,10 @@ import { TenantRowNotFound } from '@voltro/protocol';
130
162
  import { TenantScopeViolation } from '@voltro/protocol';
131
163
  import { tenantSelectionId } from '@voltro/database';
132
164
  import { TenantSelectionStore } from '@voltro/protocol';
165
+ import { TimeBucket } from '@voltro/kv/analytics';
166
+ import { TimeRange } from '@voltro/kv/analytics';
167
+ import { TimeseriesQuery } from '@voltro/kv/analytics';
168
+ import { TopNQuery } from '@voltro/kv/analytics';
133
169
  import { Tracer } from 'effect';
134
170
  import { Unauthenticated } from '@voltro/protocol';
135
171
  import { UpsertOptions } from '@voltro/database';
@@ -172,6 +208,8 @@ declare const AccessDenied_base: Schema.Class<AccessDenied, Schema.TaggedStruct<
172
208
  export declare const actingUserId: (subject: StoreMiddlewareContext["subject"]) => string | null;
173
209
 
174
210
  export declare interface ActionRunnerDeps {
211
+ /** Measures only the synchronous executor entry, never an awaited result. */
212
+ readonly measureSync?: <A>(run: () => A) => A;
175
213
  readonly outcomeOutbox?: MutationRunnerDeps['outcomeOutbox'];
176
214
  /** Build the per-call `AppContext` — NO transactional tx (actions do
177
215
  * external I/O and run outside a txn deliberately). */
@@ -183,8 +221,23 @@ export declare interface ActionRunnerDeps {
183
221
  readonly recordMetric?: MutationRunnerDeps['recordMetric'];
184
222
  /** See {@link MutationRunnerDeps.defaultDeny} — same flag, same refusal. */
185
223
  readonly defaultDeny?: boolean;
224
+ /** See {@link MutationRunnerDeps.settleCacheInvalidations} — an action's
225
+ * writes are the writer's too. */
226
+ readonly settleCacheInvalidations?: () => Promise<void>;
186
227
  }
187
228
 
229
+ /**
230
+ * Make a key active NOW, without waiting for every live process to hold it —
231
+ * for a key replacing a leaked one, where waiting is the larger risk. The
232
+ * processes that lack it are returned: they cannot read what is sealed under
233
+ * it until they are redeployed with it, and each says so on its heartbeat.
234
+ */
235
+ export declare const activateFieldKeyNow: (store: DataStore, keyId: string, holder?: Pick<FieldKeyHolder, "now">) => Promise<{
236
+ readonly from: string | undefined;
237
+ readonly to: string;
238
+ readonly lacking: ReadonlyArray<string>;
239
+ }>;
240
+
188
241
  /**
189
242
  * Live activity counters the runtime exposes to the idle detector. All
190
243
  * return the CURRENT count (0 = quiescent on that axis).
@@ -455,20 +508,7 @@ declare const AggregateNotFound_base: Schema.Class<AggregateNotFound, Schema.Tag
455
508
  readonly name: Schema.String;
456
509
  }>, YieldableError>;
457
510
 
458
- export declare interface AggregateQuery {
459
- readonly event: string;
460
- readonly filter?: PropertyFilter;
461
- readonly metric: AnalyticsMetric;
462
- readonly range: TimeRange;
463
- /**
464
- * Restrict the read to one tenant's events. `useAnalytics()` injects the
465
- * caller's `subject.tenantId` automatically, so handler code never sets
466
- * this; a sink that honours it (e.g. analytics-postgres) then AND-filters
467
- * by tenant so a query can't read across tenants. `null`/absent = unscoped
468
- * (system/cross-tenant read).
469
- */
470
- readonly tenantId?: string | null;
471
- }
511
+ export { AggregateQuery }
472
512
 
473
513
  export declare interface AggregateReadOptions {
474
514
  /**
@@ -477,11 +517,14 @@ export declare interface AggregateReadOptions {
477
517
  * Without it an aggregate can only ever be "the one global roll-up", so every
478
518
  * tenant-/team-/year-scoped roll-up (most real ones) has to stay client-side.
479
519
  * A row matches when EVERY entry matches: a scalar compares by strict
480
- * equality, an array means "one of" (an IN set).
520
+ * equality, an array means "one of" (an IN set), and an object of `gte` /
521
+ * `gt` / `lte` / `lt` is a range on that column (a `Date` bound compares
522
+ * with `Date` and ISO-string values by instant; `null` never matches).
481
523
  *
482
524
  * ```ts
483
525
  * yield* handle.read({ where: { teamId, year: 2026 } })
484
526
  * yield* handle.read({ where: { status: ['open', 'blocked'] } })
527
+ * yield* handle.read({ where: { teamId, day: { gte: from, lte: to } } })
485
528
  * ```
486
529
  *
487
530
  * Deliberately DATA, not a predicate function: it stays serializable, so the
@@ -557,71 +600,17 @@ declare const AggregateStale_base: Schema.Class<AggregateStale, Schema.TaggedStr
557
600
  readonly maxAgeMs: Schema.Number;
558
601
  }>, YieldableError>;
559
602
 
560
- export declare interface AnalyticsBucket {
561
- readonly bucketStart: Date;
562
- readonly value: number;
563
- }
603
+ export { AnalyticsBucket }
564
604
 
565
- /**
566
- * The sink doesn't implement this capability. PostHog returns this
567
- * for `aggregate`/`timeseries`/`topN`. Callers typically
568
- * `Effect.catchTag('AnalyticsCapabilityNotSupported', ...)` to fall
569
- * back to a different sink or report the missing feature.
570
- */
571
- export declare class AnalyticsCapabilityNotSupported extends AnalyticsCapabilityNotSupported_base {
572
- }
605
+ export { AnalyticsCapabilityNotSupported }
573
606
 
574
- declare const AnalyticsCapabilityNotSupported_base: Schema.Class<AnalyticsCapabilityNotSupported, Schema.TaggedStruct<"AnalyticsCapabilityNotSupported", {
575
- readonly provider: Schema.String;
576
- readonly capability: Schema.String;
577
- }>, YieldableError>;
607
+ export { AnalyticsError }
578
608
 
579
- /**
580
- * The underlying sink raised an error. `cause` is stringified for
581
- * round-trip safety across the rpc boundary.
582
- */
583
- export declare class AnalyticsError extends AnalyticsError_base {
584
- }
609
+ export { AnalyticsEvent }
585
610
 
586
- declare const AnalyticsError_base: Schema.Class<AnalyticsError, Schema.TaggedStruct<"AnalyticsError", {
587
- readonly provider: Schema.String;
588
- readonly operation: Schema.String;
589
- readonly cause: Schema.String;
590
- }>, YieldableError>;
611
+ export { AnalyticsFailure }
591
612
 
592
- /**
593
- * One analytics event. `properties` must be JSON-serialisable —
594
- * individual sinks validate at write time (Postgres stores as JSONB;
595
- * ClickHouse encodes as a JSON string; PostHog forwards verbatim).
596
- */
597
- export declare interface AnalyticsEvent {
598
- /** Event name (e.g. `'match_completed'`, `'page_viewed'`). */
599
- readonly name: string;
600
- /** Subject id (typically `ctx.request.subject.id`) — anonymous when null. */
601
- readonly subjectId?: string | null;
602
- /**
603
- * Owning tenant. Normally you DON'T set this by hand — `useAnalytics()`
604
- * stamps it from the caller's `subject.tenantId` so every event is
605
- * tenant-attributable + every read is tenant-scoped (the multi-tenant
606
- * isolation default). Set it explicitly only for a cross-tenant system
607
- * write. `null` = no tenant (system/anonymous).
608
- */
609
- readonly tenantId?: string | null;
610
- /** Property bag. Values must be JSON-serialisable. */
611
- readonly properties: Readonly<Record<string, unknown>>;
612
- /** When the event occurred. Defaults to server-now when omitted. */
613
- readonly occurredAt?: Date;
614
- }
615
-
616
- export declare type AnalyticsFailure = AnalyticsError | AnalyticsCapabilityNotSupported;
617
-
618
- /** Metric to compute. `count` = row count, `unique` = distinct subjectId,
619
- * `sum` / `avg` = numeric aggregation on a property field. */
620
- export declare type AnalyticsMetric = 'count' | 'unique' | {
621
- readonly sum: string;
622
- } | {
623
- readonly avg: string;
624
- };
613
+ export { AnalyticsMetric }
625
614
 
626
615
  export declare interface AnalyticsMirrorHandle {
627
616
  /** Tables actually being mirrored (the sink's `mirror.tables`). Empty
@@ -643,68 +632,9 @@ export declare interface AnalyticsMirrorHandle {
643
632
  readonly stats: () => AnalyticsMirrorStats;
644
633
  }
645
634
 
646
- /**
647
- * Optional CDC-mirror capability. A sink that declares `mirror` opts in
648
- * to receiving the main DataStore's reactive-table changes, so
649
- * analytical queries can JOIN warehouse events against live user data
650
- * (`SELECT … FROM voltro_events e JOIN users u ON …`).
651
- *
652
- * The framework subscribes to `DataStore.onChange` once at boot, filters
653
- * to the tables the sink lists in `tables`, and routes each change here.
654
- * Inserts/updates `upsert` by primary key; deletes `remove`. Every call
655
- * MUST be idempotent — the same change may be re-delivered after a
656
- * reconnect, and the upsert is keyed on `primaryKey` so re-applying a
657
- * row is a no-op-equivalent overwrite.
658
- *
659
- * Idempotent is not enough on its own: re-delivery can also arrive OUT
660
- * OF ORDER, so both methods additionally receive a {@link MirrorVersion}
661
- * and MUST ignore a write whose version is not greater than the version
662
- * already stored for that key (ClickHouse gets this from
663
- * `ReplacingMergeTree(version)`; DuckDB from an `ON CONFLICT … WHERE
664
- * excluded.version > version` guard).
665
- */
666
- export declare interface AnalyticsMirrorImpl {
667
- /** Reactive tables this sink mirrors. The framework only forwards
668
- * changes whose `table` is in this set. */
669
- readonly tables: ReadonlyArray<string>;
670
- /** Primary-key column the sink upserts/deletes by. Defaults to `'id'`
671
- * when omitted. */
672
- readonly primaryKey?: string;
673
- /** Apply an insert/update. Idempotent upsert keyed on `primaryKey`,
674
- * version-guarded by `write.version`. */
675
- readonly upsert: (write: AnalyticsMirrorUpsert) => Effect.Effect<void, AnalyticsFailure>;
676
- /** Apply a delete. Idempotent — removing an absent key is a no-op —
677
- * and version-guarded by `write.version`. */
678
- readonly remove: (write: AnalyticsMirrorRemove) => Effect.Effect<void, AnalyticsFailure>;
679
- /**
680
- * The highest version this sink has PERSISTED for one key — `null` when
681
- * the key has never been written (a tombstoned key still reports its
682
- * tombstone's version; both warehouse sinks keep tombstones for exactly
683
- * this reason).
684
- *
685
- * This is the fleet-scope seed read: a replica that joins mid-stream has
686
- * no per-key history of its own, so before its first write to a key it
687
- * asks the warehouse where the fleet already is and continues from
688
- * there. Without it a joiner would restart the key's versions low and
689
- * every write it produced would be silently rejected by the sink's own
690
- * `version >` guard. Called at most once per key per process (off the
691
- * commit path, inside the per-key drain worker), never per change.
692
- */
693
- readonly maxVersion: (input: {
694
- readonly table: string;
695
- readonly primaryKeyValue: unknown;
696
- }) => Effect.Effect<MirrorVersion | null, AnalyticsFailure>;
697
- }
635
+ export { AnalyticsMirrorImpl }
698
636
 
699
- /** One delete to apply to the mirror. */
700
- export declare interface AnalyticsMirrorRemove {
701
- readonly table: string;
702
- /** Primary-key value of the removed row. */
703
- readonly primaryKeyValue: unknown;
704
- /** See {@link MirrorVersion}. A delete is ordered against upserts of
705
- * the same key by exactly this number. */
706
- readonly version: MirrorVersion;
707
- }
637
+ export { AnalyticsMirrorRemove }
708
638
 
709
639
  /** Counters a caller (boot log, inspect endpoint, test) can read. */
710
640
  export declare interface AnalyticsMirrorStats {
@@ -765,15 +695,7 @@ export declare interface AnalyticsMirrorTunables {
765
695
  readonly versionStateLimit: number;
766
696
  }
767
697
 
768
- /** One insert/update to apply to the mirror. */
769
- export declare interface AnalyticsMirrorUpsert {
770
- readonly table: string;
771
- /** Post-image of the changed row. */
772
- readonly row: Readonly<Record<string, unknown>>;
773
- /** See {@link MirrorVersion} — persist it, compare against it, never
774
- * replace it with a local clock reading. */
775
- readonly version: MirrorVersion;
776
- }
698
+ export { AnalyticsMirrorUpsert }
777
699
 
778
700
  /**
779
701
  * Context.Tag the framework provides at runtime. Handlers grab it via
@@ -787,50 +709,14 @@ export declare class AnalyticsSink extends AnalyticsSink_base {
787
709
 
788
710
  declare const AnalyticsSink_base: Context.ServiceClass<AnalyticsSink, "@voltro/AnalyticsSink", AnalyticsSinkImpl>;
789
711
 
790
- /**
791
- * The runtime contract every analytics provider implements. Plugins
792
- * build a value of this shape inside their factory function and the
793
- * framework wires it under `AnalyticsSink` for handler code to consume.
794
- */
795
- export declare interface AnalyticsSinkImpl {
796
- readonly info: AnalyticsSinkInfo;
797
- readonly track: (event: AnalyticsEvent) => Effect.Effect<void, AnalyticsFailure>;
798
- readonly aggregate: (query: AggregateQuery) => Effect.Effect<number, AnalyticsFailure>;
799
- readonly timeseries: (query: TimeseriesQuery) => Effect.Effect<ReadonlyArray<AnalyticsBucket>, AnalyticsFailure>;
800
- readonly topN: (query: TopNQuery) => Effect.Effect<ReadonlyArray<AnalyticsTopEntry>, AnalyticsFailure>;
801
- /**
802
- * Present when the sink mirrors reactive tables (opt-in via the
803
- * plugin's `mirrorTables` config). Absent → no mirroring; the
804
- * framework skips wiring `DataStore.onChange` for this sink.
805
- */
806
- readonly mirror?: AnalyticsMirrorImpl;
807
- }
712
+ export { AnalyticsSinkImpl }
808
713
 
809
- /**
810
- * Per-sink metadata surfaced via the manifest endpoint + dashboard.
811
- * Lets the operator see at a glance which provider is wired + what
812
- * it supports without grepping app.config.ts.
813
- */
814
- export declare interface AnalyticsSinkInfo {
815
- readonly provider: string;
816
- readonly capabilities: {
817
- readonly track: boolean;
818
- readonly aggregate: boolean;
819
- readonly timeseries: boolean;
820
- readonly topN: boolean;
821
- /** True when the sink mirrors reactive tables into the warehouse so
822
- * analytical queries can JOIN events against live user data. */
823
- readonly mirror: boolean;
824
- };
825
- /** Optional plugin-supplied detail (`{ ingestBufferDepth: 42 }`,
826
- * `{ url: 'https://eu.posthog.com' }`, etc.) — surfaced verbatim. */
827
- readonly detail?: Readonly<Record<string, unknown>>;
828
- }
714
+ export { AnalyticsSinkInfo }
829
715
 
830
716
  /**
831
717
  * What a plugin factory returns at app.config.ts time. The framework
832
718
  * boot path reads `apiConfig.analytics` (an `AnalyticsSinkSpec`), calls
833
- * `spec.build(sqlClient)` to materialise the `AnalyticsSinkImpl`, and
719
+ * the dependency-specific `spec.build` to materialise the `AnalyticsSinkImpl`, and
834
720
  * provides it under the `AnalyticsSink` Tag.
835
721
  *
836
722
  * Specs can declare `tables` to merge into the framework's schema
@@ -839,16 +725,10 @@ export declare interface AnalyticsSinkInfo {
839
725
  * sink is wired but BEFORE rpc routes open (handshakes, dependency
840
726
  * pings, batch-buffer warmups).
841
727
  */
842
- export declare interface AnalyticsSinkSpec {
728
+ declare interface AnalyticsSinkLifecycle {
843
729
  /** Discriminator surfaced in boot logs + inspect ('postgres-analytics',
844
730
  * 'duckdb', 'clickhouse', 'tinybird', 'posthog', 'compose', 'noop'). */
845
731
  readonly kind: string;
846
- /**
847
- * Materialise the sink at boot. Receives the framework's SqlClient
848
- * (bound to the main DataStore by default). Plugins that don't need
849
- * SqlClient ignore the argument.
850
- */
851
- readonly build: (sqlClient: SqlClient.SqlClient) => AnalyticsSinkImpl;
852
732
  /** Optional schema tables the framework should create on first boot.
853
733
  * Erased to `unknown` — the framework only forwards them to the
854
734
  * schema applier, which accepts opaque table descriptors. */
@@ -871,10 +751,16 @@ export declare interface AnalyticsSinkSpec {
871
751
  readonly dispose?: (impl: AnalyticsSinkImpl) => Effect.Effect<void, never>;
872
752
  }
873
753
 
874
- export declare interface AnalyticsTopEntry {
875
- readonly key: string;
876
- readonly value: number;
877
- }
754
+ /** Explicit SQL dependency: independent sinks boot without an application SQL store. */
755
+ export declare type AnalyticsSinkSpec = AnalyticsSinkLifecycle & ({
756
+ readonly sql: 'required';
757
+ readonly build: (sqlClient: SqlClient.SqlClient) => AnalyticsSinkImpl;
758
+ } | {
759
+ readonly sql: 'none';
760
+ readonly build: () => AnalyticsSinkImpl;
761
+ });
762
+
763
+ export { AnalyticsTopEntry }
878
764
 
879
765
  /**
880
766
  * Answer one probe.
@@ -914,6 +800,12 @@ export declare const API_CSP = "default-src 'none'; frame-ancestors 'none'; base
914
800
 
915
801
  export declare const API_KEYS_TABLE = "_voltro_api_keys";
916
802
 
803
+ export declare const API_ROLE_BUCKET_MS: number;
804
+
805
+ export declare const API_ROLE_WINDOW_MS: number;
806
+
807
+ export declare const API_ROLE_WORKLOAD_BUCKETS_TABLE = "_voltro_api_role_workload_buckets";
808
+
917
809
  /** Caller side: the token this app presents to the api it binds as `name`. */
918
810
  export declare const apiBindingTokenEnv: (name: string) => string;
919
811
 
@@ -1097,6 +989,29 @@ export declare interface ApiKeyUsageDelta {
1097
989
  readonly lastUsedAt: number;
1098
990
  }
1099
991
 
992
+ /** A process can select multiple roles. Attribute once to the canonical role set,
993
+ * never once per role (which would multiply the observed loop time). */
994
+ export declare const apiRoleMetricLabel: (roles: ReadonlyArray<string>) => string;
995
+
996
+ export { ApiRoleWindowAssessment }
997
+
998
+ /** Persist one finalized five-minute interval per process incarnation. Seconds
999
+ * are exclusive active-loop increments, NOT cumulative counter snapshots. */
1000
+ export declare interface ApiRoleWorkloadBucket {
1001
+ readonly replicaId: string;
1002
+ readonly startedAt: number;
1003
+ readonly role: string;
1004
+ readonly from: number;
1005
+ readonly until: number;
1006
+ /** Only actual continuous observation can set this true. */
1007
+ readonly continuous: boolean;
1008
+ /** Each required workload must have explicit complete coverage and a value. */
1009
+ readonly workloads: Readonly<Partial<Record<ApiWorkload, {
1010
+ readonly coverage: 'partial' | 'complete';
1011
+ readonly seconds: number;
1012
+ }>>>;
1013
+ }
1014
+
1100
1015
  /**
1101
1016
  * Typed authorization slice on `ctx.access` — the ergonomic, cast-free face of
1102
1017
  * the caller's EFFECTIVE scope set (raw subject scopes ∪ rbac role-derived
@@ -1510,6 +1425,18 @@ export declare const assertWebhookRoutesDeclareVerification: (routes: ReadonlyMa
1510
1425
  readonly handle: unknown;
1511
1426
  }> | undefined) => void;
1512
1427
 
1428
+ /** Last 24 COMPLETED hours in five-minute buckets (at most five minutes behind
1429
+ * now). Never interpolate a partial boundary bucket or merge process restarts.
1430
+ * Doctor and panels consume this same decision; unknown work is never zero. */
1431
+ export declare const assessApiRoleWorkloadWindow: (input: {
1432
+ readonly now: number;
1433
+ readonly replicaId: string;
1434
+ readonly startedAt: number;
1435
+ readonly role: string;
1436
+ readonly requiredWorkloads: ReadonlyArray<ApiWorkload>;
1437
+ readonly buckets: ReadonlyArray<ApiRoleWorkloadBucket>;
1438
+ }) => ApiRoleWindowAssessment;
1439
+
1513
1440
  /**
1514
1441
  * Assign a subject to a variant — STABLE (same subject ⇒ same variant, always)
1515
1442
  * and BALANCED (proportions converge to the weights over many subjects). The
@@ -1820,9 +1747,12 @@ export declare interface BindEventInput {
1820
1747
  * this bridge must treat it as "no check", not as a guard.
1821
1748
  */
1822
1749
  readonly guards?: DeclaredAccess<unknown> | undefined;
1750
+ /** The descriptor requirement and captured app policy must match exactly. */
1751
+ readonly requiredClientPolicy?: string;
1752
+ readonly clientPolicy?: EventClientPolicy;
1823
1753
  }
1824
1754
 
1825
- export declare const bindMutation: <Input, Output, E = never>(execute: (input: Input, context: RuntimeContext) => Output | Promise<Output> | Effect.Effect<Output, E, never>, input: Input, spanName?: string, codec?: MutationOutputCodec<unknown>, idempotency?: IdempotencyBinding,
1755
+ export declare const bindMutation: <Input, Output, E = never>(execute: (input: Input, context: RuntimeContext) => Output | Promise<Output> | Effect.Effect<Output, E, never>, input: Input, spanName?: string, codec?: MutationCodec<unknown>, idempotency?: IdempotencyBinding,
1826
1756
  /**
1827
1757
  * This procedure's DECLARED error union (`descriptor.error`).
1828
1758
  *
@@ -1874,7 +1804,12 @@ guards?: ReadonlyArray<unknown> | undefined,
1874
1804
  * (`{ scope: 'x:read', from: 'id' }`) can see which resource was asked for.
1875
1805
  * Passed explicitly rather than read off RuntimeContext, which does not
1876
1806
  * carry it. */
1877
- guardInput?: unknown) => Stream.Stream<Element, Err, SubjectService | ConnectionInfo>;
1807
+ guardInput?: unknown,
1808
+ /** Plugin admission wraps construction once; element guards remain live. */
1809
+ admission?: {
1810
+ readonly tag: string;
1811
+ readonly interceptor?: ServeRpcInterceptor | undefined;
1812
+ }) => Stream.Stream<Element, Err, SubjectService | ConnectionInfo>;
1878
1813
 
1879
1814
  /**
1880
1815
  * Build a `Stream<SubscriptionEvent<T>>` that an effect/rpc streaming
@@ -1961,6 +1896,16 @@ export declare interface BootListener {
1961
1896
  * Installed by the boot path when its store exists, which is after the
1962
1897
  * bind; an app-declared `health.readiness` replaces the whole check. */
1963
1898
  readonly setDependencyPing: (ping: () => Promise<boolean>) => void;
1899
+ /** Required local runtime conditions, independent of app dependency policy.
1900
+ * Checks must be synchronous and cheap. Release removes only this registration. */
1901
+ /** Enable structured API health using captured role/workload identities. */
1902
+ readonly setRoleHealth: (identity: {
1903
+ readonly roles: ReadonlyArray<string>;
1904
+ readonly workloads: ReadonlyArray<string>;
1905
+ }) => void;
1906
+ readonly addReadinessGate: (name: string, check: () => boolean) => () => void;
1907
+ /** Required local progress conditions. App liveness cannot bypass these gates. */
1908
+ readonly addLivenessGate: (name: string, check: () => boolean) => () => void;
1964
1909
  /** Close the socket. Resolves once node reports the listener closed. */
1965
1910
  readonly close: () => Promise<void>;
1966
1911
  }
@@ -2055,6 +2000,40 @@ export declare const buildTracingLayer: (options?: TracingOptions) => Promise<Tr
2055
2000
  * hit on every read. `now` injectable for tests. */
2056
2001
  export declare const cachedBackend: (backend: SecretsBackend, ttlMs?: number, now?: () => number) => SecretsBackend;
2057
2002
 
2003
+ export declare const cachedComputedCall: (args: CachedComputedCallArgs) => Effect.Effect<CachedComputedResult, unknown, never>;
2004
+
2005
+ declare interface CachedComputedCallArgs {
2006
+ readonly cache: ComputedQueryCacheDeps;
2007
+ readonly policy: ComputedQueryCachePolicy;
2008
+ readonly key: string;
2009
+ readonly name: string;
2010
+ readonly sources: ReadonlyArray<string>;
2011
+ readonly output: Schema.Top | undefined;
2012
+ readonly subject: object;
2013
+ readonly rowFilter: RowFilterScope | undefined;
2014
+ readonly ctx: object;
2015
+ readonly signal?: AbortSignal;
2016
+ /** Run the handler to a value against the OBSERVED context and subject. */
2017
+ readonly execute: (observedCtx: object, observedSubject: object) => Promise<unknown>;
2018
+ /** The handler returned a descriptor — the snapshot cache owns that query. */
2019
+ readonly onDescriptor: () => void;
2020
+ readonly isDescriptor: (value: unknown) => boolean;
2021
+ readonly unwrap: (value: unknown) => unknown;
2022
+ }
2023
+
2024
+ /**
2025
+ * Serve one computed call through the cache: a hit, or the handler run under
2026
+ * observation and its result kept — or not — by `decideComputedStore`. The
2027
+ * caller authorizes BEFORE and re-authorizes the delivered value AFTER; this
2028
+ * function only decides what the cache does.
2029
+ */
2030
+ /** A cached call's value, and the tables a live query built from it must also
2031
+ * recompute on (read by the handler, not named in `source:`). */
2032
+ declare interface CachedComputedResult {
2033
+ readonly value: unknown;
2034
+ readonly dependencies: ReadonlyArray<string>;
2035
+ }
2036
+
2058
2037
  /** One leak: a query caching ONE entry across every caller while reading a
2059
2038
  * table whose rows are scoped per tenant. */
2060
2039
  export declare interface CacheScopeLeak {
@@ -2127,15 +2106,7 @@ export declare interface CandidateShape {
2127
2106
  export declare const canonicalInputKey: (input: unknown) => string;
2128
2107
 
2129
2108
  /* Excluded from this release type: canonicalize */
2130
-
2131
- /**
2132
- * Canonical JSON — object keys sorted recursively, so two structurally equal
2133
- * inputs that differ only in key order digest identically. A retry from a
2134
- * different client build must not mint a second approval.
2135
- * Preserve own JSON fields such as `__proto__`; a prototype setter must never
2136
- * discard part of the content that an approval identity binds.
2137
- */
2138
- export declare const canonicalJson: (value: unknown) => string;
2109
+ export { canonicalJson }
2139
2110
 
2140
2111
  export { CaughtUpVerdict }
2141
2112
 
@@ -2155,6 +2126,29 @@ export declare interface CdcDelta {
2155
2126
  readonly new?: Row_3 | null;
2156
2127
  }
2157
2128
 
2129
+ /** What a cell's envelopes can be bound to. */
2130
+ export declare interface CellContext {
2131
+ readonly table: string;
2132
+ readonly column: string;
2133
+ /** The cell's row: its primary key, and every other column (for ids a copy carries). */
2134
+ readonly rowId: unknown;
2135
+ readonly row: Readonly<Record<string, unknown>>;
2136
+ /** Every `.encrypted()` column the app declares, by table. */
2137
+ readonly declared: ReadonlyMap<string, ReadonlySet<string>>;
2138
+ /** Declared renames (`declaredRenames`): the old names a value of this cell may still be bound to. */
2139
+ readonly renames?: ReadonlyMap<string, DeclaredRename>;
2140
+ }
2141
+
2142
+ /** What a cell's re-key did. */
2143
+ export declare interface CellRekey {
2144
+ /** The cell's new text — equal to the input when nothing changed. */
2145
+ readonly text: string;
2146
+ readonly rekeyed: number;
2147
+ readonly current: number;
2148
+ /** Envelopes no candidate address opened — left as they are. */
2149
+ readonly unresolved: number;
2150
+ }
2151
+
2158
2152
  export { ChangeEvent }
2159
2153
 
2160
2154
  /** A mutation invocation's full change-set — the unit of undo. */
@@ -2170,50 +2164,24 @@ export declare interface ChangeSet {
2170
2164
  readonly crossesAction?: boolean;
2171
2165
  }
2172
2166
 
2173
- export declare interface CircuitBreaker {
2174
- /** Run an effect through the breaker. OPEN ⇒ fails fast with CircuitOpen. */
2175
- <A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, E | CircuitOpen, R>;
2176
- /** Current state — reads the Ref. */
2177
- readonly state: Effect.Effect<CircuitState>;
2178
- /** Force the breaker back to closed (clears counters). */
2179
- readonly reset: Effect.Effect<void>;
2180
- }
2181
-
2182
- export declare const CircuitBreaker: {
2183
- /** Construct a breaker. Effect-returning because it allocates a Ref. */
2184
- readonly make: (options: CircuitBreakerOptions) => Effect.Effect<CircuitBreaker>;
2185
- };
2186
-
2187
- export declare interface CircuitBreakerOptions {
2188
- /** Consecutive failures before the breaker opens. */
2189
- readonly failureThreshold: number;
2190
- /** Wait before transitioning open → half-open (Duration or string). */
2191
- readonly resetTimeout: Duration.Input;
2192
- /** Successes required in half-open before closing again. Default 1. */
2193
- readonly halfOpenSuccessThreshold?: number;
2194
- /** Predicate: does this error count toward the threshold? Default: all
2195
- * typed failures count. Interruptions NEVER count (they're not a
2196
- * downstream failure — the caller went away). */
2197
- readonly isFailure?: (error: unknown) => boolean;
2198
- /** Optional name for inspect / metrics + dashboard surfacing. */
2199
- readonly name?: string;
2200
- }
2201
-
2202
2167
  /**
2203
- * Tagged error the breaker fails with while OPEN. Declarable on a
2204
- * mutation / workflow `error:` schema so it marshals typed to the client.
2168
+ * The same checks as {@link verifyFieldKeyring}, writing nothing — for a dry
2169
+ * run. Answers which key the database records as active (`undefined` before
2170
+ * the first boot recorded any) and which one would wait for the fleet.
2205
2171
  */
2206
- export declare class CircuitOpen extends CircuitOpen_base {
2207
- }
2172
+ export declare const checkFieldKeyring: (store: DataStore, keyring: FieldKeyring, tables: Iterable<unknown>) => Promise<{
2173
+ readonly active: string | undefined;
2174
+ readonly requested?: string;
2175
+ readonly notes: ReadonlyArray<string>;
2176
+ }>;
2208
2177
 
2209
- declare const CircuitOpen_base: Schema.Class<CircuitOpen, Schema.TaggedStruct<"CircuitOpen", {
2210
- /** Breaker name (the `name` option, or `'circuit'` when unnamed). */
2211
- readonly name: Schema.String;
2212
- /** Best-effort ms until the breaker next admits a probe call. */
2213
- readonly retryAfterMs: Schema.Number;
2214
- }>, Cause.YieldableError>;
2178
+ export { CircuitBreaker }
2179
+
2180
+ export { CircuitBreakerOptions }
2181
+
2182
+ export { CircuitOpen }
2215
2183
 
2216
- export declare type CircuitState = 'closed' | 'open' | 'half-open';
2184
+ export { CircuitState }
2217
2185
 
2218
2186
  /** How many bucket widths of predecessors a won claim leaves behind. */
2219
2187
  export declare const CLAIM_RETENTION_BUCKETS = 64;
@@ -2386,7 +2354,7 @@ export declare interface CompleteOAuthResult {
2386
2354
  /**
2387
2355
  * Combine multiple analytics specs into one composite spec at
2388
2356
  * app.config.ts time. The framework's boot path materialises each
2389
- * child spec (calls `child.build(sqlClient)`) and wires them through
2357
+ * child spec with its declared SQL dependency and wires them through
2390
2358
  * `composeAnalyticsSinks` so they behave as one logical sink.
2391
2359
  *
2392
2360
  * `track()` fans out to every child; reads route to the first child
@@ -2394,12 +2362,12 @@ export declare interface CompleteOAuthResult {
2394
2362
  *
2395
2363
  * Tables contributed by ANY child get merged. `initialize` effects
2396
2364
  * run sequentially in declaration order; `dispose` effects (teardown)
2397
- * fan out to every child that declares one — only present on the
2365
+ * await each child in reverse order — only present on the
2398
2366
  * composite when at least one child does.
2399
2367
  *
2400
2368
  * analytics: composeAnalytics([
2401
2369
  * postgresAnalytics(), // primary: handles reads
2402
- * posthogAnalytics({ apiKey: '…' }), // forwards track() too
2370
+ * posthogAnalytics({ apiKey: process.env.POSTHOG_API_KEY! }),
2403
2371
  * ])
2404
2372
  */
2405
2373
  export declare const composeAnalytics: (specs: ReadonlyArray<AnalyticsSinkSpec>) => AnalyticsSinkSpec;
@@ -2443,6 +2411,44 @@ export declare interface CompressionOptions {
2443
2411
  readonly minBytes?: number;
2444
2412
  }
2445
2413
 
2414
+ /** Why a call is not looked up at all. */
2415
+ declare type ComputedCacheBypass = 'input-not-encodable' | 'no-tenant' | 'anonymous-subject';
2416
+
2417
+ /**
2418
+ * The entry one call belongs to — or why it has none.
2419
+ *
2420
+ * Every part is something a different answer could hang on: the query and the
2421
+ * build that runs it (a redis cache outlives a deploy, and two releases share
2422
+ * it during a rolling one), the output shape, the ENCODED input (so a `Date`,
2423
+ * a `bigint` or a branded id keys by its wire form, and two spellings of one
2424
+ * input share an entry), the scope's caller part, and the row-filter
2425
+ * visibility the caller's reads of the declared tables resolve to.
2426
+ */
2427
+ export declare const computedCacheKey: (args: ComputedCacheKeyInput) => {
2428
+ readonly key: string;
2429
+ } | {
2430
+ readonly bypass: ComputedCacheBypass;
2431
+ };
2432
+
2433
+ declare interface ComputedCacheKeyInput {
2434
+ readonly name: string;
2435
+ /** The running build — see `codeIdentity` on the boot side. */
2436
+ readonly codeIdentity: string;
2437
+ readonly output: Schema.Top | undefined;
2438
+ readonly input: Schema.Top | undefined;
2439
+ readonly decodedInput: unknown;
2440
+ readonly scope: ComputedCacheScope;
2441
+ readonly subject: {
2442
+ readonly id?: string | null | undefined;
2443
+ readonly type?: string | undefined;
2444
+ readonly tenantId?: string | null | undefined;
2445
+ };
2446
+ readonly rowFilter: RowFilterScope | undefined;
2447
+ readonly declaredSources: ReadonlyArray<string>;
2448
+ }
2449
+
2450
+ declare type ComputedCacheScope = 'subject' | 'tenant' | 'global';
2451
+
2446
2452
  declare interface ComputedQuery {
2447
2453
  readonly __voltroComputed: true;
2448
2454
  /** The initial computed value — emitted verbatim as the first snapshot. */
@@ -2456,6 +2462,56 @@ declare interface ComputedQuery {
2456
2462
  readonly recompute: () => Promise<unknown>;
2457
2463
  }
2458
2464
 
2465
+ /** Is capture on? The request store builder wraps its store when it is. */
2466
+ export declare const computedQueryCacheCaptureInstalled: () => boolean;
2467
+
2468
+ /**
2469
+ * The cache a COMPUTED query's handler invocation is served through. Both boot
2470
+ * paths build it over the app's `Cache` (`wrapDynamic`), and so does
2471
+ * `@voltro/testing`'s `invoke`; absent ⇒ computed queries are never cached.
2472
+ */
2473
+ export declare interface ComputedQueryCacheDeps {
2474
+ readonly wrapDynamic: <A, E>(key: string, options: {
2475
+ readonly ttlMs: number;
2476
+ readonly swrMs?: number | undefined;
2477
+ readonly leaseMs?: number | undefined;
2478
+ readonly label: string;
2479
+ }, compute: Effect.Effect<{
2480
+ readonly value: A;
2481
+ readonly store?: {
2482
+ readonly stored: unknown;
2483
+ readonly tags: ReadonlyArray<string>;
2484
+ } | undefined;
2485
+ readonly notApplicable?: boolean | undefined;
2486
+ }, E, never>, read: (stored: unknown) => Effect.Effect<Option.Option<A>>) => Effect.Effect<{
2487
+ readonly value: A;
2488
+ readonly served: 'hit' | 'stale' | 'miss';
2489
+ }, E, never>;
2490
+ /** Parse the declared `cache:` (same parser as the descriptor path). */
2491
+ readonly policyFor: (cache: {
2492
+ readonly ttl: number | string;
2493
+ readonly swr?: number | string | undefined;
2494
+ readonly scope: ComputedCacheScope;
2495
+ readonly volatile?: boolean | undefined;
2496
+ }) => ComputedQueryCachePolicy | undefined;
2497
+ /** The running build — part of every key, so a deploy never reads another
2498
+ * release's entries out of a shared cache. */
2499
+ readonly codeIdentity: string;
2500
+ readonly maxEntryBytes: number;
2501
+ /** Cluster single-flight lease, when the backend supports one. */
2502
+ readonly leaseMs?: number | undefined;
2503
+ /** Once-per-(query, reason) notice that a result was not kept. */
2504
+ readonly notice: (query: string, reason: string, message: string) => void;
2505
+ }
2506
+
2507
+ /** A computed query's resolved cache policy — ms, parsed by the boot path. */
2508
+ export declare interface ComputedQueryCachePolicy {
2509
+ readonly ttlMs: number;
2510
+ readonly swrMs: number | undefined;
2511
+ readonly scope: ComputedCacheScope;
2512
+ readonly volatile: boolean;
2513
+ }
2514
+
2459
2515
  export declare const CONNECTION_GRANTS_TABLE = "_voltro_connection_grants";
2460
2516
 
2461
2517
  /** What a declaration can learn about the account it just connected. Optional
@@ -2588,6 +2644,7 @@ export { ConstraintViolation }
2588
2644
  export declare type CoordinatedEffect = () => void | CoordinatedTickOutcome | Promise<void | CoordinatedTickOutcome>;
2589
2645
 
2590
2646
  export declare interface CoordinatedScheduleDeps {
2647
+ readonly externalTasks?: ExternalizedTaskRegistry;
2591
2648
  /** The exactly-once gate. `singleCoordinator` for one-process
2592
2649
  * deployments; `makeAdvisoryLockCoordinator(store, replicaId)` for
2593
2650
  * multi-instance SQL stores. Injected so this stays testable with a
@@ -2678,8 +2735,9 @@ export declare interface CoordinatedScheduleDiagnostics {
2678
2735
  }
2679
2736
 
2680
2737
  export declare interface CoordinatedScheduleHandle {
2681
- /** Stop the periodic task. Idempotent. */
2682
- readonly stop: () => void;
2738
+ /** Stop admission immediately; await the returned completion before releasing
2739
+ * storage. Includes timer and external-trigger claims/effects. Idempotent. */
2740
+ readonly stop: () => void | Promise<void>;
2683
2741
  /** The task name (for logging / dedup diagnostics). */
2684
2742
  readonly name: string;
2685
2743
  /**
@@ -3381,6 +3439,9 @@ export declare const decideApprovalInvocation: (input: {
3381
3439
  readonly status: "approved" | "rejected";
3382
3440
  }>;
3383
3441
 
3442
+ /** The app's `.encrypted()` columns, by table. */
3443
+ export declare const declaredEncryptedColumns: (declared: Iterable<unknown>) => ReadonlyMap<string, ReadonlySet<string>>;
3444
+
3384
3445
  /**
3385
3446
  * `unknown` when the executor's failures are all declared; otherwise a type the
3386
3447
  * handler cannot satisfy, whose one property spells out the undeclared tag(s)
@@ -3393,6 +3454,27 @@ UndeclaredErrorTags<D, R>
3393
3454
  readonly undeclaredFailure: `'${UndeclaredErrorTags<D, R>}' is not in this procedure's wire union — add it to the descriptor's \`error:\`, register it through a plugin's \`errorSchemas\`, or map it away (Effect.orDie for a defect)`;
3394
3455
  };
3395
3456
 
3457
+ /** A declared rename of a table with sealed columns: the names its values may still be bound to. */
3458
+ export declare interface DeclaredRename {
3459
+ readonly previousTable?: string;
3460
+ /** New column name → the name it was renamed from. */
3461
+ readonly columns: ReadonlyMap<string, string>;
3462
+ }
3463
+
3464
+ /**
3465
+ * The renames the app declares on tables with `.encrypted()` columns —
3466
+ * `table('new').renamedFrom('old')`, a column's `.renamedFrom('old')`. A rename
3467
+ * is catalog-only, so the rows keep values bound to the old names; the re-key
3468
+ * opens them there and seals them under the new ones.
3469
+ */
3470
+ export declare const declaredRenames: (declared: Iterable<unknown>) => ReadonlyMap<string, DeclaredRename>;
3471
+
3472
+ /** Decode a stored snapshot; `undefined` for anything this version did not
3473
+ * write (an entry from another build reads as a miss). */
3474
+ export declare const decodeSnapshotValue: (stored: unknown) => {
3475
+ readonly value: unknown;
3476
+ } | undefined;
3477
+
3396
3478
  /**
3397
3479
  * Decrypt a value produced by `encryptField` or by an `.encrypted()` column —
3398
3480
  * one encoding, so that sentence is now true in both directions.
@@ -3958,7 +4040,13 @@ export declare class Dispatcher {
3958
4040
  private readonly subscriptionRegistry;
3959
4041
  private readonly matcher;
3960
4042
  private readonly subscriptions;
3961
- private readonly unsubscribeChange;
4043
+ private unsubscribeChange;
4044
+ private closed;
4045
+ private readonly operations;
4046
+ /** Register synchronously, before user code or the first await can run. */
4047
+ private beginOperation;
4048
+ /** Includes pending initial reads, not only already-visible subscriptions. */
4049
+ private readonly interests;
3962
4050
  /**
3963
4051
  * Reverse index: tableName → set of subscription ids whose
3964
4052
  * dependent-table set INCLUDES that table. A change event on table
@@ -3984,12 +4072,18 @@ export declare class Dispatcher {
3984
4072
  /** Query labels already warned about for an undeclared raw read, so a hot
3985
4073
  * subscription logs once rather than once per subscriber. */
3986
4074
  private readonly rawReadWarned;
4075
+ /** The cache fence each written-through read observed before it started. */
4076
+ private readonly readFences;
3987
4077
  private changeEpoch;
3988
4078
  private readonly committedReads;
3989
4079
  /** The CRDT-managed columns of `table` (empty when it has none) — what a
3990
4080
  * subscription's backpressure outbox folds incrementally. */
3991
4081
  crdtColumnsFor(table: string): ReadonlySet<string>;
3992
4082
  constructor(deps: DispatcherDependencies);
4083
+ private measureLoop;
4084
+ private retainInterest;
4085
+ /** One idempotent terminal path for setup failures, revocation and disconnect. */
4086
+ private removeInterest;
3993
4087
  /**
3994
4088
  * REL-14 — a raw read that nothing can invalidate, said out loud.
3995
4089
  *
@@ -4091,6 +4185,9 @@ export declare class Dispatcher {
4091
4185
  onSubscriptionsChange(listener: () => void): () => void;
4092
4186
  /** Tear down the change-event listener. Call on server shutdown. */
4093
4187
  close(): void;
4188
+ /** Close admission immediately, then join reads and deliveries before the
4189
+ * owner disposes the store. Do not await this from inside a query callback. */
4190
+ closeAndDrain(): Promise<void>;
4094
4191
  /**
4095
4192
  * Re-run EVERY live subscription, because this replica proved it missed
4096
4193
  * changes it can never recover.
@@ -4116,6 +4213,8 @@ export declare class Dispatcher {
4116
4213
  }
4117
4214
 
4118
4215
  export declare interface DispatcherDependencies {
4216
+ /** Active synchronous live-change work only; asynchronous reads are not timed. */
4217
+ readonly measureSync?: <A>(run: () => A) => A;
4119
4218
  readonly store: DataStore;
4120
4219
  /** CRDT columns per table (schemaRegistry.crdtColumns). Feeds the
4121
4220
  * downstream lane: crdt cells diff as incremental mergeCells ops.
@@ -4161,6 +4260,8 @@ export declare interface DispatcherDependencies {
4161
4260
  export declare const DORMANCY_WAKEUP_TENANT = "default";
4162
4261
 
4163
4262
  export declare interface DrainDeps<R = never> {
4263
+ /** Active synchronous handler entry only; never await a Promise inside this measurement. */
4264
+ readonly measureSync?: <A>(run: () => A) => A;
4164
4265
  readonly store: Pick<DataStore, 'query' | 'update' | 'updateMany' | 'insert' | 'delete'>;
4165
4266
  readonly handlers: ReadonlyMap<string, OutboxHandlerDefinition<R>>;
4166
4267
  /** Execute with the application's services. Required by drainOutbox when R
@@ -4291,7 +4392,7 @@ export declare interface EffectInsertIgnoreOutcomeStore {
4291
4392
 
4292
4393
  /** The minimal Effect-store surface `insertManyRowsEffect` needs. */
4293
4394
  export declare interface EffectInsertManyStore {
4294
- readonly insertMany: (table: string, rows: ReadonlyArray<Row>) => Effect.Effect<ReadonlyArray<Row>, StoreError>;
4395
+ readonly insertMany: (table: string, rows: ReadonlyArray<Row>, options?: InsertManyOptions) => Effect.Effect<ReadonlyArray<Row>, StoreError>;
4295
4396
  }
4296
4397
 
4297
4398
  /** The minimal Effect-store surface `insertRowEffect` needs. */
@@ -4315,7 +4416,7 @@ export declare interface EffectStoreOps {
4315
4416
  * hand-built descriptor. */
4316
4417
  readonly query: <R = Row>(descriptor: QueryDescriptor<R>) => Effect.Effect<ReadonlyArray<R>, StoreError>;
4317
4418
  readonly insert: (table: string, row: Row) => Effect.Effect<Row, StoreError>;
4318
- readonly insertMany: (table: string, rows: ReadonlyArray<Row>) => Effect.Effect<ReadonlyArray<Row>, StoreError>;
4419
+ readonly insertMany: (table: string, rows: ReadonlyArray<Row>, options?: InsertManyOptions) => Effect.Effect<ReadonlyArray<Row>, StoreError>;
4319
4420
  readonly patchJson: (table: string, primaryKey: string, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>) => Effect.Effect<Row | null, StoreError>;
4320
4421
  readonly update: (table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>) => Effect.Effect<Row | null, StoreError>;
4321
4422
  readonly updateMany: (table: string, patch: Readonly<Record<string, unknown>>, options: {
@@ -4402,6 +4503,10 @@ export declare const emptySchemaRegistry: SchemaRegistry;
4402
4503
  * should be observed are constructed — `observeStore` decides once, per store. */
4403
4504
  export declare const enableGraphObservation: () => void;
4404
4505
 
4506
+ /** Encode a snapshot for the cache. Throws on a value with no faithful form —
4507
+ * the caller does not store it then. */
4508
+ export declare const encodeSnapshotValue: (value: unknown) => unknown;
4509
+
4405
4510
  /**
4406
4511
  * Encrypt a value with the registered field cipher, in the SAME encoding an
4407
4512
  * `.encrypted()` column uses. Throws if no cipher is registered.
@@ -4433,6 +4538,20 @@ export declare const enforceApproval: (input: {
4433
4538
  readonly traceId?: string | undefined;
4434
4539
  }) => Promise<void>;
4435
4540
 
4541
+ /** One connected engine SDK, as it named itself in `x-voltro-sdk`. It proves nothing and grants nothing. */
4542
+ export declare interface EngineClient {
4543
+ readonly id: string;
4544
+ /** `csharp`, `unity`, `godot` or `unreal`. */
4545
+ readonly target: string;
4546
+ readonly version: string;
4547
+ /** SHA-256 of the API surface the SDK was generated from, or null when it did not say. */
4548
+ readonly surface: string | null;
4549
+ readonly connectedAt: number;
4550
+ }
4551
+
4552
+ /** The engine SDKs connected to this process now, oldest first. */
4553
+ export declare const engineClientsSnapshot: () => ReadonlyArray<EngineClient>;
4554
+
4436
4555
  export { EnqueueOptions }
4437
4556
 
4438
4557
  /**
@@ -5407,6 +5526,12 @@ export declare const externalizedTask: (name: string) => ExternalizedTask | unde
5407
5526
 
5408
5527
  export declare const externalizedTaskNames: () => ReadonlyArray<string>;
5409
5528
 
5529
+ export declare interface ExternalizedTaskRegistry {
5530
+ readonly register: (task: ExternalizedTask) => () => void;
5531
+ readonly get: (name: string) => ExternalizedTask | undefined;
5532
+ readonly names: () => ReadonlyArray<string>;
5533
+ }
5534
+
5410
5535
  /** Build an Effect `ExternalSpan` parent from a `traceparent` header so
5411
5536
  * inbound HTTP work continues the caller's trace. Returns undefined
5412
5537
  * when the header is missing/invalid (no parent → a fresh root span). */
@@ -5470,11 +5595,251 @@ declare type FailureTag<E> = 0 extends (1 & E) ? never : E extends {
5470
5595
  readonly _tag: infer T;
5471
5596
  } ? T extends 'UnknownError' ? never : T extends string ? T : never : never;
5472
5597
 
5598
+ export declare const FIELD_ENCRYPTION_ACTIVE_ENV = "VOLTRO_FIELD_ENCRYPTION_ACTIVE";
5599
+
5600
+ export declare const FIELD_ENCRYPTION_KEY_ENV = "VOLTRO_FIELD_ENCRYPTION_KEY";
5601
+
5602
+ export declare const FIELD_ENCRYPTION_KEYS_ENV = "VOLTRO_FIELD_ENCRYPTION_KEYS";
5603
+
5604
+ export declare const FIELD_ENCRYPTION_PREVIOUS_KEY_ENV = "VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS";
5605
+
5606
+ export declare const FIELD_KEY_HEARTBEAT_MS = 10000;
5607
+
5608
+ /** The table of leases on field-encryption keys. */
5609
+ export declare const FIELD_KEY_HOLDERS_TABLE = "_voltro_field_key_holders";
5610
+
5611
+ export declare const FIELD_KEY_LEASE_MS = 30000;
5612
+
5613
+ /** The table of the last field-key count and re-key. */
5614
+ export declare const FIELD_KEY_REPORTS_TABLE = "_voltro_field_key_reports";
5615
+
5616
+ /** The environment names a keyring is read from, for a resolver that fetches them. */
5617
+ export declare const FIELD_KEYRING_ENV_NAMES: readonly ["VOLTRO_FIELD_ENCRYPTION_KEYS", "VOLTRO_FIELD_ENCRYPTION_ACTIVE", "VOLTRO_FIELD_ENCRYPTION_KEY", "VOLTRO_FIELD_ENCRYPTION_KEY_PREVIOUS"];
5618
+
5619
+ /** The table recording each field-encryption key id's state and canary. */
5620
+ export declare const FIELD_KEYS_TABLE = "_voltro_field_keys";
5621
+
5473
5622
  export declare interface FieldChange {
5474
5623
  readonly from: unknown;
5475
5624
  readonly to: unknown;
5476
5625
  }
5477
5626
 
5627
+ /** What this process opened since it started, per key and location — the Governance panel's view. */
5628
+ export declare const fieldDecryptSnapshot: () => ReadonlyArray<{
5629
+ readonly key: string;
5630
+ readonly location: string;
5631
+ readonly ok: number;
5632
+ readonly failed: number;
5633
+ }>;
5634
+
5635
+ /** The last `voltro secret status`: sealed values per key id, over all and per location. */
5636
+ export declare interface FieldKeyCountReport {
5637
+ readonly totals: Readonly<Record<string, number>>;
5638
+ readonly unnamed: number;
5639
+ readonly plaintext: number;
5640
+ readonly unreadable: number;
5641
+ readonly locations: ReadonlyArray<{
5642
+ readonly table: string;
5643
+ readonly column: string;
5644
+ readonly byKey: Readonly<Record<string, number>>;
5645
+ readonly unnamed: number;
5646
+ readonly plaintext: number;
5647
+ }>;
5648
+ }
5649
+
5650
+ /** One key of a keyring: its id and its secret (a passphrase, or 64 hex). */
5651
+ export declare interface FieldKeyEntry {
5652
+ readonly id: string;
5653
+ readonly secret: string;
5654
+ }
5655
+
5656
+ /** What one heartbeat found. `unheld`: the database's active key is one this
5657
+ * process does not hold — it missed its lease while the fleet moved on. */
5658
+ export declare interface FieldKeyHeartbeat {
5659
+ readonly active: string;
5660
+ readonly activated?: {
5661
+ readonly from: string | undefined;
5662
+ readonly to: string;
5663
+ };
5664
+ readonly waiting?: FieldKeyWaiting;
5665
+ readonly unheld: boolean;
5666
+ }
5667
+
5668
+ /** A process's lease. `now` is for tests; the lease is how long a process that
5669
+ * stopped heartbeating is still waited for. */
5670
+ export declare interface FieldKeyHolder {
5671
+ readonly holderId: string;
5672
+ readonly leaseMs?: number;
5673
+ readonly now?: () => number;
5674
+ }
5675
+
5676
+ export declare interface FieldKeyHolderHandle {
5677
+ /** Stop heartbeating, wait for a beat in flight, and give up the lease. */
5678
+ readonly stop: () => Promise<void>;
5679
+ }
5680
+
5681
+ /** A holder id for this process. */
5682
+ export declare const fieldKeyHolderId: () => string;
5683
+
5684
+ /** One process's lease, as recorded — `expiresAt` in the past means it no longer counts. */
5685
+ export declare interface FieldKeyHolderRecord {
5686
+ readonly holderId: string;
5687
+ readonly keyIds: ReadonlyArray<string>;
5688
+ readonly activeKeyId: string;
5689
+ readonly heartbeatAt: Date;
5690
+ readonly expiresAt: Date;
5691
+ }
5692
+
5693
+ export declare interface FieldKeyHolderRow {
5694
+ readonly holderId: string;
5695
+ readonly keyIds: ReadonlyArray<string>;
5696
+ readonly activeKeyId: string;
5697
+ readonly expiresAt: string;
5698
+ }
5699
+
5700
+ /**
5701
+ * The id a key gets when the operator does not name one: `k` + 10 hex
5702
+ * characters of an HMAC over the derived key. Stable for a given key, short
5703
+ * enough to ride in every envelope, and not the key — the fingerprint is keyed
5704
+ * by the key itself, so it says nothing about it to someone without it.
5705
+ */
5706
+ export declare const fieldKeyIdOf: (secret: string) => string;
5707
+
5708
+ /** The last `voltro secret rekey` (also run by `rotate`): what moved, what is left, and whether the active key alone opens everything. */
5709
+ export declare interface FieldKeyRekeyReport {
5710
+ readonly active: string;
5711
+ readonly rekeyed: number;
5712
+ readonly left: number;
5713
+ readonly verified: boolean;
5714
+ }
5715
+
5716
+ export declare interface FieldKeyReports {
5717
+ readonly status?: {
5718
+ readonly reportedAt: Date;
5719
+ readonly report: FieldKeyCountReport;
5720
+ };
5721
+ readonly rekey?: {
5722
+ readonly reportedAt: Date;
5723
+ readonly report: FieldKeyRekeyReport;
5724
+ };
5725
+ }
5726
+
5727
+ /** A field cipher over several keys, each with an id — what `makeFieldKeyring` returns. */
5728
+ export declare interface FieldKeyring extends FieldCipher {
5729
+ /** The key new values are sealed under — the configured active key until
5730
+ * {@link FieldKeyring.activate} names another one the keyring holds. */
5731
+ readonly activeKeyId: string;
5732
+ /** The active key the configuration names. The database may keep another
5733
+ * active until every live process holds this one (`verifyFieldKeyring`). */
5734
+ readonly requestedKeyId: string;
5735
+ /** Every id the keyring holds, the configured active key first. */
5736
+ readonly keyIds: ReadonlyArray<string>;
5737
+ /** Each held key's fingerprint ({@link fieldKeyIdOf} — an HMAC of the key, never
5738
+ * the key), by id: what the boot line and the Governance panel show. */
5739
+ readonly fingerprints: Readonly<Record<string, string>>;
5740
+ /** Seal under one named key, whichever is active (a key's canary). */
5741
+ readonly sealWith: (keyId: string, plaintext: string, binding?: FieldBinding) => string;
5742
+ /** Does the named key open this value? `false` for a key the keyring does not hold. */
5743
+ readonly opensWith: (keyId: string, ciphertext: string, binding?: FieldBinding) => boolean;
5744
+ /** Seal new values under another held key — the database, not the
5745
+ * environment, decides which key is active (`verifyFieldKeyring`). */
5746
+ readonly activate: (keyId: string) => void;
5747
+ }
5748
+
5749
+ /** The keys a process holds, and the one new values are sealed under. */
5750
+ export declare interface FieldKeyringConfig {
5751
+ readonly active: string;
5752
+ readonly keys: ReadonlyArray<FieldKeyEntry>;
5753
+ }
5754
+
5755
+ /** A keyring configuration that cannot be used — named, never containing a key. */
5756
+ export declare class FieldKeyringConfigError extends Error {
5757
+ readonly _tag = "FieldKeyringConfigError";
5758
+ constructor(message: string);
5759
+ }
5760
+
5761
+ /** The boot cannot use this keyring with this database — the message names
5762
+ * ids, columns and the way out, never a key. */
5763
+ export declare class FieldKeyringRefusal extends Error {
5764
+ readonly _tag = "FieldKeyringRefusal";
5765
+ constructor(message: string);
5766
+ }
5767
+
5768
+ /** What a boot's check found and recorded. */
5769
+ export declare interface FieldKeyringVerification {
5770
+ /** The key new values are sealed under, as the database records it. */
5771
+ readonly active: string;
5772
+ /** Ids this boot recorded for the first time. */
5773
+ readonly registered: ReadonlyArray<string>;
5774
+ /** Set when this boot made another key active. */
5775
+ readonly activated?: {
5776
+ readonly from: string | undefined;
5777
+ readonly to: string;
5778
+ };
5779
+ /** A requested key that is not active yet, and the live processes that lack it. */
5780
+ readonly waiting?: FieldKeyWaiting;
5781
+ /** Things worth a log line, never a refusal. */
5782
+ readonly notes: ReadonlyArray<string>;
5783
+ }
5784
+
5785
+ /** One recorded key id — its state and canary, never its key. */
5786
+ export declare interface FieldKeyRow {
5787
+ readonly keyId: string;
5788
+ readonly state: FieldKeyState;
5789
+ readonly canary: string;
5790
+ readonly registeredAt: Date | null;
5791
+ readonly activatedAt: Date | null;
5792
+ readonly replacedAt: Date | null;
5793
+ readonly retiredAt: Date | null;
5794
+ readonly requestedAt: Date | null;
5795
+ }
5796
+
5797
+ /** A key's state. `pending`: held, never active. `active`: new values are sealed
5798
+ * under it — exactly one. `decrypt-only`: was active, replaced; values may still
5799
+ * be sealed under it. `retired`: nothing is sealed under it any more. */
5800
+ export declare type FieldKeyState = 'pending' | 'active' | 'decrypt-only' | 'retired';
5801
+
5802
+ export declare interface FieldKeyStateRow {
5803
+ readonly keyId: string;
5804
+ readonly state: string;
5805
+ readonly activatedAt: string | null;
5806
+ readonly requestedAt: string | null;
5807
+ }
5808
+
5809
+ export declare interface FieldKeyStatus {
5810
+ /** The recorded keys and their states — empty before the first boot recorded any. */
5811
+ readonly keys: ReadonlyArray<FieldKeyStateRow>;
5812
+ /** Every process that holds a lease, live or not (`expiresAt` says which). */
5813
+ readonly holders: ReadonlyArray<FieldKeyHolderRow>;
5814
+ readonly locations: ReadonlyArray<SealedLocation>;
5815
+ /** Envelopes per key id, over every location. */
5816
+ readonly totals: Readonly<Record<string, number>>;
5817
+ readonly unnamed: number;
5818
+ readonly plaintext: number;
5819
+ /** Columns that could not be read — listed, never silently left out of the counts. */
5820
+ readonly unreadable: ReadonlyArray<UnreadableColumn>;
5821
+ }
5822
+
5823
+ /**
5824
+ * Count every sealed value in the database by the key it names. `declared` are
5825
+ * the app's tables: their `.encrypted()` columns are always reported (with
5826
+ * plaintext counted), the rest only where envelopes were found.
5827
+ */
5828
+ export declare const fieldKeyStatus: (dialect: DialectId, declared: Iterable<unknown>) => Effect.Effect<{
5829
+ keys: FieldKeyStateRow[];
5830
+ holders: FieldKeyHolderRow[];
5831
+ locations: SealedLocation[];
5832
+ totals: Record<string, number>;
5833
+ unreadable: UnreadableColumn[];
5834
+ unnamed: number;
5835
+ plaintext: number;
5836
+ }, SqlError, SqlClient.SqlClient>;
5837
+
5838
+ export declare interface FieldKeyWaiting {
5839
+ readonly keyId: string;
5840
+ readonly holders: ReadonlyArray<string>;
5841
+ }
5842
+
5478
5843
  /**
5479
5844
  * Canonical fingerprint over `(table, predicate, indexHint?)`. The
5480
5845
  * optional `indexHint` participates so two subscriptions with the same
@@ -5851,6 +6216,13 @@ export declare interface HealthConfigInput {
5851
6216
  readonly readiness?: () => boolean | Promise<boolean>;
5852
6217
  }
5853
6218
 
6219
+ /**
6220
+ * One heartbeat: renew this process's lease, complete a requested activation
6221
+ * once every live holder holds the key, and seal under the database's active
6222
+ * key from now on. Any holder can complete an activation another one requested.
6223
+ */
6224
+ export declare const heartbeatFieldKeys: (store: DataStore, keyring: FieldKeyring, holder: FieldKeyHolder) => Promise<FieldKeyHeartbeat>;
6225
+
5854
6226
  /** Define a histogram. `boundaries` default to the framework duration buckets. */
5855
6227
  export declare const histogramMetric: (name: string, boundaries?: ReadonlyArray<number>, description?: string) => Metric.Histogram<number>;
5856
6228
 
@@ -5992,9 +6364,10 @@ export declare class InMemoryDataStore implements DataStore {
5992
6364
  * to tell apart from "no such table".
5993
6365
  */
5994
6366
  storedTables(): ReadonlyArray<StoredTableShape>;
6367
+ integrityQuery(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
5995
6368
  query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
5996
6369
  insert(table: string, row: Row): Promise<Row>;
5997
- insertMany(table: string, rows: ReadonlyArray<Row>): Promise<ReadonlyArray<Row>>;
6370
+ insertMany(table: string, rows: ReadonlyArray<Row>, options?: InsertManyOptions): Promise<ReadonlyArray<Row>>;
5998
6371
  patchJson(table: string, primaryKey: RowKey, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>): Promise<Row | null>;
5999
6372
  update(table: string, primaryKey: RowKey, patch: Readonly<Record<string, unknown>>): Promise<Row | null>;
6000
6373
  delete(table: string, primaryKey: RowKey): Promise<boolean>;
@@ -6026,8 +6399,9 @@ export declare const insertIgnoreRowOutcomeEffect: <T extends AnyWritableTable>(
6026
6399
  readonly outcome: "inserted" | "ignored";
6027
6400
  }, StoreError>;
6028
6401
 
6029
- /** `insertRowEffect`'s batch — every element checked against `InferInsertRow<T>`. */
6030
- export declare const insertManyRowsEffect: <T extends AnyWritableTable>(store: EffectInsertManyStore, table: T, rows: ReadonlyArray<InferInsertRow<T>>) => Effect.Effect<ReadonlyArray<InferRow<T>>, StoreError>;
6402
+ /** `insertRowEffect`'s batch — every element checked against `InferInsertRow<T>`.
6403
+ * `{ returning: 'none' }` resolves to `[]` without reading the rows back. */
6404
+ export declare const insertManyRowsEffect: <T extends AnyWritableTable>(store: EffectInsertManyStore, table: T, rows: ReadonlyArray<InferInsertRow<T>>, options?: InsertManyOptions) => Effect.Effect<ReadonlyArray<InferRow<T>>, StoreError>;
6031
6405
 
6032
6406
  /**
6033
6407
  * Insert a row, typed against the table, on the Effect channel.
@@ -6132,6 +6506,10 @@ export { inspectWorkflow }
6132
6506
 
6133
6507
  export declare const installApprovalGate: (gate: ApprovalGateApi) => void;
6134
6508
 
6509
+ /** Turn on read capture for cached computes — called at boot when any query
6510
+ * declares `cache:`. Until then the request store is not wrapped for it. */
6511
+ export declare const installComputedQueryCacheCapture: () => void;
6512
+
6135
6513
  /**
6136
6514
  * Install the resolver that answers `guards: [{ action, resourceType }]`.
6137
6515
  *
@@ -6235,6 +6613,9 @@ export declare const isExpectationDefinition: (value: unknown) => value is Expec
6235
6613
 
6236
6614
  export declare const isExperimentDefinition: (value: unknown) => value is ExperimentDefinition;
6237
6615
 
6616
+ /** Is this cipher a keyring (ids, per-key seal and open), not a bare cipher? */
6617
+ export declare const isFieldKeyring: (cipher: FieldCipher | undefined) => cipher is FieldKeyring;
6618
+
6238
6619
  export { isForeignKeyViolation }
6239
6620
 
6240
6621
  /**
@@ -6461,7 +6842,7 @@ export declare interface KvFacade {
6461
6842
  readonly service: Effect.Success<typeof Kv>;
6462
6843
  /** The resolved backend kind — for boot logging. */
6463
6844
  readonly backend: 'database' | 'redis' | 'memory';
6464
- /** Tear down the managed runtime (closes a pooled redis connection). */
6845
+ /** Release this consumer runtime; an injected registry retains its pools. */
6465
6846
  readonly dispose: () => Promise<void>;
6466
6847
  }
6467
6848
 
@@ -6470,6 +6851,10 @@ export declare interface KvFacadeOptions {
6470
6851
  readonly store: DataStore;
6471
6852
  /** Defaults to `process.env`. */
6472
6853
  readonly env?: NodeJS.ProcessEnv;
6854
+ readonly backend?: 'database' | 'redis' | 'memory';
6855
+ readonly connectionName?: string;
6856
+ readonly keyPrefix?: string;
6857
+ readonly connections?: RespConnections;
6473
6858
  }
6474
6859
 
6475
6860
  /**
@@ -6591,6 +6976,27 @@ export declare interface LocalityAwareOptions {
6591
6976
  */
6592
6977
  export declare const localityAwareSelector: (options: LocalityAwareOptions) => ReplicaSelector;
6593
6978
 
6979
+ /** One location's outcome. */
6980
+ export declare interface LocationRekey {
6981
+ readonly table: string;
6982
+ readonly column: string;
6983
+ readonly cells: number;
6984
+ readonly rekeyed: number;
6985
+ readonly current: number;
6986
+ readonly unresolved: number;
6987
+ /** Cells the app changed between the read and the write — run again. */
6988
+ readonly changedMeanwhile: number;
6989
+ /** Values that need a re-key in a table with no single-column primary key to address the cell by. */
6990
+ readonly unaddressable: number;
6991
+ }
6992
+
6993
+ /** Read the current key row within its expected tenant under a write fence.
6994
+ * Supply an existing transaction after the application's organization/account
6995
+ * fences and keep it through the protected write. Null matches unassigned
6996
+ * keys only. The internal row uses the normal store codec; never return it in
6997
+ * a public response. This is not bearer verification or an authorization grant. */
6998
+ export declare const lockApiKeyById: (store: DataStore, id: string, tenantId: string | null) => Promise<ApiKeyRow | null>;
6999
+
6594
7000
  export declare interface LogTraceContext {
6595
7001
  readonly traceId: string;
6596
7002
  readonly spanId?: string;
@@ -6662,7 +7068,7 @@ log?: SchedulerLogger) => Coordinator;
6662
7068
  * tampered / wrong-key value AND on a bound value read at another address —
6663
7069
  * both surface as a read error, never silent corruption.
6664
7070
  */
6665
- export declare const makeAesCipher: (key: Buffer) => FieldCipher;
7071
+ export declare const makeAesCipher: (key: Buffer, keyId?: string) => FieldCipher;
6666
7072
 
6667
7073
  export declare const makeApiKeyService: (store: ApiKeyStore, opts?: {
6668
7074
  prefix?: string;
@@ -6690,6 +7096,61 @@ export declare const makeApiKeyUsageFlusher: (store: ApiKeyStore) => (id: string
6690
7096
  readonly lastUsedAt: number;
6691
7097
  }) => Promise<void>;
6692
7098
 
7099
+ /** In-memory, bounded finalized-bucket queue. Current instrumentation is partial;
7100
+ * this collector cannot certify complete workload coverage merely from uptime. */
7101
+ export declare const makeApiRoleBucketSampler: (input: {
7102
+ readonly replicaId: string;
7103
+ readonly startedAt: number;
7104
+ readonly role: string;
7105
+ readonly workloads: ReadonlyArray<ApiWorkload>;
7106
+ readonly epochNow: number;
7107
+ readonly monotonicNow: number;
7108
+ }) => {
7109
+ record(segment: {
7110
+ readonly workload: ApiWorkload;
7111
+ readonly from: number;
7112
+ readonly until: number;
7113
+ }): void;
7114
+ /** Heartbeat every30s. Delays >60s or clock skew >1s conservatively invalidate
7115
+ * affected intervals; never spread elapsed request time across buckets. */
7116
+ advance(monotonicNow: number, epochNow: number): void;
7117
+ pending(): ReadonlyArray<ApiRoleWorkloadBucket>;
7118
+ acknowledge(from: number): void;
7119
+ status: () => {
7120
+ pendingBuckets: number;
7121
+ droppedBuckets: number;
7122
+ rejectedSegments: number;
7123
+ };
7124
+ };
7125
+
7126
+ /** Measures only the synchronous execution of a callback, including throws.
7127
+ * Returning a Promise measures its construction, not its awaited lifetime.
7128
+ * Each resumed async callback must be instrumented separately. No wall-time
7129
+ * request duration may be fed into this counter as a substitute for loop time.
7130
+ * Nested calls on this recorder suspend their parent, so time is exclusive. */
7131
+ export declare const makeApiRoleWorkloadMetrics: (roles: ReadonlyArray<string>, options?: {
7132
+ readonly now?: () => number;
7133
+ readonly onSegment?: (segment: {
7134
+ readonly workload: ApiWorkload;
7135
+ readonly from: number;
7136
+ readonly until: number;
7137
+ }) => void;
7138
+ }) => {
7139
+ role: string;
7140
+ observationErrors: () => number;
7141
+ /** Effect4's tracer context surrounds each synchronous primitive, including
7142
+ * the first, resumed and inherited child work. Preserve the existing tracer;
7143
+ * Promise callbacks outside evaluation still use measureSync explicitly. */
7144
+ observeEffect<A, E, R>(effect: Effect.Effect<A, E, R>, workload: ApiWorkload): Effect.Effect<A, E, R>;
7145
+ measureSync<A>(workload: ApiWorkload, run: () => A): A;
7146
+ /** Local totals, not a 24-hour history or evidence of full instrumentation. */
7147
+ snapshot(): ReadonlyArray<{
7148
+ readonly role: string;
7149
+ readonly workload: ApiWorkload;
7150
+ readonly seconds: number;
7151
+ }>;
7152
+ };
7153
+
6693
7154
  /** Build the `ctx.access` slice for a subject. Reads the effective-scope seam
6694
7155
  * LIVE (via getters/closures) so a scope rbac resolves after context
6695
7156
  * construction is still reflected. Used by the shared AppContext builder AND
@@ -6713,7 +7174,7 @@ export declare interface MakeApprovalGateOptions {
6713
7174
 
6714
7175
  /** Build the `ctx.kv` facade. `KV_BACKEND` picks the backend; unknown values
6715
7176
  * fall back to `database`. */
6716
- export declare const makeAsyncKv: ({ store, env }: KvFacadeOptions) => Promise<KvFacade>;
7177
+ export declare const makeAsyncKv: ({ store, env, backend: configuredBackend, connectionName, keyPrefix, connections }: KvFacadeOptions) => Promise<KvFacade>;
6717
7178
 
6718
7179
  /**
6719
7180
  * The `AuthMiddleware` + `ConnectionInfoMiddleware` pair every rpc surface
@@ -6832,19 +7293,45 @@ export declare const makeEventPublisher: (ctx: EventPublishContext) => EventPubl
6832
7293
 
6833
7294
  export declare const makeEventsFacade: (options: EventsFacadeOptions) => EventsAppContext;
6834
7295
 
7296
+ /** Pass one instance to a host's scheduler factories and trigger route. */
7297
+ export declare const makeExternalizedTaskRegistry: () => ExternalizedTaskRegistry;
7298
+
6835
7299
  /**
6836
7300
  * Derive a key from a passphrase and build the cipher — with the keys a
6837
- * ROTATION retires, when there are any.
7301
+ * ROTATION retires, when there are any. Ids are derived from the keys
7302
+ * (`fieldKeyIdOf`); a previous key equal to the current one is skipped.
6838
7303
  *
6839
- * Writes use the current key; reads try the current one, then each previous
6840
- * one. GCM authenticates, so a wrong key fails its tag rather than producing
6841
- * garbage, and trying them in turn is safe. Without this, a key change made
6842
- * every value written before it unreadable — measured: every sleeping or
6843
- * suspended run of an `encrypted: true` workflow failed its replay with
6844
- * `MalformedMessage`, and every `.encrypted()` column read failed.
7304
+ * Writes use the current key; reads open a value by the key it names, or by
7305
+ * trying the current one, then each previous one. GCM authenticates, so a
7306
+ * wrong key fails its tag rather than producing garbage. Without this, a key
7307
+ * change made every value written before it unreadable — measured: every
7308
+ * sleeping or suspended run of an `encrypted: true` workflow failed its replay
7309
+ * with `MalformedMessage`, and every `.encrypted()` column read failed.
6845
7310
  */
6846
7311
  export declare const makeFieldCipher: (passphrase: string, previous?: ReadonlyArray<string>) => FieldCipher;
6847
7312
 
7313
+ /**
7314
+ * A cipher over a keyring: new values are sealed under the active key and name
7315
+ * it; a value is opened by the key it names (a lookup), and one written before
7316
+ * values named their key by trying each key, the active one first.
7317
+ *
7318
+ * A named key that is not in the keyring is a refusal that names it, so an
7319
+ * operator reads "sealed under k2026a, which this process does not hold"
7320
+ * instead of a GCM failure. A named key that does not open the value (an id
7321
+ * that was altered) falls back to trying the others — the id is a hint, GCM is
7322
+ * the authority.
7323
+ */
7324
+ export declare const makeFieldKeyring: (config: FieldKeyringConfig, options?: {
7325
+ /**
7326
+ * Refuse to seal until {@link FieldKeyring.activate} has run — the boot's
7327
+ * keyring sets this, so nothing is sealed under the configured key before
7328
+ * the database has said which key is active. A value sealed under a key the
7329
+ * fleet has not accepted is one a replica on the previous configuration
7330
+ * cannot read.
7331
+ */
7332
+ readonly sealOnlyOnceActivated?: boolean;
7333
+ }) => FieldKeyring;
7334
+
6848
7335
  /** Build the fluent `select` / `update` / `delete` entry functions over a
6849
7336
  * backend + scope. Wired into `wrapStoreWithMixinBehaviour` so they hang
6850
7337
  * off `ctx.store` alongside the keyed `query`/`insert`/`update`/`delete`. */
@@ -6867,12 +7354,18 @@ export declare const makeLazyWorkflowFacade: (resolve: () => WorkflowsAppContext
6867
7354
  export declare const makeListConnectionsExecutor: (deps: ConnectionBuiltinDeps) => (_input: Record<string, never>, ctx: ConnectionExecutorCtx) => Promise<ReadonlyArray<ConnectionState_2>>;
6868
7355
 
6869
7356
  /**
6870
- * Build the replay codec from a mutation's `descriptor.output` Schema (call it at
6871
- * the bind site). Both directions fall back to the raw value if the Schema can't
6872
- * round-trip it — a guard for exotic schemas; for a value the handler actually
6873
- * produced, `encodeUnknownSync` never throws. `undefined` schema → no codec.
7357
+ * Build one input/output contract from both declared schemas. Refuse incompatible
7358
+ * values with payload-free errors; never return or log an old raw response
7359
+ * when the current output contract cannot decode it. Input is captured from its schema before keyed acquisition.
6874
7360
  */
6875
- export declare const makeMutationOutputCodec: (outputSchema: Schema.Top | undefined) => MutationOutputCodec<unknown> | undefined;
7361
+ export declare const makeMutationCodec: (inputSchema: Schema.Top, outputSchema: Schema.Top, admit: MutationCodec<unknown>["admit"]) => MutationCodec<unknown>;
7362
+
7363
+ /** Current admission for a keyed unary call, including its cached result.
7364
+ * Pure refresher binding is separate from business interceptors/executors. */
7365
+ export declare const makeMutationReplayAdmission: (procedure: MutationLike, kind: "mutation" | "action", options?: {
7366
+ readonly defaultDeny?: boolean;
7367
+ readonly bindReplayAuthority?: RpcReplayAuthorityBinding | undefined;
7368
+ }) => ((input: unknown, request: ServeRequestContext) => Promise<void>);
6876
7369
 
6877
7370
  /**
6878
7371
  * Build the shared mutation runner. Used by BOTH the rpc WS handler and
@@ -6946,7 +7439,7 @@ export declare const makeOutboxFacade: (deps: OutboxFacadeDeps) => OutboxFacade;
6946
7439
  */
6947
7440
  export declare const makeProcessAdapter: (supervisor: AppSupervisor) => WakeAdapter;
6948
7441
 
6949
- export declare const makeQueryDescriptorProducer: <D>(deps: QueryProducerDeps<D>) => (query: MutationLike, input: unknown) => (requestContext: ServeRequestContext) => D | ComputedQuery | Effect.Effect<D | ComputedQuery, unknown, never>;
7442
+ export declare const makeQueryDescriptorProducer: <D>(deps: QueryProducerDeps<D>, workloadOverride?: "rpc" | "subscriptions") => (query: MutationLike, input: unknown) => (requestContext: ServeRequestContext) => ComputedQuery | D | Effect.Effect<ComputedQuery | D, unknown, never>;
6950
7443
 
6951
7444
  export declare const makeQueryFinalizer: (options: QueryFinalizeOptions) => ((descriptor: QueryDescriptor, subject: {
6952
7445
  readonly tenantId?: string | null;
@@ -7385,35 +7878,22 @@ export declare const MIN_LEVEL_RESYNC_MS = 30000;
7385
7878
  /** Mint a fresh service token. `voltro dev` and `voltro secret generate` call this. */
7386
7879
  export declare const mintServiceToken: () => string;
7387
7880
 
7388
- /**
7389
- * Ordering token for one mirrored write.
7390
- *
7391
- * **The sink MUST persist this and MUST NOT invent its own.** It is
7392
- * assigned by the framework at the moment the change leaves the store's
7393
- * change channel — i.e. in COMMIT order — so a write with a lower
7394
- * version is, by construction, an older image of that row. Stamping a
7395
- * version at sink-call time instead (a `Date.now()` inside `upsert`)
7396
- * makes a late-arriving STALE image outrank the fresh one and win
7397
- * permanently; that is the bug this type exists to make unrepresentable.
7398
- *
7399
- * How the number is derived depends on the store's `changeScope`:
7400
- *
7401
- * - `'local'` (one process observes each change): a hybrid clock —
7402
- * wall-clock microseconds forced strictly upward, so it orders within
7403
- * a millisecond and keeps increasing across a restart.
7404
- * - `'fleet'` (every replica observes every change): derived from the
7405
- * CHANGE, not from the receiving process — the warehouse's own max
7406
- * version for the key (read once via {@link AnalyticsMirrorImpl.maxVersion})
7407
- * plus the change's per-key position in the totally-ordered fleet
7408
- * stream. N replicas mirroring the same change therefore stamp the
7409
- * SAME number, and the sink's version guard dedupes the N duplicate
7410
- * writes for free — no leader, no clock, no skew hazard.
7411
- */
7412
- export declare type MirrorVersion = number;
7881
+ export { MirrorVersion }
7413
7882
 
7414
7883
  /** The error `.one()` throws: the factory's, or the framework's `NoRowFound`. */
7415
7884
  export declare const missingRowError: (info: NoRowFoundInfo, onMissing: OnMissingRow | undefined) => unknown;
7416
7885
 
7886
+ /**
7887
+ * Store encoded wire output and decode it against the current output contract.
7888
+ * A failed codec never falls back to an unvalidated raw response.
7889
+ */
7890
+ export declare interface MutationCodec<Output> {
7891
+ readonly input: Schema.Top;
7892
+ readonly admit: (input: unknown, context: RuntimeContext) => Promise<void>;
7893
+ readonly encode: (output: Output) => unknown;
7894
+ readonly decode: (stored: unknown) => Output;
7895
+ }
7896
+
7417
7897
  export declare interface MutationLifecycle {
7418
7898
  readonly afterCommit: (work: () => Promise<unknown>) => void;
7419
7899
  /** Already selected physical, namespace-bound nontransactional store.
@@ -7425,8 +7905,16 @@ export declare interface MutationLifecycle {
7425
7905
  export declare interface MutationLike {
7426
7906
  readonly descriptor: {
7427
7907
  readonly name: string;
7908
+ readonly undo?: {
7909
+ readonly capture: false;
7910
+ readonly reason: string;
7911
+ } | undefined;
7428
7912
  readonly source?: string | ReadonlyArray<string> | undefined;
7429
7913
  readonly guards?: ReadonlyArray<AnyCheckSpec> | undefined;
7914
+ /** Query only — read by the computed-query cache (`computedQueryCache.ts`). */
7915
+ readonly cache?: QueryCacheLike | undefined;
7916
+ readonly input?: Schema.Top | undefined;
7917
+ readonly output?: Schema.Top | undefined;
7430
7918
  /** The second-human control (mutation/action only). Enforced by
7431
7919
  * `enforceApprovalGate`, immediately after `enforceGuards`. */
7432
7920
  readonly requiresApproval?: AnyApprovalPolicy | undefined;
@@ -7441,25 +7929,20 @@ export declare interface MutationLike {
7441
7929
  executor(input: unknown, ctx: unknown): unknown;
7442
7930
  }
7443
7931
 
7444
- /**
7445
- * Encode/decode a mutation's output for idempotent replay. On a FRESH call the
7446
- * WIRE form (`encode`) is stored, so a later replay `decode`s it back to the exact
7447
- * Output the handler would have returned and the rpc layer re-encodes it
7448
- * identically. Storing the DECODED Output directly would corrupt `Date`/etc.
7449
- * fields through the store's JSON round-trip (encode there expects a `Date`, not
7450
- * the ISO string a round-trip produced). Built from `descriptor.output` at the
7451
- * call site; both fns fall back to the raw value if the Schema can't round-trip.
7452
- */
7453
- export declare interface MutationOutputCodec<Output> {
7454
- readonly encode: (output: Output) => unknown;
7455
- readonly decode: (stored: unknown) => Output;
7456
- }
7457
-
7458
7932
  export declare interface MutationRunnerDeps {
7933
+ /** Measures only the synchronous executor entry, never an awaited result. */
7934
+ readonly measureSync?: <A>(run: () => A) => A;
7459
7935
  readonly outcomeOutbox?: (request: ServeRequestContext) => OutboxEnqueueFacade | undefined;
7460
7936
  /** A failure aborts the business transaction; transient contention replays
7461
7937
  * executor and hook together. Never perform external I/O here. */
7462
7938
  readonly beforeMutationCommit?: ServeMutationCommitHook | undefined;
7939
+ /**
7940
+ * Resolves once the cache invalidations this process started have reached
7941
+ * the cache. Awaited after commit, BEFORE the mutation answers — without it
7942
+ * the writer's next read raced its own invalidation and was served the
7943
+ * pre-write entry. Both boot paths pass `wireCacheInvalidation`'s `settled`.
7944
+ */
7945
+ readonly settleCacheInvalidations?: () => Promise<void>;
7463
7946
  readonly store: TransactionalStore;
7464
7947
  /** Select and provision the request's physical store before opening its
7465
7948
  * transaction. Resolved once, including across retries and post-commit folds. */
@@ -7524,6 +8007,19 @@ export declare interface MutationStore extends DataStore {
7524
8007
  hardDelete(table: string, primaryKey: RowKey): Promise<boolean>;
7525
8008
  }
7526
8009
 
8010
+ /**
8011
+ * The encoding a body of `size` bytes would be compressed with — `undefined`
8012
+ * unless (enabled ∧ type compressible ∧ size ≥ threshold ∧ the client accepts
8013
+ * an encoding). Lets a caller that has not read the body yet (a static file
8014
+ * known only by `stat`) take the same decision `maybeCompress` takes.
8015
+ */
8016
+ export declare const negotiatedCompression: (input: {
8017
+ readonly size: number;
8018
+ readonly contentType: string | undefined;
8019
+ readonly acceptEncoding: string | undefined;
8020
+ readonly options?: CompressionOptions | undefined;
8021
+ }) => NegotiatedEncoding | undefined;
8022
+
7527
8023
  export declare type NegotiatedEncoding = 'br' | 'gzip';
7528
8024
 
7529
8025
  /** Pick the encoding the client accepts — brotli preferred, gzip second,
@@ -7531,24 +8027,7 @@ export declare type NegotiatedEncoding = 'br' | 'gzip';
7531
8027
  * (a client sending both accepts both). */
7532
8028
  export declare const negotiateEncoding: (acceptEncoding: string | undefined) => NegotiatedEncoding | undefined;
7533
8029
 
7534
- /**
7535
- * A fresh trace id for work that has NO incoming span to continue — a webhook
7536
- * delivery, a schedule firing, a devtools invoke, a REST or gRPC entry point.
7537
- *
7538
- * 128 random bits as 32 lowercase hex, the shape `TRACEPARENT_RE` above accepts
7539
- * and the shape a collector, a log pipeline or `voltro traces` can join on.
7540
- *
7541
- * The reason this exists as ONE function: every entry point that minted its own
7542
- * had drifted somewhere different, and three of the shapes were not ids at all.
7543
- * `incoming:<webhookId>`, `reaction:<workflow>` and the literal
7544
- * `'webhooks:trigger'` are CONSTANTS — they were the caller's `source` label
7545
- * copied into the id field, so every delivery a process ever handled shared one
7546
- * trace and the waterfall merged unrelated work. `public-`/`grpc-`/`rest-` plus
7547
- * eight base36 characters carried ~41 bits, which collides long before a busy
7548
- * day is over. The entry-point kind is not lost by fixing this: it lives in the
7549
- * `source` field beside the id, which is where it was already being written.
7550
- */
7551
- export declare const newTraceId: () => string;
8030
+ export { newTraceId }
7552
8031
 
7553
8032
  /**
7554
8033
  * The backoff curve, extracted so it can be asserted directly — a schedule
@@ -7665,6 +8144,14 @@ export declare interface NoRowFoundInfo {
7665
8144
  readonly found: number;
7666
8145
  }
7667
8146
 
8147
+ /** Record a non-table input of the cached compute in flight, if any. Called by
8148
+ * the framework surfaces that reach outside the database. */
8149
+ export declare const noteCacheVolatile: (reason: string) => void;
8150
+
8151
+ /** Note that the query in flight depends on `table` without reading it
8152
+ * through `ctx.store`. */
8153
+ export declare const noteDependency: (table: string) => void;
8154
+
7668
8155
  /** Note one read against the in-flight procedure. No scope → nothing happens. */
7669
8156
  export declare const noteRead: (descriptor: QueryDescriptor) => void;
7670
8157
 
@@ -7694,6 +8181,45 @@ export declare interface Notification {
7694
8181
  readonly newMatched: boolean;
7695
8182
  }
7696
8183
 
8184
+ /** What a cross-environment restore did to the sealed values it cannot read. */
8185
+ export declare interface NullSealedReport {
8186
+ readonly nulled: ReadonlyArray<{
8187
+ readonly table: string;
8188
+ readonly column: string;
8189
+ readonly count: number;
8190
+ }>;
8191
+ readonly unreadable: ReadonlyArray<{
8192
+ readonly table: string;
8193
+ readonly column: string;
8194
+ readonly count: number;
8195
+ }>;
8196
+ /** Recorded key ids removed: they came with the restored data and this environment does not hold them. */
8197
+ readonly forgottenKeys: ReadonlyArray<string>;
8198
+ readonly unreadableColumns: ReadonlyArray<UnreadableColumn>;
8199
+ }
8200
+
8201
+ /**
8202
+ * After restoring another environment's database: write NULL for every whole
8203
+ * sealed value no configured key opens — the source ciphertext is useless here
8204
+ * and users re-enter it — count the sealed copies inside other values (they
8205
+ * cannot be nulled), and forget the restored key records and leases this
8206
+ * environment does not hold, so the next boot records its own keys.
8207
+ */
8208
+ export declare const nullUnreadableSealedValues: (dialect: DialectId, keyring: FieldKeyring | undefined, declared: Iterable<unknown>) => Effect.Effect<{
8209
+ nulled: {
8210
+ table: string;
8211
+ column: string;
8212
+ count: number;
8213
+ }[];
8214
+ unreadable: {
8215
+ table: string;
8216
+ column: string;
8217
+ count: number;
8218
+ }[];
8219
+ forgottenKeys: string[];
8220
+ unreadableColumns: UnreadableColumn[];
8221
+ }, SqlError, SqlClient.SqlClient>;
8222
+
7697
8223
  export declare interface OAuth2ConnectionDefinition extends ConnectionDefinitionCommon {
7698
8224
  readonly kind: 'oauth2';
7699
8225
  readonly authorizeUrl: string;
@@ -7734,6 +8260,17 @@ export declare type OAuthClientCredential = string | (() => string);
7734
8260
 
7735
8261
  export declare const oauthCredentialValue: (credential: OAuthClientCredential) => string;
7736
8262
 
8263
+ /** Preserve the caller's environment, Exit and cancellation. Observation is
8264
+ * installed before the body's first primitive; it creates no detached runtime. */
8265
+ export declare const observeApiRoleEffect: <A, E>(effect: Effect.Effect<A, E, never>, metrics: ReturnType<typeof makeApiRoleWorkloadMetrics>, workload: ApiWorkload) => Effect.Effect<A, E, never>;
8266
+
8267
+ /**
8268
+ * Test seam: a ConnectionInfo layer that hands every request's headers to
8269
+ * `observe` and reports only the client id. It never authenticates and is not a
8270
+ * boot path; it lives here so ConnectionInfo keeps one construction site.
8271
+ */
8272
+ export declare const observeConnectionHeaders: (observe: (headers: Readonly<Record<string, string | undefined>>, clientId: number) => void) => Layer.Layer<ConnectionInfoMiddleware>;
8273
+
7737
8274
  /** One recorded `(procedure → table, op)` edge. */
7738
8275
  export declare interface ObservedEdge {
7739
8276
  readonly tag: string;
@@ -7817,6 +8354,16 @@ export declare type OnMissingRow = (info: NoRowFoundInfo) => unknown;
7817
8354
  */
7818
8355
  export declare const onProcessShutdown: (hook: () => void | Promise<void>) => (() => void);
7819
8356
 
8357
+ /**
8358
+ * Open every envelope in one cell with `keyring` — each under the address it
8359
+ * was sealed under, found the same way a re-key finds it. A keyring holding the
8360
+ * active key alone answers "does anything still need another key?".
8361
+ */
8362
+ export declare const openCell: (text: string, keyring: FieldKeyring, context: CellContext) => {
8363
+ readonly opened: number;
8364
+ readonly unopened: number;
8365
+ };
8366
+
7820
8367
  /** Thrown by `.expectVersion(n).set(...)` when the optimistic-lock guard
7821
8368
  * matched no row (the row was concurrently updated or deleted). */
7822
8369
  export declare class OptimisticLockError extends OptimisticLockError_base {
@@ -8112,6 +8659,9 @@ export declare interface PairInput {
8112
8659
  */
8113
8660
  export declare const parseApprovalInterval: (spec: string) => number | null;
8114
8661
 
8662
+ /** `unity/0.1.0; surface=<64 hex>` → its parts; anything else is not an engine SDK. */
8663
+ export declare const parseEngineSdkHeader: (value: string) => Omit<EngineClient, "id" | "connectedAt"> | null;
8664
+
8115
8665
  /** Parse a `traceparent` header. Returns null when malformed or when
8116
8666
  * the all-zero invalid ids are present (per the spec those are
8117
8667
  * treated as "no parent"). */
@@ -8316,8 +8866,7 @@ export declare type ProbeKind = 'liveness' | 'readiness' | 'startup';
8316
8866
  * columns, which have no wire representation at all. */
8317
8867
  export declare const projectConnectionState: (definition: ConnectionDefinition, row: AnyRow_2 | undefined) => ConnectionState_2;
8318
8868
 
8319
- /** Exact-match property filter. Values are AND'd. */
8320
- export declare type PropertyFilter = Readonly<Record<string, string | number | boolean | null>>;
8869
+ export { PropertyFilter }
8321
8870
 
8322
8871
  export declare interface ProvenanceActor {
8323
8872
  readonly id: string | null;
@@ -8471,8 +9020,25 @@ export declare interface PumpOptions {
8471
9020
  */
8472
9021
  export declare const pumpOutbox: <T>(outbox: SubscriptionOutbox<T>, sink: OutboxSink<T>, label: string, options?: PumpOptions) => Promise<void>;
8473
9022
 
9023
+ /** A query's declared `cache:` block, structurally — see `QueryCacheConfig`. */
9024
+ export declare interface QueryCacheLike {
9025
+ readonly ttl: number | string;
9026
+ readonly swr?: number | string | undefined;
9027
+ readonly scope: ComputedCacheScope;
9028
+ readonly volatile?: boolean | undefined;
9029
+ }
9030
+
9031
+ /** A query's declared `cache:`, parsed — `undefined` when the ttl does not
9032
+ * parse (the cache is then off for that query; `voltro doctor` says so). */
9033
+ export declare const queryCachePolicy: (cache: {
9034
+ readonly ttl: number | string;
9035
+ readonly swr?: number | string | undefined;
9036
+ readonly scope: ComputedCacheScope;
9037
+ readonly volatile?: boolean | undefined;
9038
+ }) => ComputedQueryCachePolicy | undefined;
9039
+
8474
9040
  /**
8475
- * The two scopes, composed once, for every path that finalises a descriptor.
9041
+ * The read scopes, composed once, for every path that finalises a descriptor.
8476
9042
  *
8477
9043
  * **The primitives above were extracted so there is "no second copy that could
8478
9044
  * drift" — and then the COMPOSITION became the second copy.** `voltro dev`
@@ -8498,6 +9064,8 @@ export declare const pumpOutbox: <T>(outbox: SubscriptionOutbox<T>, sink: Outbox
8498
9064
  export declare interface QueryFinalizeOptions {
8499
9065
  readonly tenantScopedTables: ReadonlySet<string>;
8500
9066
  readonly softDeleteScopedTables: ReadonlySet<string>;
9067
+ /** Tables carrying expires(); hosts derive this set from the schema registry. */
9068
+ readonly expiresScopedTables?: ReadonlySet<string>;
8501
9069
  readonly observe?: {
8502
9070
  /** Before scoping — sees what the handler asked for. */
8503
9071
  readonly beforeScope?: (descriptor: QueryDescriptor, subject: {
@@ -8515,6 +9083,9 @@ export declare type QueryLike<R = Row_7> = QueryDescriptor<R> | {
8515
9083
  };
8516
9084
 
8517
9085
  export declare interface QueryProducerDeps<D> {
9086
+ readonly workloadMetrics?: ReturnType<typeof makeApiRoleWorkloadMetrics>;
9087
+ /** See {@link ComputedQueryCacheDeps}. */
9088
+ readonly computedCache?: ComputedQueryCacheDeps;
8518
9089
  readonly buildContext: (request: ServeRequestContext) => {
8519
9090
  readonly store: unknown;
8520
9091
  };
@@ -8827,6 +9398,19 @@ export declare interface ReactiveSocketTunables {
8827
9398
  * (unit tests, a bare library use). */
8828
9399
  export declare const reactiveSocketTunables: () => ReactiveSocketTunables;
8829
9400
 
9401
+ /** Bounded exact-incarnation read. Corruption/read failure is raised, never
9402
+ * flattened to no observations. Membership expiry does not delete this history. */
9403
+ export declare const readApiRoleWorkloadWindow: (store: DataStore, input: {
9404
+ readonly now: number;
9405
+ readonly replicaId: string;
9406
+ readonly startedAt: number;
9407
+ readonly role: string;
9408
+ readonly requiredWorkloads: ReadonlyArray<ApiWorkload>;
9409
+ }) => Promise<{
9410
+ buckets: ApiRoleWorkloadBucket[];
9411
+ assessment: ApiRoleWindowAssessment;
9412
+ }>;
9413
+
8830
9414
  export declare interface ReadClassification {
8831
9415
  /** Tables whose ROWS can reach the caller: the queried table and its joins. */
8832
9416
  readonly composed: ReadonlySet<string>;
@@ -8834,6 +9418,14 @@ export declare interface ReadClassification {
8834
9418
  readonly restricting: ReadonlySet<string>;
8835
9419
  }
8836
9420
 
9421
+ /** Every recorded lease, live or not. */
9422
+ export declare const readFieldKeyHolders: (store: DataStore) => Promise<ReadonlyArray<FieldKeyHolderRecord>>;
9423
+
9424
+ export declare const readFieldKeyReports: (store: DataStore) => Promise<FieldKeyReports>;
9425
+
9426
+ /** The recorded keys, without decrypting anything. */
9427
+ export declare const readFieldKeys: (store: DataStore) => Promise<ReadonlyArray<FieldKeyRow>>;
9428
+
8837
9429
  /** First-or-null for a boot DataStore or a query-only tuple source. It invokes
8838
9430
  * the supplied query path, preserving that path's filters and codecs. A raw
8839
9431
  * driver remains raw; this helper does not add request scoping. */
@@ -8973,6 +9565,12 @@ export declare const recordExternalMissed: (name: string, count?: number) => voi
8973
9565
 
8974
9566
  export declare const recordExternalTrigger: (name: string, outcome: "accepted" | "duplicate" | "noOccurrence" | "ahead" | "notExternal" | "unknownSchedule") => void;
8975
9567
 
9568
+ /** One open of a sealed value. `key` opened it (`ok`) or was named by it (`failed`). */
9569
+ export declare const recordFieldDecrypt: (key: string, location: string, outcome: "ok" | "failed") => void;
9570
+
9571
+ /** Record the last count or re-key — one row per kind, replaced each time. */
9572
+ export declare const recordFieldKeyReport: <K extends keyof FieldKeyReports>(store: DataStore, kind: K, report: NonNullable<FieldKeyReports[K]>["report"], at?: Date) => Promise<void>;
9573
+
8976
9574
  /**
8977
9575
  * Record one emitted event into the identity's ring. Snapshots RESET the ring
8978
9576
  * (the snapshot's revision becomes the new replay base — a client holding an
@@ -9103,76 +9701,32 @@ export declare const redactColumns: <R extends Row>(rows: ReadonlyArray<R>, reda
9103
9701
  /** Blank sensitive-looking columns. Returns a new object; null passes through. */
9104
9702
  export declare const redactRow: (row: Row_2 | null | undefined) => Row_2 | null;
9105
9703
 
9106
- /**
9107
- * Options for `redisRywPositionStore`. The store needs TWO Redis
9108
- * connections because `SUBSCRIBE` puts a client in a mode where only
9109
- * (P)SUB / UNSUB / PING / QUIT are valid — sharing one client across
9110
- * writes and the subscribe loop would deadlock. Pass the same client
9111
- * for both unless you've separated them yourself.
9112
- */
9704
+ /** Registry-owned command facade and one borrowed Pub/Sub consumer. */
9113
9705
  export declare interface RedisRywOptions {
9114
- /**
9115
- * Connection used for SET / DEL / GET fallback writes. Stays in
9116
- * normal command mode.
9117
- */
9118
- readonly client: Redis;
9119
- /**
9120
- * Separate connection dedicated to the SUBSCRIBE loop. Pass a fresh
9121
- * `client.duplicate()` of the main connection; the store does NOT
9122
- * duplicate internally because callers may want to manage the
9123
- * subscription connection's lifecycle separately.
9124
- */
9125
- readonly subscriber: Redis;
9126
- /**
9127
- * Redis key prefix — every entry stores under `${keyPrefix}:<subjectKey>`.
9128
- * Defaults to `'voltro:ryw'`. Override to namespace per-app when
9129
- * multiple deployments share a Redis.
9130
- */
9131
- readonly keyPrefix?: string;
9132
- /**
9133
- * Pub/sub channel name. Defaults to `${keyPrefix}:bus`. All nodes
9134
- * sharing an RYW state MUST agree on this name.
9135
- */
9706
+ readonly client: RespClient;
9707
+ readonly pubsub: RedisPubSub;
9708
+ /** Application/environment namespace. Required; never shared implicitly. */
9709
+ readonly keyPrefix: string;
9710
+ /** Defaults to `${keyPrefix}:bus`; all participating processes must agree. */
9136
9711
  readonly channel?: string;
9137
- /** Clock injection for tests. Defaults to `Date.now`. */
9138
9712
  readonly now?: () => number;
9139
- /**
9140
- * Called when a Redis-side error occurs (publish/setex/subscribe
9141
- * failure). RYW degrades gracefully — local writes still succeed,
9142
- * other instances just won't see this node's writes until the next
9143
- * one. Hook for observability; default is a console.warn.
9144
- */
9713
+ /** Local writes survive remote failures. Failed mutations are never replayed. */
9145
9714
  readonly onError?: (error: unknown, op: 'set' | 'delete' | 'subscribe' | 'parse') => void;
9146
9715
  }
9147
9716
 
9717
+ export declare interface RedisRywPositionStore extends RywPositionStore {
9718
+ /** Drain accepted writes and release this consumer, leaving shared sockets open. */
9719
+ readonly close: () => Promise<void>;
9720
+ }
9721
+
9148
9722
  /**
9149
- * Build a Redis-backed RYW store. Cross-instance coherent within one
9150
- * round-trip; sync read path against a local mirror that the
9151
- * SUBSCRIBE loop keeps warm. Lifecycle:
9152
- *
9153
- * - on `set(key, entry)`:
9154
- * 1. write to local map (immediate, sync)
9155
- * 2. fire-and-forget SETEX in Redis with millisecond TTL
9156
- * 3. fire-and-forget PUBLISH on the channel — peers update
9157
- * their local mirror without round-tripping Redis again
9158
- * - on `delete(key)`:
9159
- * 1. remove from local map
9160
- * 2. fire-and-forget DEL + PUBLISH
9161
- * - on `get(key)`:
9162
- * 1. read from local map; lazy-evict if expired
9163
- *
9164
- * The SUBSCRIBE loop receives self-publishes (Redis sends them back
9165
- * to the sender). Each entry carries a `senderId`; the local node
9166
- * filters out its own broadcasts so we don't double-apply.
9167
- *
9168
- * On boot the local mirror is EMPTY. A read for a subject whose write
9169
- * happened on a peer node before this node started will miss the
9170
- * mirror — but `ReplicatedDataStore` falls back to primary on
9171
- * "no position", so the worst-case is "one extra primary read on the
9172
- * very first request that touches a peer-written subject". After that
9173
- * read, this node's own writes propagate normally.
9723
+ * Subscribe before returning. Reads consult only the process-local mirror.
9724
+ * Writes update it synchronously, then enqueue PSETEX/DEL followed by PUBLISH.
9725
+ * Pub/Sub is eventual and cannot replay lost messages or rebuild a new mirror.
9726
+ * A missing position still follows replica routing under fallback and wait;
9727
+ * neither policy establishes cross-process read-your-writes before delivery.
9174
9728
  */
9175
- export declare const redisRywPositionStore: (options: RedisRywOptions) => RywPositionStore;
9729
+ export declare const redisRywPositionStore: (options: RedisRywOptions) => Effect.Effect<RedisRywPositionStore, KvError>;
9176
9730
 
9177
9731
  /** `__voltro.undo.redo` — re-apply a previously-undone invocation's forward
9178
9732
  * changes. Idempotent if not currently undone. */
@@ -9226,7 +9780,15 @@ export declare const registerConnectionStream: (clientId: number, interrupt: ()
9226
9780
  */
9227
9781
  export declare const registerDefaultTupleSource: (source: TupleSource) => boolean;
9228
9782
 
9229
- export declare const registerExternalizedTask: (task: ExternalizedTask) => void;
9783
+ /** Process-level callers share the same map even across duplicated modules. */
9784
+ export declare const registerExternalizedTask: (task: ExternalizedTask) => (() => void);
9785
+
9786
+ /**
9787
+ * Record a key ahead of the deploy that brings it, as `pending`, with its
9788
+ * canary — so the first boot that holds it verifies its material, and the
9789
+ * configuration without it still boots (nothing is sealed under a pending key).
9790
+ */
9791
+ export declare const registerFieldKey: (store: DataStore, keyring: FieldKeyring, keyId: string) => Promise<void>;
9230
9792
 
9231
9793
  export { registerRetention }
9232
9794
 
@@ -9323,6 +9885,37 @@ export declare interface RegistryTableLike {
9323
9885
  * request was. */
9324
9886
  export declare const rehydrateGuards: (serialized: ReadonlyArray<SerializedGuard>) => ReadonlyArray<AnyGuardSpec>;
9325
9887
 
9888
+ /**
9889
+ * Re-seal every envelope in one cell that is not under the active key, or
9890
+ * names no key, under the active key and the address it was sealed under.
9891
+ */
9892
+ export declare const rekeyCell: (text: string, keyring: FieldKeyring, context: CellContext) => CellRekey;
9893
+
9894
+ export declare interface RekeyReport {
9895
+ readonly active: string;
9896
+ readonly locations: ReadonlyArray<LocationRekey>;
9897
+ readonly unreadable: ReadonlyArray<UnreadableColumn>;
9898
+ }
9899
+
9900
+ /**
9901
+ * Re-key every discovered location under the keyring's active key. With
9902
+ * `dryRun`, count what would change and write nothing.
9903
+ */
9904
+ export declare const rekeySealedValues: (dialect: DialectId, keyring: FieldKeyring, declared: Iterable<unknown>, options?: {
9905
+ readonly dryRun?: boolean;
9906
+ readonly onProgress?: (location: LocationRekey) => void;
9907
+ /** Runs between reading a cell and writing it back — where another writer may change it. */
9908
+ readonly beforeWrite?: (cell: {
9909
+ readonly table: string;
9910
+ readonly column: string;
9911
+ readonly rowId: unknown;
9912
+ }) => Effect.Effect<void, unknown, SqlClient.SqlClient>;
9913
+ }) => Effect.Effect<{
9914
+ active: string;
9915
+ locations: LocationRekey[];
9916
+ unreadable: UnreadableColumn[];
9917
+ }, SqlError, SqlClient.SqlClient>;
9918
+
9326
9919
  /** The store slice `applyDeclaredRelations` needs. `columns` is the junction's
9327
9920
  * DERIVED reference pair — the truth the descriptor's declaration is checked
9328
9921
  * against before anything is written. */
@@ -9345,6 +9938,9 @@ export declare interface RelationTuple {
9345
9938
  readonly resourceId: string;
9346
9939
  }
9347
9940
 
9941
+ /** Give up this process's lease — on shutdown, so nobody waits for it. */
9942
+ export declare const releaseFieldKeyHolder: (store: DataStore, holderId: string) => Promise<void>;
9943
+
9348
9944
  /**
9349
9945
  * Release a claim back to `pending` (CAS `fired → pending`) — used when
9350
9946
  * the wake failed, so the next poll retries it (at-least-once wake). The
@@ -9435,6 +10031,7 @@ export declare interface ReplicaSelector {
9435
10031
  }
9436
10032
 
9437
10033
  export declare class ReplicatedDataStore implements DataStore {
10034
+ readonly integrityQuery?: NonNullable<DataStore['integrityQuery']>;
9438
10035
  private readonly primary;
9439
10036
  private readonly replicas;
9440
10037
  private readonly adapter;
@@ -9446,12 +10043,13 @@ export declare class ReplicatedDataStore implements DataStore {
9446
10043
  private readonly health;
9447
10044
  private readonly recordRouting?;
9448
10045
  private readonly now;
10046
+ private closing;
9449
10047
  constructor(options: ReplicatedDataStoreOptions);
9450
10048
  query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
9451
10049
  private routeToReplica;
9452
10050
  private routeWaitMode;
9453
10051
  insert(table: string, row: Row): Promise<Row>;
9454
- insertMany(table: string, rows: ReadonlyArray<Row>): Promise<ReadonlyArray<Row>>;
10052
+ insertMany(table: string, rows: ReadonlyArray<Row>, options?: InsertManyOptions): Promise<ReadonlyArray<Row>>;
9455
10053
  patchJson(table: string, primaryKey: string, path: string, value: unknown, additionalPatch?: Readonly<Record<string, unknown>>): Promise<Row | null>;
9456
10054
  update(table: string, primaryKey: string, patch: Readonly<Record<string, unknown>>): Promise<Row | null>;
9457
10055
  delete(table: string, primaryKey: string): Promise<boolean>;
@@ -9476,6 +10074,8 @@ export declare class ReplicatedDataStore implements DataStore {
9476
10074
  injectExternalChange(event: ChangeEvent): void;
9477
10075
  /** Forwarded from the primary — see DataStore.changeScope. */
9478
10076
  get changeScope(): 'local' | 'fleet';
10077
+ /** Drain the RYW consumer before closing every SQL pool; repeated close is shared. */
10078
+ close(gracePeriodMs?: number): Promise<void>;
9479
10079
  private stampPostWrite;
9480
10080
  private emitRouting;
9481
10081
  }
@@ -9557,8 +10157,8 @@ export declare interface ResendOptions {
9557
10157
  readonly attempts?: number;
9558
10158
  }
9559
10159
 
9560
- /** Exported for tests — the warn-once set is process-global by design. */
9561
- export declare const resetComputedQueryCacheWarnings: () => void;
10160
+ /** Test seam: back to the boot default. */
10161
+ export declare const resetComputedQueryCacheCaptureForTests: () => void;
9562
10162
 
9563
10163
  /** Test/dev-only — clear ALL per-connection state. Don't call from app code. */
9564
10164
  export declare const _resetConnectionCredentialsForTest: () => void;
@@ -9569,6 +10169,9 @@ export declare const resetDetachedForTest: () => void;
9569
10169
  /** Test seams for the tag-cache guard — the cache is module-private otherwise. */
9570
10170
  export declare const resetEventMetricTagCacheForTests: () => void;
9571
10171
 
10172
+ /** Test seam. */
10173
+ export declare const resetFieldDecryptCountsForTests: () => void;
10174
+
9572
10175
  /** Drop everything recorded. For tests; not called by the runtime. */
9573
10176
  export declare const resetObservedGraph: () => void;
9574
10177
 
@@ -9772,6 +10375,15 @@ export declare interface ResolvedWorkflowCallerContext extends WorkflowCallerCon
9772
10375
  readonly rowFilter: RowFilterScope;
9773
10376
  }
9774
10377
 
10378
+ /**
10379
+ * The keyring an environment configures, or `undefined` when it configures no
10380
+ * key at all. Refuses (`FieldKeyringConfigError`, never containing key material)
10381
+ * an unknown or missing active id, a duplicate id, one key under two ids,
10382
+ * `KEY` equal to `_PREVIOUS`, and a `KEY`/`_PREVIOUS` set beside `KEYS` that
10383
+ * `KEYS` does not contain.
10384
+ */
10385
+ export declare const resolveFieldKeyring: (env: Readonly<Record<string, string | undefined>>) => FieldKeyringConfig | undefined;
10386
+
9775
10387
  /**
9776
10388
  * Resolve the app's health block against the boot path's own readiness flag.
9777
10389
  *
@@ -10042,6 +10654,15 @@ export { RetentionSpec }
10042
10654
 
10043
10655
  export { retentionTtlMsFromEnv }
10044
10656
 
10657
+ /**
10658
+ * Retire a key: nothing may be sealed under it again, and a configuration may
10659
+ * drop it — the boot no longer asks for it. Refused for the active key, a key
10660
+ * nobody recorded, and a key a live process still seals new values under (it
10661
+ * has not adopted the new key yet). Whether VALUES are still sealed under it is
10662
+ * the caller's question to answer first (`voltro secret retire` counts them).
10663
+ */
10664
+ export declare const retireFieldKey: (store: DataStore, keyId: string, holder?: Pick<FieldKeyHolder, "now">) => Promise<void>;
10665
+
10045
10666
  /**
10046
10667
  * Drop the ISR cache entries for a path on every `voltro start` replica.
10047
10668
  *
@@ -10349,6 +10970,8 @@ export declare interface RpcConnection {
10349
10970
 
10350
10971
  export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
10351
10972
  readonly group: RpcGroup.RpcGroup<Rpcs>;
10973
+ /** Executable RPC surfaces for this replica; omitted retains both transports. */
10974
+ readonly workloadAdmission?: RpcWorkloadAdmission;
10352
10975
  readonly handlersLayer: Layer.Layer<Rpc.ToHandler<Rpcs>>;
10353
10976
  /** Optional handlers layer dedicated to the one-shot HTTP rpc surface
10354
10977
  * (`POST /rpc`, used by SSR web-router loaders). The HTTP JSON protocol
@@ -10544,6 +11167,14 @@ export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
10544
11167
  readonly security?: TransportSecurityOptions;
10545
11168
  }
10546
11169
 
11170
+ export declare interface RpcWorkloadAdmission {
11171
+ readonly rpc: boolean;
11172
+ readonly subscriptions: boolean;
11173
+ /** Event streams are independent of live query subscriptions. */
11174
+ readonly events?: boolean;
11175
+ readonly routes?: ReadonlyArray<string>;
11176
+ }
11177
+
10547
11178
  /**
10548
11179
  * Rules that apply to one change, or an empty list.
10549
11180
  *
@@ -10628,6 +11259,10 @@ export declare const runSingleNodeOrchestrator: (opts: SingleNodeOrchestratorOpt
10628
11259
  export declare const RUNTIME_NAME: "framework-runtime";
10629
11260
 
10630
11261
  export declare interface RuntimeContext {
11262
+ /** Effective incoming Cookie header for server-side signature verification.
11263
+ * Unverified private input; never return, log or persist it. Optional for
11264
+ * internal callers and cookie-less requests. */
11265
+ readonly cookieHeader?: string;
10631
11266
  /** Subscription / call subject — user, API key, service account, or anonymous. */
10632
11267
  readonly subject: Subject;
10633
11268
  /** Trace identifier — the active span's W3C trace id (32 hex) when
@@ -10761,6 +11396,8 @@ export declare interface RywPositionStore {
10761
11396
  delete(subjectKey: string): void;
10762
11397
  /** Approximate size for observability + tests. */
10763
11398
  size(): number;
11399
+ /** Release consumer resources when this store owns any. */
11400
+ readonly close?: () => Promise<void>;
10764
11401
  }
10765
11402
 
10766
11403
  /**
@@ -10933,6 +11570,8 @@ export declare type ScheduleHandler = (ctx: ScheduleContext) => void | Promise<v
10933
11570
  export declare type ScheduleOverlapPolicy = 'skip' | 'queue' | 'parallel';
10934
11571
 
10935
11572
  export declare interface SchedulerDeps {
11573
+ /** Active synchronous handler entry only; never await a Promise inside this measurement. */
11574
+ readonly measureSync?: <A>(run: () => A) => A;
10936
11575
  /** App-owned service environment; the signal interrupts Effect work on timeout. */
10937
11576
  readonly runEffect?: (effect: Extract<ReturnType<ScheduleHandler>, Effect.Effect<unknown, unknown, unknown>>, signal: AbortSignal) => Promise<unknown>;
10938
11577
  readonly store: DataStore;
@@ -11044,6 +11683,8 @@ export declare interface SchedulerDeps {
11044
11683
  }
11045
11684
 
11046
11685
  export declare interface SchedulerHandle {
11686
+ /** Completed startup and shutdown has not begun; not a successful-job claim. */
11687
+ isRunning(): boolean;
11047
11688
  /** Fire a schedule immediately, bypassing the cron clock + the
11048
11689
  * coordinator. Waits for handler completion and returns the recorded run id. */
11049
11690
  fireNow(name: string, trigger: ScheduleTrigger | 'manual'): Promise<string | undefined>;
@@ -11349,6 +11990,22 @@ declare interface ScopeCtx {
11349
11990
  /** The scope strings a UI shows ("ask someone with …"). */
11350
11991
  export declare const scopeStringsOf: (serialized: ReadonlyArray<SerializedGuard>) => ReadonlyArray<string>;
11351
11992
 
11993
+ /** One column that holds envelopes, and what is under which key in it. */
11994
+ export declare interface SealedLocation {
11995
+ readonly table: string;
11996
+ readonly column: string;
11997
+ /** The app declares it `.encrypted()` (a whole cell is one value), rather than a place found holding envelopes. */
11998
+ readonly declared: boolean;
11999
+ /** Cells holding at least one envelope. */
12000
+ readonly cells: number;
12001
+ /** Envelopes, counted per key id. */
12002
+ readonly byKey: Readonly<Record<string, number>>;
12003
+ /** Envelopes written before envelopes named their key. */
12004
+ readonly unnamed: number;
12005
+ /** Declared `.encrypted()` only: non-null cells that are not an envelope. */
12006
+ readonly plaintext: number;
12007
+ }
12008
+
11352
12009
  export declare interface SecretsBackend {
11353
12010
  /** Resolve a secret by key. `undefined` when absent. */
11354
12011
  readonly get: (key: string) => Promise<string | undefined>;
@@ -11488,6 +12145,8 @@ export declare type SerializedGuard = {
11488
12145
  readonly resource: string | null;
11489
12146
  } | {
11490
12147
  readonly kind: 'authenticated';
12148
+ } | {
12149
+ readonly kind: 'upgraded';
11491
12150
  };
11492
12151
 
11493
12152
  /**
@@ -11507,6 +12166,14 @@ export declare type ServeMutationCommitHook = (context: Parameters<ServeRpcInter
11507
12166
  }) => Effect.Effect<void, unknown>;
11508
12167
 
11509
12168
  export declare interface ServeRequestContext {
12169
+ /** Listener-owned identity; used only for measurement attribution here. */
12170
+ readonly connection?: {
12171
+ readonly transport: 'http' | 'websocket';
12172
+ };
12173
+ /** Effective incoming Cookie header for server-side signature verification.
12174
+ * Unverified private input; never return, log or persist it. Optional for
12175
+ * internal callers and cookie-less requests. */
12176
+ readonly cookieHeader?: string;
11510
12177
  readonly subject: unknown;
11511
12178
  readonly traceId: string;
11512
12179
  readonly spanId?: string;
@@ -11565,7 +12232,7 @@ export declare const serverOnlyLeaks: (query: AuditableQuery, serverOnlyByTable:
11565
12232
  /** A plugin interceptor — wraps the base run Effect (Effect-native chain). */
11566
12233
  export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown, never>, meta: {
11567
12234
  readonly tag: string;
11568
- readonly kind: 'mutation' | 'action' | 'query' | 'workflow';
12235
+ readonly kind: 'mutation' | 'action' | 'query' | 'workflow' | 'stream';
11569
12236
  readonly input: unknown;
11570
12237
  readonly subject: unknown;
11571
12238
  readonly traceId: string;
@@ -11781,8 +12448,14 @@ export declare interface SingleNodeOrchestratorOptions {
11781
12448
  export declare interface SnapshotCache {
11782
12449
  readonly wrap: (binding: SnapshotCacheBinding, tags: ReadonlyArray<string>, compute: () => Promise<ReadonlyArray<Row>>) => Promise<ReadonlyArray<Row>>;
11783
12450
  /** Write-through from the live recompute, so the cache is never staler
11784
- * than the freshest live subscriber. Fire-and-forget. */
11785
- readonly put: (binding: SnapshotCacheBinding, tags: ReadonlyArray<string>, rows: ReadonlyArray<Row>) => void;
12451
+ * than the freshest live subscriber. Fire-and-forget. `fence` is the
12452
+ * cache's invalidation position observed BEFORE the rows were read: a
12453
+ * later invalidation of any tag voids the write, so an earlier, slower
12454
+ * read can never overwrite a later one. */
12455
+ readonly put: (binding: SnapshotCacheBinding, tags: ReadonlyArray<string>, rows: ReadonlyArray<Row>, fence: number) => void;
12456
+ /** The cache's invalidation position — observed before a read whose rows
12457
+ * will be written through. */
12458
+ readonly fence: () => Promise<number>;
11786
12459
  }
11787
12460
 
11788
12461
  /**
@@ -11794,6 +12467,8 @@ export declare interface SnapshotCacheBinding {
11794
12467
  readonly key: string;
11795
12468
  readonly ttlMs: number;
11796
12469
  readonly swrMs: number | undefined;
12470
+ /** The query name its counters are kept under (`queryStats`, metrics). */
12471
+ readonly label?: string | undefined;
11797
12472
  }
11798
12473
 
11799
12474
  /**
@@ -11805,6 +12480,20 @@ export declare const snapshotCrdt: (store: CrdtHistoryStore, cell: CrdtCellRef &
11805
12480
  readonly createdBy?: string;
11806
12481
  }) => Promise<CrdtVersion | undefined>;
11807
12482
 
12483
+ /**
12484
+ * The entry a READ belongs to: the rpc layer's scoped key plus the descriptor
12485
+ * actually read.
12486
+ *
12487
+ * The rpc key names the query, its input and the scope (subject / tenant /
12488
+ * global) — but not what the caller may SEE. A registered row filter makes two
12489
+ * colleagues of one tenant read different rows through the same `'tenant'` key,
12490
+ * and keying by the rpc key alone served the first colleague's filtered rows to
12491
+ * the next one. The read descriptor carries every predicate the caller's
12492
+ * visibility added, so two readers share an entry exactly when they read the
12493
+ * same thing.
12494
+ */
12495
+ export declare const snapshotEntryFor: (binding: SnapshotCacheBinding, read: QueryDescriptor) => SnapshotCacheBinding;
12496
+
11808
12497
  /** Snapshot the whole registry, normalized to `MetricSample[]` — plus the
11809
12498
  * socket-backpressure counters (`subscriptionSocketMetrics.ts`), appended
11810
12499
  * HERE so every reader (Prometheus exporter, inspect Metrics panel) sees
@@ -11842,32 +12531,23 @@ export declare class SsrfBlockedError extends Error {
11842
12531
  */
11843
12532
  export declare const ssrfGuardHttpClientLayer: (policy?: SsrfPolicy) => Layer.Layer<HttpClient.HttpClient, never, HttpClient.HttpClient>;
11844
12533
 
11845
- export declare interface SsrfPolicy {
11846
- /**
11847
- * Hosts that bypass the guard. An entry may be an exact hostname
11848
- * (`billing.internal`), a suffix wildcard (`*.svc.cluster.local`), or a
11849
- * host:port (`127.0.0.1:8787`) when only one port should be reachable.
11850
- *
11851
- * This is the one escape hatch, and it is deliberately per-HOST rather than a
11852
- * boolean: "we call one internal service" and "we do not check URLs" are very
11853
- * different postures, and a boolean cannot tell them apart six months later.
11854
- */
11855
- readonly allowHosts?: ReadonlyArray<string>;
11856
- /** Max redirect hops to follow. Each one is revalidated. Default 5. */
11857
- readonly maxRedirects?: number;
11858
- }
12534
+ export { SsrfPolicy }
12535
+
12536
+ export { ssrfReason }
11859
12537
 
11860
12538
  /**
11861
- * Decide whether a URL may be fetched. Returns `null` when allowed, else the
11862
- * reason. Fails CLOSED: a URL that will not parse is blocked.
11863
- *
11864
- * DNS is deliberately NOT resolved — this is a synchronous check on the literal
11865
- * target, so a hostname that RESOLVES to a private address (DNS rebinding) is not
11866
- * caught here. Blocking the direct-IP vector is the high-severity, cheap half;
11867
- * rebinding needs network-layer egress control and is called out in the docs
11868
- * rather than silently implied.
12539
+ * Heartbeat every {@link FIELD_KEY_HEARTBEAT_MS} for as long as the process
12540
+ * runs. Logs a transition once — a key that became active, a key still waiting
12541
+ * and for whom, an active key this process does not hold — never every beat.
11869
12542
  */
11870
- export declare const ssrfReason: (rawUrl: string, policy?: SsrfPolicy) => string | null;
12543
+ export declare const startFieldKeyHolder: (store: DataStore, keyring: FieldKeyring, holder: FieldKeyHolder & {
12544
+ readonly intervalMs?: number;
12545
+ readonly log: {
12546
+ readonly info: (message: string) => void;
12547
+ readonly warn: (message: string) => void;
12548
+ readonly error: (message: string) => void;
12549
+ };
12550
+ }) => FieldKeyHolderHandle;
11871
12551
 
11872
12552
  /**
11873
12553
  * Start the rpc-based runtime server on Node. Composes:
@@ -12097,6 +12777,12 @@ export declare interface SubscriberDefinition {
12097
12777
  readonly table: string;
12098
12778
  /** Default `'any'`. */
12099
12779
  readonly on?: SubscribeOp | ReadonlyArray<Exclude<SubscribeOp, 'any'>>;
12780
+ /** Bind this invocation's event publisher to the trusted committed row's
12781
+ * tenant. No client input is available here. Omitted keeps system/global
12782
+ * publication. This does not change ctx.store's system identity or turn a
12783
+ * best-effort subscriber into durable delivery. Invalid results refuse the
12784
+ * handler rather than publishing into the global partition. */
12785
+ readonly eventTenant?: (event: SubscribeEvent) => string | null;
12100
12786
  /**
12101
12787
  * Run the handler ONCE PER CHANGE across the whole fleet, instead of once on
12102
12788
  * every replica that sees it.
@@ -12446,7 +13132,7 @@ export declare const tenantSelectionStore: (getStore: () => DataStore | undefine
12446
13132
  * a page. */
12447
13133
  export declare const TERMINAL_RUN_STATUSES: ReadonlySet<string>;
12448
13134
 
12449
- export declare type TimeBucket = 'minute' | 'hour' | 'day' | 'week' | 'month';
13135
+ export { TimeBucket }
12450
13136
 
12451
13137
  export declare interface TimelineEvent {
12452
13138
  /** Preserves the store's warning that images are not original commit images. */
@@ -12512,29 +13198,11 @@ export declare class TimelineRecorder {
12512
13198
  replay(table: string, asOfSeq: number, currentRows: ReadonlyArray<Row_2>): ReadonlyArray<Row_2>;
12513
13199
  }
12514
13200
 
12515
- /** Time window. `to` defaults to now. */
12516
- export declare interface TimeRange {
12517
- readonly from: Date;
12518
- readonly to?: Date;
12519
- }
13201
+ export { TimeRange }
12520
13202
 
12521
- export declare interface TimeseriesQuery extends AggregateQuery {
12522
- readonly bucket: TimeBucket;
12523
- }
13203
+ export { TimeseriesQuery }
12524
13204
 
12525
- export declare interface TopNQuery {
12526
- readonly event: string;
12527
- /** Property to group by (must be a string-valued property). */
12528
- readonly groupBy: string;
12529
- readonly metric: 'count' | {
12530
- readonly sum: string;
12531
- };
12532
- readonly n: number;
12533
- readonly range: TimeRange;
12534
- readonly filter?: PropertyFilter;
12535
- /** Tenant scope — injected by `useAnalytics()`; see {@link AggregateQuery.tenantId}. */
12536
- readonly tenantId?: string | null;
12537
- }
13205
+ export { TopNQuery }
12538
13206
 
12539
13207
  /** A row a mutation wrote, carried to rule evaluation. `row` is the POST-WRITE
12540
13208
  * state (insert result / update result). */
@@ -12986,7 +13654,11 @@ export declare class UndoStack<E> {
12986
13654
  */
12987
13655
  export declare const undoSurfaceEnabled: (env?: Record<string, string | undefined>) => boolean;
12988
13656
 
12989
- export declare const unregisterExternalizedTask: (name: string) => void;
13657
+ export declare interface UnreadableColumn {
13658
+ readonly table: string;
13659
+ readonly column: string;
13660
+ readonly reason: string;
13661
+ }
12990
13662
 
12991
13663
  /** One `source:` entry that resolves to no declared table. */
12992
13664
  export declare interface UnresolvedSource {
@@ -12999,6 +13671,8 @@ export declare interface UnresolvedSource {
12999
13671
  /** True when the name is in the `channel:` namespace: it named a reactivity
13000
13672
  * channel nobody declared, which is a different repair from a stale table. */
13001
13673
  readonly channel: boolean;
13674
+ /** Intentionally optional integration observation, retained in source. */
13675
+ readonly optional?: true;
13002
13676
  }
13003
13677
 
13004
13678
  /**
@@ -13010,6 +13684,7 @@ export declare interface UnresolvedSource {
13010
13684
  export declare const unresolvedSources: (procedures: ReadonlyArray<{
13011
13685
  readonly name: string;
13012
13686
  readonly source: string | ReadonlyArray<string> | undefined;
13687
+ readonly optionalSource?: ReadonlyArray<string>;
13013
13688
  }>, declared: ReadonlySet<string>) => ReadonlyArray<UnresolvedSource>;
13014
13689
 
13015
13690
  /** Thrown at boot; the message is the whole point, so it names the path and the
@@ -13091,6 +13766,27 @@ export declare type VerificationDeclared<T> = T & {
13091
13766
  readonly [WEBHOOK_VERIFICATION_PROPERTY]: WebhookVerification;
13092
13767
  };
13093
13768
 
13769
+ /**
13770
+ * Check the keyring against the database and record what is new; make the
13771
+ * database's active key the keyring's. Throws {@link FieldKeyringRefusal} when
13772
+ * the keyring cannot read this database's data.
13773
+ *
13774
+ * `tables` are the app's declared tables: a few sealed values per
13775
+ * `.encrypted()` column are opened as a sample, and nothing else is read.
13776
+ */
13777
+ export declare const verifyFieldKeyring: (store: DataStore, keyring: FieldKeyring, tables: Iterable<unknown>, holder?: FieldKeyHolder) => Promise<FieldKeyringVerification>;
13778
+
13779
+ /** Every location's envelopes, opened with `keyring` — after a re-key, a keyring with the active key alone. */
13780
+ export declare const verifySealedValues: (dialect: DialectId, keyring: FieldKeyring, declared: Iterable<unknown>) => Effect.Effect<{
13781
+ opened: number;
13782
+ unopened: {
13783
+ readonly table: string;
13784
+ readonly column: string;
13785
+ readonly count: number;
13786
+ }[];
13787
+ unreadable: UnreadableColumn[];
13788
+ }, SqlError, SqlClient.SqlClient>;
13789
+
13094
13790
  /** The pure core of live revocation: given a subject + action + a row-set,
13095
13791
  * return ONLY the rows the subject may still see. The reactive matcher calls
13096
13792
  * this on a permission-changing write and diffs against the prior visible set;
@@ -13168,6 +13864,19 @@ export declare const _voltroConnectionsTable: TableLike;
13168
13864
 
13169
13865
  export declare const _voltroCrdtVersionsTable: TableLike;
13170
13866
 
13867
+ /** The leases: which process holds which keys, and until when it counts —
13868
+ * rewritten by every heartbeat, so not reactive. */
13869
+ export declare const _voltroFieldKeyHoldersTable: TableLike;
13870
+
13871
+ /** The last count and the last re-key `voltro secret status` / `rekey` / `rotate`
13872
+ * ran — what the Governance panel shows as a rotation's progress, since the
13873
+ * app itself never scans every sealed value. Counts and locations, never a value. */
13874
+ export declare const _voltroFieldKeyReportsTable: TableLike;
13875
+
13876
+ /** Each field-encryption key id's state and canary — a framework table, created for every app.
13877
+ * Declared by its literal name: the data-transfer classification guard reads declarations. */
13878
+ export declare const _voltroFieldKeysTable: TableLike;
13879
+
13171
13880
  /** `_voltro_rebac_tuples` — the relationship tuples `can()` reads. One row =
13172
13881
  * "`subjectId` has `relation` on `<resourceType>:<resourceId>`". Grant by
13173
13882
  * inserting; revoke by deleting (a delete is the live-revocation trigger). */
@@ -13917,7 +14626,7 @@ export declare interface WorkflowsAppContext {
13917
14626
  * execution, or never to run — read once, without waiting. A queued handle
13918
14627
  * is resolved through its durable receipt, so the answer survives the
13919
14628
  * process that recorded the handle. */
13920
- readStart(handle: WorkflowRunHandle, options?: Pick<WorkflowWaitOptions, 'callerContext'>): Promise<WorkflowStartObservation>;
14629
+ readStart(handle: WorkflowRunHandle, options?: WorkflowRunOptions): Promise<WorkflowStartObservation>;
13921
14630
  /**
13922
14631
  * Set on the facade a mutation TRANSACTION hands out: every start it records
13923
14632
  * is provisional until the transaction commits, so nothing about a start can
@@ -14100,6 +14809,10 @@ export declare const wrapStoreWithBootCodec: (underlying: DataStore, dialectId:
14100
14809
 
14101
14810
  export declare const wrapStoreWithMixinBehaviour: (rawUnderlying: DataStore, ctx: StoreMiddlewareContext) => FluentStore;
14102
14811
 
14812
+ /** Writer errors propagate to the publisher, which owns retry/logging. A failed
14813
+ * history write must not be mistaken for a successfully recorded zero bucket. */
14814
+ export declare const writeApiRoleWorkloadBucket: (store: DataStore, bucket: ApiRoleWorkloadBucket) => Promise<void>;
14815
+
14103
14816
  export declare const writeReplicaObservation: (store: DataStore, kind: ReplicaObservationKind, payload: unknown) => Promise<void>;
14104
14817
 
14105
14818
  export { }