@voltro/protocol 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -8,6 +8,7 @@ import { RpcMiddleware } from '@effect/rpc';
8
8
  import { Schema } from 'effect';
9
9
  import { SqlClient } from '@effect/sql';
10
10
  import { Stream } from '@effect/rpc/RpcSchema';
11
+ import { VersionConflict } from '@voltro/database/wire';
11
12
 
12
13
  export declare interface ActionProcedureDescriptor<Name extends string, Input extends Schema.Schema.Any, Output extends Schema.Schema.Any, Error extends Schema.Schema.All> {
13
14
  readonly kind: 'action';
@@ -37,6 +38,9 @@ export declare interface ActionProcedureDescriptor<Name extends string, Input ex
37
38
  * `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
38
39
  * the only two shapes `security.defaultDeny` accepts. */
39
40
  readonly openAccess: string | undefined;
41
+ /** Only these apps of the project may call this procedure — see `service` on
42
+ * the definer's options. Absent → the app's own clients call it. */
43
+ readonly service: ServiceSpec | undefined;
40
44
  /** Opt this action into a public REST endpoint (innovation/11). */
41
45
  readonly publicApi: PublicApiSpec | undefined;
42
46
  /** Opt this action into the auto-synthesized agent toolset (innovation/07). */
@@ -80,6 +84,46 @@ export declare const ADMIN_SCOPE = "admin:full";
80
84
  * so the wording lives beside the semantics it describes. */
81
85
  export declare const advisoryResourceGuardWarning: (tags: ReadonlyArray<string>) => string;
82
86
 
87
+ /**
88
+ * What an UNAUTHENTICATED caller is — the app's decision, never the request's.
89
+ *
90
+ * A request that matches no auth strategy still resolves to a Subject, and
91
+ * that Subject's `tenantId` decides what every tenant-scoped table and every
92
+ * `openAccess` procedure answers. Left to the request, the `x-tenant` header
93
+ * would choose it: any client could read any tenant's public-per-tenant data,
94
+ * and a procedure that was `openAccess` for the tenant's MEMBERS would answer
95
+ * for whichever tenant the header named. So the policy is declared once, in
96
+ * `app.config.ts` (`auth.anonymous`), and `voltro serve` refuses to boot
97
+ * without it.
98
+ *
99
+ * - `'refuse'` — no unauthenticated callers at all: `Unauthenticated`.
100
+ * - `'no-tenant'` — the caller carries the empty tenant `''`: every
101
+ * tenant-scoped read is empty (fails closed), tables without `tenant()` and
102
+ * procedures that need no tenant answer. The default of the composer, and
103
+ * the right choice for an api whose public surface is token-shaped (an
104
+ * invite preview, a signed link).
105
+ * - `'header'` — the tenant is what the request's `x-tenant` header names; a
106
+ * request without it is refused. For public-per-tenant data (a marketing
107
+ * site per tenant, public listings by slug) — and ONLY that: every
108
+ * `openAccess` procedure and every table without `tenant()` is then public
109
+ * for a client-chosen tenant.
110
+ * - `{ tenantId }` — every unauthenticated caller is that ONE tenant, chosen
111
+ * here; the header is not consulted.
112
+ *
113
+ * NEVER `null`: a null tenant is a SYSTEM subject to the store and reads
114
+ * unscoped, across every tenant.
115
+ */
116
+ export declare type AnonymousPolicy = 'refuse' | 'no-tenant' | 'header' | {
117
+ readonly tenantId: string;
118
+ };
119
+
120
+ /** The development boot's convenience, which no app config can name: the
121
+ * header's tenant, else the dev tenant. `voltro dev` runs an undecided app
122
+ * with it and says so; `voltro serve` refuses instead. */
123
+ export declare type AnonymousPolicyOrDevTenant = AnonymousPolicy | {
124
+ readonly headerOrTenant: string;
125
+ };
126
+
83
127
  export declare const anonymousSubject: (tenantId: string | null, credentialRejected?: string) => Subject;
84
128
 
85
129
  /** The erased policy a descriptor carries (input generic dropped). */
@@ -89,9 +133,9 @@ export declare interface AnyApprovalPolicy {
89
133
  readonly reason?: string;
90
134
  }
91
135
 
92
- /** A guard entry is a scope check, a relationship check, an identity check, or
93
- * a declared no-check-needed decision. */
94
- export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec;
136
+ /** A guard entry is a scope check, a relationship check, an identity check, a
137
+ * declared no-check-needed decision, or a service-only decision. */
138
+ export declare type AnyCheckSpec = GuardCheckSpec | PolicyCheckSpec | AuthenticatedCheckSpec | OpenAccessSpec | ServiceAccessSpec;
95
139
 
96
140
  /** Any declared event, with the generics erased — for registries and audits. */
97
141
  export declare type AnyEventDescriptor = EventDescriptor<string, Schema.Schema.Any, Schema.Schema.Any>;
@@ -130,6 +174,21 @@ export declare const APIKEY_ISSUE_OTHER_SCOPE = "apikeys:issue:other";
130
174
  */
131
175
  export declare const APIKEY_ISSUE_SELF_SCOPE = "apikeys:issue:self";
132
176
 
177
+ export declare class ApiUnavailable extends ApiUnavailable_base {
178
+ }
179
+
180
+ declare const ApiUnavailable_base: Schema.TaggedErrorClass<ApiUnavailable, "ApiUnavailable", {
181
+ readonly _tag: Schema.tag<"ApiUnavailable">;
182
+ } & {
183
+ /** The binding name on the caller (`apis.<name>`). */
184
+ api: typeof Schema.String;
185
+ /** The rpc tag that was called. */
186
+ tag: typeof Schema.String;
187
+ /** Why no answer arrived. */
188
+ reason: Schema.Literal<["timeout", "transport", "closed"]>;
189
+ message: typeof Schema.String;
190
+ }>;
191
+
133
192
  /** The subject a decision produces, plus what the decision took away. `removed`
134
193
  * is never non-empty for a `grant`. */
135
194
  export declare interface AppliedScopes {
@@ -137,6 +196,9 @@ export declare interface AppliedScopes {
137
196
  readonly removed: ReadonlyArray<string>;
138
197
  }
139
198
 
199
+ /** Keep a pairing for the next boot; the app reads it when it builds its api config. */
200
+ export declare const applyPairing: (storage: PairingStorage, pairing: Pairing) => Promise<void>;
201
+
140
202
  export declare const applyRowPatch: (prev: ReadonlyArray<PatchRow>, patch: RowPatch, options?: ApplyRowPatchOptions) => ReadonlyArray<PatchRow>;
141
203
 
142
204
  /**
@@ -556,7 +618,7 @@ export declare interface AuthStrategy {
556
618
  readonly resolve: (input: AuthStrategyInput) => Promise<StrategyResolution> | StrategyResolution;
557
619
  /**
558
620
  * The bearer-token PREFIX this strategy claims, when it gates on one
559
- * (`'sk_'`, `'awb_'`). Declared so a collision is DETECTABLE.
621
+ * (`'sk_'`, `'acme_'`). Declared so a collision is DETECTABLE.
560
622
  *
561
623
  * Two strategies claiming the same prefix is not a harmless duplicate: the
562
624
  * chain is first-match-wins, so whichever runs first decides the Subject —
@@ -677,7 +739,7 @@ export declare type AuthSubjectGuardVerdict = {
677
739
  * `fresh` the caller runs the handler then calls `finish`/`fail`; on
678
740
  * `replay`/`conflict` it returns immediately without touching the handler.
679
741
  */
680
- export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, ttlMs: number, now: number) => Promise<IdempotencyOutcome>;
742
+ export declare const beginIdempotent: (store: IdempotencyStore, scope: string, key: string, ttlMs: number, now: number, inFlightLeaseMs?: number) => Promise<IdempotencyOutcome>;
681
743
 
682
744
  /**
683
745
  * A cross-table business rule was violated inside a mutation's transaction; the
@@ -754,9 +816,14 @@ export declare const checkGuards: (subject: Subject, guards: ReadonlyArray<AnyCh
754
816
  *
755
817
  * Returns a typed `ScopeError` naming the first unmet scope, or `null` when
756
818
  * every guard passes. The runtime calls this in the dispatch spine BEFORE the
757
- * executor (mutations: before the transaction opens).
819
+ * executor (mutations: before the transaction opens). A resolver or tuple
820
+ * source that answers `'missing'` turns the refusal into `NoSuchResource` for
821
+ * a signed-in caller — the first failing guard decides, so a scope refusal
822
+ * ahead of it is never upgraded into a disclosure.
758
823
  */
759
- export declare const checkGuardsEffect: (subject: Subject, guards: ReadonlyArray<AnyCheckSpec> | undefined, input: unknown, options?: GuardCheckOptions) => Effect.Effect<ScopeError | Unauthenticated | null>;
824
+ export declare const checkGuardsEffect: (subject: Subject, guards: ReadonlyArray<AnyCheckSpec> | undefined, input: unknown, options?: GuardCheckOptions) => Effect.Effect<ScopeError | Unauthenticated | NoSuchResource | null>;
825
+
826
+ export declare const clearPairing: (storage: PairingStorage) => Promise<void>;
760
827
 
761
828
  export declare interface ClientDescriptor {
762
829
  readonly kind: 'query' | 'mutation' | 'action' | 'stream' | 'workflow';
@@ -817,42 +884,17 @@ export declare interface ClientTarget {
817
884
  export declare const compareSurfaces: (mine: Readonly<Record<string, string>>, theirs: Readonly<Record<string, string>>) => SurfaceMismatch;
818
885
 
819
886
  export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrategy>, options?: {
820
- /** Fallback when no strategy matched. Default: returns the input's
821
- * tenant from `x-tenant` header or null. */
887
+ /** Fallback when no strategy matched. Replaces `anonymous` entirely: the
888
+ * function owns the anonymous subject AND the rejected-credential decision. */
822
889
  readonly fallback?: (input: AuthStrategyInput) => Subject;
823
890
  /** Hook for logging — called on every `failed` resolution. */
824
891
  readonly onStrategyFailed?: (event: {
825
892
  strategyId: string;
826
893
  reason: string;
827
894
  }) => void;
828
- /**
829
- * Reject anonymous callers that carry no tenant. When `true` and no
830
- * strategy matched AND no `x-tenant` header is present, the resolver
831
- * throws `Unauthenticated` instead of returning a null-tenant
832
- * anonymous Subject. Use it on multi-tenant apps where every call —
833
- * even an anonymous one — must be scoped to a tenant; without it a
834
- * tenant-less anonymous Subject can read across the whole DB on
835
- * tables that aren't `tenant()`-scoped. Ignored when a custom
836
- * `fallback` is supplied (the fallback owns that decision).
837
- */
838
- readonly anonymousTenantRequired?: boolean;
839
- /**
840
- * The ONE tenant every anonymous caller resolves to — chosen by the server,
841
- * never by the request. When set, the `x-tenant` header is not consulted:
842
- * a client naming its own tenant is exactly the thing a fixed anonymous
843
- * tenant exists to rule out. Ignored when `fallback` is supplied (that
844
- * function owns the anonymous subject).
845
- */
846
- readonly anonymousTenantId?: string;
847
- /**
848
- * The tenant an anonymous caller resolves to when its request names none
849
- * in `x-tenant`. The header still wins when present — this only replaces
850
- * the `null` tenant. It exists so a boot path can supply a default tenant
851
- * without a `fallback`: a `fallback` owns the rejected-credential decision,
852
- * this does not, so a presented-and-rejected credential is still refused.
853
- * Ignored when `anonymousTenantId` or `fallback` is set.
854
- */
855
- readonly anonymousTenantDefault?: string;
895
+ /** What an unauthenticated caller is — see {@link AnonymousPolicy}.
896
+ * Default `'no-tenant'`. Ignored when `fallback` is supplied. */
897
+ readonly anonymous?: AnonymousPolicyOrDevTenant;
856
898
  /**
857
899
  * The subject's memberships — the ONE source, strategy-independent. Read
858
900
  * only when a stored tenant selection exists for the subject and names a
@@ -1348,8 +1390,8 @@ export declare interface CoordinatedScheduleHandle {
1348
1390
  */
1349
1391
  export declare interface CoordinatedTaskOptions {
1350
1392
  /**
1351
- * Stop ticking entirely on an idle tick with no pending deadline, and come
1352
- * back only on `wake()`. Default `false`.
1393
+ * Stop ticking entirely on an idle tick that reports `nextDueInMs: null`
1394
+ * (nothing scheduled), and come back only on `wake()`. Default `false`.
1353
1395
  *
1354
1396
  * Pass `true` only when an arrival is GUARANTEED to call `wake()` — a change
1355
1397
  * subscription on the table your task drains, on a deployment where remote
@@ -1363,10 +1405,17 @@ export declare interface CoordinatedTaskOptions {
1363
1405
  export declare interface CoordinatedTickOutcome {
1364
1406
  /** `true` when the tick found nothing to do. Only an idle tick backs off. */
1365
1407
  readonly idle: boolean;
1366
- /** Milliseconds until the earliest deadline this task already knows about.
1367
- * Caps the backoff, so a task that is idle now but has something due in
1368
- * 400 ms is armed for 400 ms rather than for the idle ceiling. */
1369
- readonly nextDueInMs?: number;
1408
+ /** Milliseconds until the earliest deadline this task already knows about —
1409
+ * a retry, a re-poll, a lease running out. Caps the backoff, so a task that
1410
+ * is idle now but has something due in 400 ms is armed for 400 ms rather
1411
+ * than for the idle ceiling.
1412
+ *
1413
+ * `null` says "nothing is scheduled; new work can only arrive as a write",
1414
+ * and is the ONLY idle report that lets a `disarmWhenIdle` task stop
1415
+ * ticking. Leaving the field out backs off instead: work that comes due on
1416
+ * a clock writes nothing, so nothing would ever wake a task that disarmed
1417
+ * while holding it. */
1418
+ readonly nextDueInMs?: number | null;
1370
1419
  }
1371
1420
 
1372
1421
  /**
@@ -1379,11 +1428,33 @@ export declare interface CoordinatedTickOutcome {
1379
1428
  * because that is where the normalised decision lands, and because every
1380
1429
  * enforcement path reads the descriptor's array and nothing else.
1381
1430
  */
1382
- export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec>;
1431
+ export declare type DeclaredAccess<Input = unknown> = ReadonlyArray<AnyGuardSpec<Input> | OpenAccessSpec | ServiceAccessSpec>;
1383
1432
 
1384
1433
  /** Every channel declared in this process, by routing key. */
1385
1434
  export declare const declaredReactivityChannelKeys: () => ReadonlySet<string>;
1386
1435
 
1436
+ /**
1437
+ * Does this guard list name a resource — a relationship guard, or a scope guard
1438
+ * with a `resource` extractor? Exactly the guards whose resolver may answer
1439
+ * `'missing'`, so exactly the procedures whose wire union carries
1440
+ * {@link NoSuchResource}.
1441
+ */
1442
+ export declare const declaresResourceGuard: (guards: ReadonlyArray<AnyCheckSpec> | undefined) => boolean;
1443
+
1444
+ /**
1445
+ * How long a RUNNING claim holds without renewal.
1446
+ *
1447
+ * A claim used to go stale only after the replay window — 24 hours by default —
1448
+ * so a process that died inside a handler left its key `in_flight` for a day:
1449
+ * every retry was refused as "still in flight", including the retry of a
1450
+ * workflow step whose callee had simply restarted. A live handler renews its
1451
+ * claim every third of this (`holdIdempotentClaim`); a dead one stops, and the
1452
+ * next retry takes over after at most this long. A handler that blocks its event
1453
+ * loop longer than this can be taken over while still running — the same bound
1454
+ * every lease has.
1455
+ */
1456
+ export declare const DEFAULT_IN_FLIGHT_LEASE_MS = 30000;
1457
+
1387
1458
  /** Default cap on cached subjects before the cache sweeps + trims. */
1388
1459
  export declare const DEFAULT_SCOPE_CACHE_MAX_ENTRIES = 10000;
1389
1460
 
@@ -1418,6 +1489,26 @@ export declare const defineAction: <const Name extends string, Input extends Sch
1418
1489
  * openAccess: 'public pricing page — reads no caller data'
1419
1490
  */
1420
1491
  readonly openAccess?: string;
1492
+ /**
1493
+ * Make this a SERVICE procedure: callable only by the listed apps of this
1494
+ * project, and by nobody else — no browser session, no API key, no admin.
1495
+ *
1496
+ * service: { callers: ['worker'] }
1497
+ *
1498
+ * The caller is another app that binds this one under `apis:` and calls it as
1499
+ * `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
1500
+ * this app accepts under `serviceCallers`. It is its own access decision, so it
1501
+ * satisfies `security.defaultDeny` and cannot sit beside `guards:` or
1502
+ * `openAccess:`.
1503
+ *
1504
+ * The handler runs as the calling SERVICE: it reads across tenants and must
1505
+ * name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
1506
+ * shape for per-tenant work.
1507
+ *
1508
+ * Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
1509
+ * `rpcGroup.generated.ts`.
1510
+ */
1511
+ readonly service?: ServiceSpec;
1421
1512
  /** Table(s) this action READS. Declaring it is what lets `voltro check` know
1422
1513
  * the table is alive — without it, a table only an action touches reads as
1423
1514
  * an orphan. See `ActionProcedureDescriptor.source`. */
@@ -1631,6 +1722,26 @@ export declare const defineMutation: <const Name extends string, Input extends S
1631
1722
  * openAccess: 'public pricing page — reads no caller data'
1632
1723
  */
1633
1724
  readonly openAccess?: string;
1725
+ /**
1726
+ * Make this a SERVICE procedure: callable only by the listed apps of this
1727
+ * project, and by nobody else — no browser session, no API key, no admin.
1728
+ *
1729
+ * service: { callers: ['worker'] }
1730
+ *
1731
+ * The caller is another app that binds this one under `apis:` and calls it as
1732
+ * `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
1733
+ * this app accepts under `serviceCallers`. It is its own access decision, so it
1734
+ * satisfies `security.defaultDeny` and cannot sit beside `guards:` or
1735
+ * `openAccess:`.
1736
+ *
1737
+ * The handler runs as the calling SERVICE: it reads across tenants and must
1738
+ * name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
1739
+ * shape for per-tenant work.
1740
+ *
1741
+ * Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
1742
+ * `rpcGroup.generated.ts`.
1743
+ */
1744
+ readonly service?: ServiceSpec;
1634
1745
  /** Project this mutation as a public REST endpoint (innovation/11). */
1635
1746
  readonly publicApi?: PublicApiSpec;
1636
1747
  /** Expose this mutation as an agent tool (innovation/07). */
@@ -1764,6 +1875,26 @@ export declare const defineQuery: <const Name extends string, Input extends Sche
1764
1875
  * openAccess: 'public pricing page — reads no caller data'
1765
1876
  */
1766
1877
  readonly openAccess?: string;
1878
+ /**
1879
+ * Make this a SERVICE procedure: callable only by the listed apps of this
1880
+ * project, and by nobody else — no browser session, no API key, no admin.
1881
+ *
1882
+ * service: { callers: ['worker'] }
1883
+ *
1884
+ * The caller is another app that binds this one under `apis:` and calls it as
1885
+ * `ctx.apis.<name>.<tag>(input)`; it authenticates with the service credential
1886
+ * this app accepts under `serviceCallers`. It is its own access decision, so it
1887
+ * satisfies `security.defaultDeny` and cannot sit beside `guards:` or
1888
+ * `openAccess:`.
1889
+ *
1890
+ * The handler runs as the calling SERVICE: it reads across tenants and must
1891
+ * name a tenant to write into one — `ctx.storeForTenant(input.tenantId)` is the
1892
+ * shape for per-tenant work.
1893
+ *
1894
+ * Codegen emits it into `serviceGroup.generated.ts`, never into the browser's
1895
+ * `rpcGroup.generated.ts`.
1896
+ */
1897
+ readonly service?: ServiceSpec;
1767
1898
  /** Project this query as a public REST endpoint (innovation/11). */
1768
1899
  readonly publicApi?: PublicApiSpec;
1769
1900
  /** Expose this query as an agent tool (innovation/07). */
@@ -1933,6 +2064,20 @@ export declare const encodeRestError: (format: RestFormat, error: RestFormatErro
1933
2064
  */
1934
2065
  export declare const encodeRestSuccess: (format: RestFormat, value: unknown, ctx: RestFormatContext) => RestFormatResponse;
1935
2066
 
2067
+ /**
2068
+ * The text to SHOW for a failure value — its `message`, else its `_tag`, else
2069
+ * `String(value)`.
2070
+ *
2071
+ * Its partner above, for the same reason: every client surface hands over the
2072
+ * declared error as the plain `{ _tag, …fields, message }` object the wire
2073
+ * carries, and `String()` of a plain object is `[object Object]`. A
2074
+ * subscription used to hand over an Effect `Cause`, whose `String()` happened
2075
+ * to read, so `String(error)` in an error banner or an `error.tsx` looked right
2076
+ * until the value became what the docs always said it was. Works on an `Error`,
2077
+ * a `Schema.TaggedError` instance, the wire object and a bare string alike.
2078
+ */
2079
+ export declare const errorMessage: (err: unknown) => string;
2080
+
1936
2081
  /**
1937
2082
  * Read the `_tag` off a thrown error value, or `undefined` when it has none.
1938
2083
  *
@@ -2308,9 +2453,10 @@ export declare const finishIdempotent: (store: IdempotencyStore, scope: string,
2308
2453
  *
2309
2454
  * A query's success type is the three-member union above, and a one-shot read
2310
2455
  * takes only its first event: a `snapshot` resolves to its `data`; an `error`
2311
- * REJECTS — with the typed error on `cause` and `voltroError`, so a caller can
2312
- * still branch on its `_tag` — exactly as a failed exit would; a `delta` cannot
2313
- * be first and says so. Anything else is a mutation or action's own result and
2456
+ * REJECTS with the typed error ITSELF — exactly as a failed exit does, and the
2457
+ * same value a live subscription's `error` and the api's error bus hand over,
2458
+ * so one `_tag` switch serves every surface; a `delta` cannot be first and says
2459
+ * so. Anything else is a mutation or action's own result and
2314
2460
  * passes through, which may legitimately be a tagged object of the app's.
2315
2461
  *
2316
2462
  * Shared because there are two such reads — the server loader's `ctx.query`
@@ -2326,6 +2472,31 @@ export declare const firstEventValue: (tag: string, value: unknown, label: strin
2326
2472
  /** A route rendered for humans — logs, the inspect surface, the dashboard. */
2327
2473
  export declare const formatEventRoute: (route: string) => string;
2328
2474
 
2475
+ /**
2476
+ * The framework errors EVERY procedure of a kind carries on its wire union —
2477
+ * the unconditional members `wireErrorUnion` adds. The conditional ones (a
2478
+ * resource guard's `NoSuchResource`, `requiresApproval`'s refusals,
2479
+ * `MutationConnectionRequired`) are left out on purpose: a type that cannot see
2480
+ * the condition must not promise them.
2481
+ *
2482
+ * It exists for the TYPE-level reader, `defineExecutor`, which checks an
2483
+ * executor's failure channel against the union the server will judge it by. A
2484
+ * test pins it against `wireErrorUnion` per kind, so the two cannot drift.
2485
+ */
2486
+ export declare const FRAMEWORK_WIRE_ERRORS: {
2487
+ readonly query: readonly [typeof ScopeError, typeof Unauthenticated];
2488
+ readonly stream: readonly [typeof ScopeError, typeof Unauthenticated];
2489
+ readonly action: readonly [typeof ScopeError, typeof Unauthenticated, typeof ValidationError, typeof ValidationErrors];
2490
+ readonly mutation: readonly [typeof ScopeError, typeof Unauthenticated, typeof BusinessRuleViolation, typeof MutationInFlight, typeof VersionConflict, typeof ValidationError, typeof ValidationErrors];
2491
+ };
2492
+
2493
+ /** The error values {@link FRAMEWORK_WIRE_ERRORS} admits for `Kind`. */
2494
+ export declare type FrameworkWireError<Kind extends WireProcedureKind> = Schema.Schema.Type<(typeof FRAMEWORK_WIRE_ERRORS)[Kind][number]>;
2495
+
2496
+ /** How long a record of this status stays fresh — the replay window for a
2497
+ * completed response, the lease for a running one (never longer than the window). */
2498
+ export declare const freshFor: (status: IdempotencyRecord["status"], ttlMs: number, inFlightLeaseMs: number) => number;
2499
+
2329
2500
  /** Close code a gateway connection receives when its credential expires —
2330
2501
  * the same session-expiry contract the rpc socket has (SEC-16), spelled as
2331
2502
  * an application close code so foreign clients can reauth + reconnect. */
@@ -2448,6 +2619,9 @@ export declare const hasEnforcedGuard: (guards: DeclaredAccess | undefined) => b
2448
2619
  * the RAW subject scopes — use `hasEffectiveScope` to include rbac roles. */
2449
2620
  export declare const hasScope: (subject: Subject, scope: string) => boolean;
2450
2621
 
2622
+ /** Renew a fresh claim's lease while its handler runs; returns the stop. */
2623
+ export declare const holdIdempotentClaim: (store: IdempotencyStore, scope: string, key: string, inFlightLeaseMs?: number, now?: () => number) => (() => void);
2624
+
2451
2625
  /**
2452
2626
  * Short-circuit response shape. Returning a `HttpInterceptResponse`
2453
2627
  * from the interceptor halts the pipeline — the framework sends the
@@ -2547,14 +2721,24 @@ export declare interface IdempotencyStore {
2547
2721
  /** Current record for (scope, key), or null. Read-side (inspect/dashboard). */
2548
2722
  readonly get: (scope: string, key: string) => Promise<IdempotencyRecord | null>;
2549
2723
  /**
2550
- * Atomically claim (scope, key) IF absent OR stale (older than `ttlMs`):
2724
+ * Atomically claim (scope, key) IF absent OR stale — a COMPLETED record older
2725
+ * than `ttlMs` (the replay window), or an IN-FLIGHT one whose lease
2726
+ * (`inFlightLeaseMs`, renewed by the live handler) lapsed:
2551
2727
  * write a fresh `in_flight` row and return `'claimed'`. Otherwise return the
2552
2728
  * EXISTING (still-fresh) record. The atomicity here is the whole game — two
2553
2729
  * concurrent same-key requests both hit this, exactly one gets `'claimed'`,
2554
2730
  * the loser reads the winner's record. In SQL this is one
2555
2731
  * `INSERT … ON CONFLICT DO UPDATE … WHERE existing.createdAt < now - ttl`.
2556
2732
  */
2557
- readonly claim: (scope: string, key: string, now: number, ttlMs: number) => Promise<'claimed' | IdempotencyRecord>;
2733
+ readonly claim: (scope: string, key: string, now: number, ttlMs: number, inFlightLeaseMs: number) => Promise<'claimed' | IdempotencyRecord>;
2734
+ /**
2735
+ * Extend a RUNNING claim's lease: bump its timestamp while it is still
2736
+ * `in_flight` (a completed or released claim is left alone). The server calls
2737
+ * it while the handler runs; a process that dies stops calling it, and its
2738
+ * claim goes stale after `inFlightLeaseMs` instead of after the whole replay
2739
+ * window.
2740
+ */
2741
+ readonly renew: (scope: string, key: string, now: number) => Promise<void>;
2558
2742
  /** Flip the claim to `completed` and store the response. */
2559
2743
  readonly complete: (scope: string, key: string, response: IdempotencyResponse, now: number) => Promise<void>;
2560
2744
  /** Drop the claim (handler errored → a retry may re-process). */
@@ -2637,12 +2821,21 @@ export declare interface InsertTarget<Input = unknown, Row = unknown, Item = Rec
2637
2821
  readonly shapeItem?: ((input: Input, optimisticId: string) => Item) | undefined;
2638
2822
  }
2639
2823
 
2824
+ /** Is this failure a missing answer (retry it) rather than an answer (do not)? */
2825
+ export declare const isApiUnavailable: (error: unknown) => error is ApiUnavailable;
2826
+
2640
2827
  /** Runtime narrowing for the identity variant. */
2641
2828
  export declare const isAuthenticatedCheck: (g: AnyCheckSpec) => g is AuthenticatedCheckSpec;
2642
2829
 
2643
2830
  /** Narrow a guard entry to the identity variant. */
2644
2831
  export declare const isAuthenticatedGuard: <I>(g: AnyGuardSpec<I>) => g is AuthenticatedGuardSpec;
2645
2832
 
2833
+ /** Belongs in the app's own client group (`rpcGroup.generated.ts`). */
2834
+ export declare const isClientReachable: (descriptor: {
2835
+ readonly internal?: boolean | undefined;
2836
+ readonly service?: ServiceSpec | undefined;
2837
+ }) => boolean;
2838
+
2646
2839
  export declare const isEventDescriptor: (value: unknown) => value is AnyEventDescriptor;
2647
2840
 
2648
2841
  /** True when every row in the set carries an `id`. The dispatcher uses
@@ -2651,6 +2844,17 @@ export declare const isEventDescriptor: (value: unknown) => value is AnyEventDes
2651
2844
  * diffed by id and falls back to shipping the full data. */
2652
2845
  export declare const isIdKeyed: (rows: ReadonlyArray<Readonly<Record<string, unknown>>>) => rows is ReadonlyArray<PatchRow>;
2653
2846
 
2847
+ /**
2848
+ * A machine actor that works ACROSS tenants: the framework acting as itself
2849
+ * (`system`) or another app of the project calling in (`service`).
2850
+ *
2851
+ * This is a statement about DATA placement only — no row filter, no implicit
2852
+ * tenant, an explicit tenant for writes. It says nothing about authority: a
2853
+ * service holds no scopes, and every place that lets `system` bypass a check
2854
+ * keeps asking `isSystemSubject`.
2855
+ */
2856
+ export declare const isMachineSubject: (subject: Subject) => boolean;
2857
+
2654
2858
  /** Runtime narrowing for the declared-open variant. */
2655
2859
  export declare const isOpenAccess: (g: AnyCheckSpec) => g is OpenAccessSpec;
2656
2860
 
@@ -2676,6 +2880,20 @@ export declare const isReactivityChannel: (value: unknown) => value is Reactivit
2676
2880
  */
2677
2881
  export declare const isReactivityChannelKey: (key: string) => boolean;
2678
2882
 
2883
+ /** Runtime narrowing for the service-only variant. */
2884
+ export declare const isServiceAccess: (g: AnyCheckSpec) => g is ServiceAccessSpec;
2885
+
2886
+ /** Belongs in the group other apps call (`serviceGroup.generated.ts`). */
2887
+ export declare const isServiceReachable: (descriptor: {
2888
+ readonly internal?: boolean | undefined;
2889
+ readonly service?: ServiceSpec | undefined;
2890
+ }) => boolean;
2891
+
2892
+ /** Is this caller another app of the project, and which one? */
2893
+ export declare const isServiceSubject: (subject: Subject) => subject is Extract<Subject, {
2894
+ readonly type: "service";
2895
+ }>;
2896
+
2679
2897
  export declare const isSystemSubject: (subject: Subject) => boolean;
2680
2898
 
2681
2899
  export declare const isTenantSelectable: (subject: Subject) => subject is TenantSelectableSubject;
@@ -2807,6 +3025,9 @@ export declare interface MutationProcedureDescriptor<Name extends string, Input
2807
3025
  * `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
2808
3026
  * the only two shapes `security.defaultDeny` accepts. */
2809
3027
  readonly openAccess: string | undefined;
3028
+ /** Only these apps of the project may call this procedure — see `service` on
3029
+ * the definer's options. Absent → the app's own clients call it. */
3030
+ readonly service: ServiceSpec | undefined;
2810
3031
  /** Opt this mutation into a public REST endpoint (innovation/11). */
2811
3032
  readonly publicApi: PublicApiSpec | undefined;
2812
3033
  /** Opt this mutation into the auto-synthesized agent toolset (innovation/07). */
@@ -2862,7 +3083,7 @@ export declare interface NestedTargetFields<Input = unknown> {
2862
3083
  /** Guard WHICH cached query entries this target patches: only entries whose
2863
3084
  * CURRENT value satisfies the predicate. Pure + browser-safe. Prevents a
2864
3085
  * patch from bleeding across sibling subscriptions that share a source table
2865
- * (the guard AWB hand-writes as `roadmap.id === input.roadmapId`). Absent →
3086
+ * (the guard an app would otherwise hand-write as `roadmap.id === input.roadmapId`). Absent →
2866
3087
  * every entry matching the target `table` is patched. */
2867
3088
  readonly match?: ((value: unknown, input: Input) => boolean) | undefined;
2868
3089
  }
@@ -2917,6 +3138,32 @@ declare const NoRowFound_base: Schema.TaggedErrorClass<NoRowFound, "NoRowFound",
2917
3138
  found: typeof Schema.Number;
2918
3139
  }>;
2919
3140
 
3141
+ /**
3142
+ * A resource guard's refusal when the resource it names does not exist — as
3143
+ * far as THIS caller may know.
3144
+ *
3145
+ * Only an app can say that. The guard never infers absence from an empty tuple
3146
+ * set or a `false` answer: "no relation" and "no row" look identical from the
3147
+ * outside, and telling them apart is an existence oracle. So this is raised
3148
+ * only when a tuple source or resolver answers `'missing'`
3149
+ * ({@link ResourceMissing}), and only for a signed-in caller — an anonymous one
3150
+ * gets the ordinary refusal whatever the source said.
3151
+ *
3152
+ * `resourceType` is the relationship guard's type, or `null` for a scope guard
3153
+ * with a `resource` extractor (which names no type). Merged into the wire union
3154
+ * of every procedure that declares a resource-bearing guard.
3155
+ */
3156
+ export declare class NoSuchResource extends NoSuchResource_base {
3157
+ }
3158
+
3159
+ declare const NoSuchResource_base: Schema.TaggedErrorClass<NoSuchResource, "NoSuchResource", {
3160
+ readonly _tag: Schema.tag<"NoSuchResource">;
3161
+ } & {
3162
+ resourceType: Schema.NullOr<typeof Schema.String>;
3163
+ resourceId: typeof Schema.String;
3164
+ message: typeof Schema.String;
3165
+ }>;
3166
+
2920
3167
  /**
2921
3168
  * What a plugin contributes to the OTel layer via `contributeObservability`.
2922
3169
  * The OTel types are intentionally `unknown` so this browser-safe protocol
@@ -3001,6 +3248,38 @@ export declare const openAccessSpec: (reason: string) => OpenAccessSpec;
3001
3248
  */
3002
3249
  export declare const orderByBearerPrefix: (strategies: ReadonlyArray<AuthStrategy>, headers: Readonly<Record<string, string | undefined>>) => ReadonlyArray<AuthStrategy>;
3003
3250
 
3251
+ export declare interface Pairing {
3252
+ /** The api's base url as the DEVICE reaches it — a LAN address, never `localhost`. */
3253
+ readonly api: string;
3254
+ /** The api process name (`main`), for the client's api binding. */
3255
+ readonly name: string;
3256
+ /** The dev inspect token, when the CLI knows it. */
3257
+ readonly token?: string;
3258
+ }
3259
+
3260
+ export declare const PAIRING_HOST = "pair";
3261
+
3262
+ export declare const PAIRING_SCHEME = "voltro-dev";
3263
+
3264
+ /** Where `applyPairing` keeps the record — the platform's plain storage. */
3265
+ export declare const PAIRING_STORAGE_KEY = "voltro:pairing";
3266
+
3267
+ /** The link text. Refuses a loopback api: a phone cannot reach the laptop's `localhost`. */
3268
+ export declare const pairingLink: (pairing: Pairing) => string;
3269
+
3270
+ /** A key/value store in either spelling: the platform's `StoreStorage`
3271
+ * (`getItem`/`setItem`, what `platform.storage('local')` returns) or a
3272
+ * `get`/`set` pair (a secure store, a test fake). */
3273
+ export declare type PairingStorage = {
3274
+ readonly get: (key: string) => Promise<string | null> | string | null;
3275
+ readonly set: (key: string, value: string) => Promise<void> | void;
3276
+ readonly remove?: (key: string) => Promise<void> | void;
3277
+ } | {
3278
+ readonly getItem: (key: string) => Promise<string | null> | string | null;
3279
+ readonly setItem: (key: string, value: string) => Promise<void> | void;
3280
+ readonly removeItem?: (key: string) => Promise<void> | void;
3281
+ };
3282
+
3004
3283
  /** Split a route back into its three parts. */
3005
3284
  export declare const parseEventRoute: (route: string) => {
3006
3285
  readonly tenantId: string | null;
@@ -3008,6 +3287,11 @@ export declare const parseEventRoute: (route: string) => {
3008
3287
  readonly key: string;
3009
3288
  };
3010
3289
 
3290
+ /** The record from a link, or `null` for anything that is not a pairing link. */
3291
+ export declare const parsePairingLink: (url: string) => Pairing | null;
3292
+
3293
+ export declare const patchCellSchema: Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>;
3294
+
3011
3295
  /** A single row in a subscription result. Must carry an `id`; everything
3012
3296
  * else is opaque to the patch layer. */
3013
3297
  export declare type PatchRow = Readonly<Record<string, unknown>> & {
@@ -3378,16 +3662,29 @@ export declare interface PluginDashboardMount {
3378
3662
  export declare type PluginDeactivateHook = (ctx: PluginLifecycleContext) => Effect.Effect<void, unknown> | Promise<void> | void;
3379
3663
 
3380
3664
  /**
3381
- * An environment variable a plugin reads. DECLARATION ONLY — metadata, not a
3382
- * read path: the plugin still reads the value itself (typically
3383
- * `options.X ?? process.env.X`). Declaring it makes the plugin's env needs
3384
- * visible to the env manifest (`/_voltro/inspect/env`), the generated
3385
- * `.env.example`, and the dashboard Env panel — so an operator can see, before
3386
- * running, exactly what each plugin expects.
3387
- *
3388
- * Structurally identical to `@voltro/env`'s `DeclaredEnvVar` (kept local here
3389
- * to avoid a protocol → env package dependency).
3665
+ * One place a plugin sends data outside the installation — folded by
3666
+ * `voltro compliance generate` into `egress.json`, the machine-readable list an
3667
+ * operator reviews before opening a firewall, and answers an air-gap question
3668
+ * from. Declare what the plugin CAN reach with the options it was given: a
3669
+ * storage plugin on the database provider reaches nothing; on s3 it reaches
3670
+ * the bucket's endpoint.
3390
3671
  */
3672
+ export declare interface PluginEgressDeclaration {
3673
+ /**
3674
+ * Where: a host (`api.example.com`), a URL, or — when the address is data,
3675
+ * not configuration — what decides it, prefixed `runtime:`
3676
+ * (`runtime:webhook targets`).
3677
+ */
3678
+ readonly destination: string;
3679
+ /** Why the data goes there, in one line. */
3680
+ readonly purpose: string;
3681
+ /** What leaves: `content` (what users write), `personal` (who someone is),
3682
+ * `files`, `metadata` (ids, names, timestamps), `telemetry`, `credentials`. */
3683
+ readonly data: ReadonlyArray<'content' | 'personal' | 'files' | 'metadata' | 'telemetry' | 'credentials'>;
3684
+ /** How an operator turns it off, and what the installation does then. */
3685
+ readonly disable: string;
3686
+ }
3687
+
3391
3688
  export declare interface PluginEnvVar {
3392
3689
  /** The env var name, e.g. `DD_API_KEY`. */
3393
3690
  readonly name: string;
@@ -3452,11 +3749,17 @@ export declare interface PluginErrorSchema {
3452
3749
 
3453
3750
  /** A public raw-HTTP route a plugin serves on the framework listener. */
3454
3751
  export declare interface PluginHttpRoute {
3455
- /** HTTP method, or `'*'` for any (the handler decides). PATCH/HEAD/OPTIONS
3456
- * are first-class — the REST desugar used to mount `'*'` partly BECAUSE
3457
- * this union lacked PATCH; that reason is gone (the `'*'` mount remains
3458
- * for its other job: one dispatcher per shared path + a precise 405). */
3752
+ /** HTTP method, or `'*'` for any (the handler decides). The server mounts the
3753
+ * path for exactly the methods its routes declare; `'*'` mounts it for every
3754
+ * method the router knows (34), so keep that for a route that really serves
3755
+ * them — see `answersOtherMethods` for one that only wants to refuse them. */
3459
3756
  readonly method: '*' | 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
3757
+ /** `true`: the handler gates its own method, so a request with a method the
3758
+ * path does not declare is still handed to it — to answer with a precise
3759
+ * `405` and `Allow` instead of the router's `404`. The path is still mounted
3760
+ * only for `method`. A REST route sets it. Without it, a route that declares
3761
+ * one method never sees another. */
3762
+ readonly answersOtherMethods?: boolean | undefined;
3460
3763
  /** Absolute path, e.g. `/_voltro/storage`. Matched byte for byte: the
3461
3764
  * router folds case and a trailing slash to FIND the route, the dispatcher
3462
3765
  * then asks whether the request's path is one the route declared — so
@@ -3947,6 +4250,14 @@ export declare type PluginMigrationSqlClient = SqlClient.SqlClient;
3947
4250
  */
3948
4251
  export declare type PluginPermission = 'rpc:intercept:mutation' | 'rpc:intercept:query' | 'rpc:intercept:action' | 'rpc:intercept:workflow' | 'schedule:fire' | 'schedule:read' | 'workflow:step' | 'http:intercept' | 'inspect:read' | 'inspect:write' | 'dashboard:mount' | 'store:write' | 'migration:run' | 'store:changes:read' | `secrets:read:${string}` | `network:outbound:${string}` | `plugin:hook:${string}`;
3949
4252
 
4253
+ /** What a plugin collects and uses — folded by `voltro compliance generate`
4254
+ * into PrivacyInfo.xcprivacy and the Play Data-safety answers. A plugin that
4255
+ * moves data (routes, tables) and declares nothing is listed by name. */
4256
+ export declare interface PluginPrivacyDeclaration {
4257
+ readonly collects?: ReadonlyArray<PrivacyCollectedData>;
4258
+ readonly requiredReasonApis?: ReadonlyArray<PrivacyRequiredReasonApi>;
4259
+ }
4260
+
3950
4261
  /**
3951
4262
  * Per-call context handed to a plugin-contributed rpc route's
3952
4263
  * executor. Mirror of the user-handler `AppContext` shape but typed
@@ -4221,9 +4532,10 @@ export declare interface PolicyGuardRequest {
4221
4532
  * Answers "does `subject` have `action` on `<resourceType>:<resourceId>`".
4222
4533
  * Registered once at boot via `setPolicyGuardResolver`. A failed Effect is
4223
4534
  * treated as a DENIAL — a resolver wanting another posture catches its own
4224
- * errors.
4535
+ * errors. Only `true` allows; `'missing'` refuses with {@link NoSuchResource}
4536
+ * for a signed-in caller.
4225
4537
  */
4226
- export declare type PolicyGuardResolver = (req: PolicyGuardRequest) => Effect.Effect<boolean, unknown>;
4538
+ export declare type PolicyGuardResolver = (req: PolicyGuardRequest) => Effect.Effect<ResourceVerdict, unknown>;
4227
4539
 
4228
4540
  /**
4229
4541
  * A relationship (ReBAC) guard — "may this subject perform ACTION on THIS row?"
@@ -4255,6 +4567,38 @@ export declare interface PolicyGuardSpec<Input = unknown> {
4255
4567
  readonly resource: (input: Input) => string | undefined;
4256
4568
  }
4257
4569
 
4570
+ /**
4571
+ * An environment variable a plugin reads. DECLARATION ONLY — metadata, not a
4572
+ * read path: the plugin still reads the value itself (typically
4573
+ * `options.X ?? process.env.X`). Declaring it makes the plugin's env needs
4574
+ * visible to the env manifest (`/_voltro/inspect/env`), the generated
4575
+ * `.env.example`, and the dashboard Env panel — so an operator can see, before
4576
+ * running, exactly what each plugin expects.
4577
+ *
4578
+ * Structurally identical to `@voltro/env`'s `DeclaredEnvVar` (kept local here
4579
+ * to avoid a protocol → env package dependency).
4580
+ */
4581
+ /** One kind of data an app or plugin collects, in the vocabulary both stores
4582
+ * understand (`voltro compliance generate` maps it to Apple's and Google's). */
4583
+ export declare interface PrivacyCollectedData {
4584
+ readonly category: 'name' | 'email' | 'phone' | 'physical-address' | 'identifiers' | 'device-id' | 'payment-info' | 'purchase-history' | 'precise-location' | 'coarse-location' | 'photos-or-videos' | 'audio' | 'user-content' | 'browsing-history' | 'search-history' | 'product-interaction' | 'crash-data' | 'performance-data' | 'other-diagnostic-data' | 'other-data';
4585
+ /** Linked to the user's identity. */
4586
+ readonly linked: boolean;
4587
+ /** Used to track the user across apps or sites (advertising). */
4588
+ readonly tracking: boolean;
4589
+ readonly purposes: ReadonlyArray<'app-functionality' | 'analytics' | 'product-personalization' | 'developer-advertising' | 'third-party-advertising' | 'other'>;
4590
+ /** Play: shared with third parties. Default false. */
4591
+ readonly shared?: boolean;
4592
+ /** Play: the user can decline it. Default false. */
4593
+ readonly optional?: boolean;
4594
+ }
4595
+
4596
+ /** Apple's required-reason API use (`NSPrivacyAccessedAPICategory…` + reason codes). */
4597
+ export declare interface PrivacyRequiredReasonApi {
4598
+ readonly api: string;
4599
+ readonly reasons: ReadonlyArray<string>;
4600
+ }
4601
+
4258
4602
  export declare const PROBLEM_JSON = "application/problem+json; charset=utf-8";
4259
4603
 
4260
4604
  /** RFC 9457 Problem Details for one failure: `type` from the tag, `title` from
@@ -4283,6 +4627,16 @@ export declare interface ProcedureLike {
4283
4627
 
4284
4628
  export declare const procedureShapeDigest: (rpc: ProcedureLike) => string;
4285
4629
 
4630
+ /**
4631
+ * The error values a procedure's wire union can carry, as far as types can
4632
+ * tell: its own `error:`, the framework's unconditional members for its kind,
4633
+ * and every plugin-registered error.
4634
+ */
4635
+ export declare type ProcedureWireError<D extends {
4636
+ readonly kind: WireProcedureKind;
4637
+ readonly error: Schema.Schema.All;
4638
+ }> = Schema.Schema.Type<D['error']> | FrameworkWireError<D['kind']> | RegisteredPluginError;
4639
+
4286
4640
  export declare const PROTOCOL_VERSION: 1;
4287
4641
 
4288
4642
  export declare type ProtocolVersion = typeof PROTOCOL_VERSION;
@@ -4425,6 +4779,13 @@ export declare interface PublicApiSpec {
4425
4779
  * optional, the rest inherited. See `PublicApiProfileConfig`.
4426
4780
  */
4427
4781
  readonly format?: PublicApiProfileConfig;
4782
+ /**
4783
+ * GET only: how long a CDN may keep a successful answer, and the tags it can
4784
+ * purge it by (`Cache-Tag`, `Surrogate-Key`). A request with a credential is
4785
+ * always answered `private` — see `RestCache`. `tags` receives the answer as
4786
+ * it goes on the wire.
4787
+ */
4788
+ readonly cache?: RestCache;
4428
4789
  }
4429
4790
 
4430
4791
  /**
@@ -4512,6 +4873,9 @@ export declare interface QueryProcedureDescriptor<Name extends string, Input ext
4512
4873
  * `openAccess: '<why>'`. Mutually exclusive with `guards`; together they are
4513
4874
  * the only two shapes `security.defaultDeny` accepts. */
4514
4875
  readonly openAccess: string | undefined;
4876
+ /** Only these apps of the project may call this procedure — see `service` on
4877
+ * the definer's options. Absent → the app's own clients call it. */
4878
+ readonly service: ServiceSpec | undefined;
4515
4879
  /** Opt this query into a public REST endpoint (innovation/11). */
4516
4880
  readonly publicApi: PublicApiSpec | undefined;
4517
4881
  /** Opt this query into the auto-synthesized agent toolset (innovation/07). */
@@ -4584,7 +4948,7 @@ export declare const queryToRpc: <Name extends string, Input extends Schema.Sche
4584
4948
  }>, Schema.Struct<{
4585
4949
  op: Schema.Literal<["mergeCells"]>;
4586
4950
  path: typeof Schema.String;
4587
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
4951
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
4588
4952
  }>]>>;
4589
4953
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
4590
4954
  }>;
@@ -4669,6 +5033,11 @@ export declare type ReactivitySource = TableName | ReactivityChannel;
4669
5033
  */
4670
5034
  export declare type ReactivitySourceValue = string | ReactivityChannel;
4671
5035
 
5036
+ export declare const readPairing: (storage: PairingStorage) => Promise<Pairing | null>;
5037
+
5038
+ /** Every error a plugin registers, as one union (`never` before codegen). */
5039
+ export declare type RegisteredPluginError = VoltroPluginErrors[keyof VoltroPluginErrors];
5040
+
4672
5041
  /**
4673
5042
  * Declare, for this realm, what namespace each installed plugin actually
4674
5043
  * answers to. Emitted into `rpcGroup.generated.ts` by the codegen — one call
@@ -4685,6 +5054,19 @@ export declare type ReactivitySourceValue = string | ReactivityChannel;
4685
5054
  */
4686
5055
  export declare const registerPluginAliases: (aliases: Readonly<Record<string, string>>) => void;
4687
5056
 
5057
+ /**
5058
+ * The failure value as an `Error`, for a reporter that needs one — the value
5059
+ * itself when it already is one, else an `Error` named by its `_tag` (or
5060
+ * `NonErrorThrown`) with `errorMessage(value)` as its message and the value as
5061
+ * its `cause`.
5062
+ *
5063
+ * A loader that does not catch a refusal throws the declared error, which in a
5064
+ * web process is the plain object decoded from the wire. Handed to an error
5065
+ * reporter as it is, that becomes "Object captured as exception": no name to
5066
+ * group by, no stack, no message.
5067
+ */
5068
+ export declare const reportableError: (err: unknown) => Error;
5069
+
4688
5070
  /** Gate a handler on a scope; fails with a typed `ScopeError` if missing.
4689
5071
  * Checks the EFFECTIVE set so role-derived scopes count. */
4690
5072
  export declare const requireScope: (subject: Subject, scope: string) => Effect.Effect<void, ScopeError>;
@@ -4713,6 +5095,14 @@ export declare const resolveRestFormat: (config: PublicApiProfileConfig | undefi
4713
5095
  */
4714
5096
  export declare const resolveWorkflowAccess: (name: string, options: WorkflowAccessOptions<never>) => WorkflowAccess;
4715
5097
 
5098
+ /**
5099
+ * The answer a tuple source or resolver gives when the resource a guard names
5100
+ * does not exist for this caller. Return it only when disclosing that is
5101
+ * acceptable — typically after a TENANT-SCOPED read, so a row in another tenant
5102
+ * reads as missing rather than as "exists, not yours".
5103
+ */
5104
+ export declare type ResourceMissing = 'missing';
5105
+
4716
5106
  /** The question the runtime asks a registered resource-scope resolver. */
4717
5107
  export declare interface ResourceScopeRequest {
4718
5108
  /** The authenticated caller. */
@@ -4729,11 +5119,53 @@ export declare interface ResourceScopeRequest {
4729
5119
  * an Effect so it can read the app's role tables. It should NOT leak errors —
4730
5120
  * `checkGuardsEffect` treats a failed resolver as a DENIAL (fail-closed), so a
4731
5121
  * resolver that wants a different posture must catch its own errors.
5122
+ *
5123
+ * Only `true` allows. `'missing'` refuses with {@link NoSuchResource} (for a
5124
+ * signed-in caller); anything else refuses with `ScopeError`.
4732
5125
  */
4733
- export declare type ResourceScopeResolver = (req: ResourceScopeRequest) => Effect.Effect<boolean, unknown>;
5126
+ export declare type ResourceScopeResolver = (req: ResourceScopeRequest) => Effect.Effect<ResourceVerdict, unknown>;
5127
+
5128
+ /** A resource resolver's verdict: allowed, refused, or no such resource. */
5129
+ export declare type ResourceVerdict = boolean | ResourceMissing;
4734
5130
 
4735
5131
  export declare const REST_PROFILE_NAMES: ReadonlyArray<PublicApiProfileName>;
4736
5132
 
5133
+ /** A BINARY streaming response (download/export). The serve layer pipes the
5134
+ * ReadableStream without buffering — a body larger than the heap is fine.
5135
+ * The LAZY thunk form defers opening the source until the response streams. */
5136
+ declare interface RestByteResponse {
5137
+ readonly __voltroRestBytes: true;
5138
+ readonly byteStream: {
5139
+ readonly stream: ReadableStream<Uint8Array> | (() => ReadableStream<Uint8Array>);
5140
+ readonly contentLength?: number;
5141
+ readonly contentDisposition?: string;
5142
+ };
5143
+ readonly contentType?: string;
5144
+ readonly status?: number;
5145
+ readonly headers?: Readonly<Record<string, string | ReadonlyArray<string>>>;
5146
+ }
5147
+
5148
+ /**
5149
+ * Caching for a GET route's successful (200) answers.
5150
+ *
5151
+ * An anonymous request's answer is `public` for `maxAge` seconds (`max-age`
5152
+ * and `s-maxage`), `stale-while-revalidate` when set, and carries `tags` as
5153
+ * `Cache-Tag` (comma-separated) and `Surrogate-Key` (space-separated) — the
5154
+ * headers CDNs purge by. A request that carries a credential (`Authorization`
5155
+ * or a cookie) gets `private, no-cache` and no tags, whatever is declared:
5156
+ * what a key or a session may see must never land in a shared cache. Every
5157
+ * such answer says `Vary: Authorization, Cookie`. Errors are not affected.
5158
+ */
5159
+ declare interface RestCache {
5160
+ /** Seconds a cache may serve the answer without asking again. */
5161
+ readonly maxAge: number;
5162
+ /** Seconds after that a stale answer may still be served while the cache refetches. */
5163
+ readonly staleWhileRevalidate?: number;
5164
+ /** Purge tags from the answer as it goes on the wire, and the input.
5165
+ * A tag with whitespace or a comma is dropped (no header can carry it). */
5166
+ readonly tags?: (output: unknown, input: unknown) => ReadonlyArray<string>;
5167
+ }
5168
+
4737
5169
  export declare type RestDates = 'iso' | 'epoch-ms';
4738
5170
 
4739
5171
  export declare type RestEnvelope = 'none' | 'data' | 'jsonapi';
@@ -4823,6 +5255,11 @@ declare interface RestGuardRejection {
4823
5255
  readonly message: string;
4824
5256
  }
4825
5257
 
5258
+ /** What a REST handler may return: the decoded output, a `response(...)` with
5259
+ * its own status and headers, an `sse(...)` stream, or `bytes(...)` for a
5260
+ * non-JSON body (a file, an export, an XML document). */
5261
+ declare type RestHandlerResult<O> = O | RestResponse<O> | RestStreamResponse | RestByteResponse;
5262
+
4826
5263
  declare type RestMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
4827
5264
 
4828
5265
  export declare type RestNaming = 'camelCase' | 'snake_case';
@@ -4886,7 +5323,7 @@ declare interface RestRouteDescriptor<I, O> {
4886
5323
  /** Output schema. The handler returns the decoded `Type`; the desugar
4887
5324
  * encodes it to JSON for the `200` response. */
4888
5325
  readonly output: Schema.Schema<O, any, never>;
4889
- readonly handler: (input: I, ctx: RestRouteContext) => Promise<O | RestResponse<O>> | O | RestResponse<O>;
5326
+ readonly handler: (input: I, ctx: RestRouteContext) => Promise<RestHandlerResult<O>> | RestHandlerResult<O>;
4890
5327
  readonly summary?: string;
4891
5328
  readonly description?: string;
4892
5329
  readonly example?: RestRouteExample;
@@ -4943,6 +5380,9 @@ declare interface RestRouteDescriptor<I, O> {
4943
5380
  * of the JSON), so it is correct across content-encodings — the
4944
5381
  * transport's compression varies the bytes, not the representation. */
4945
5382
  readonly etag?: boolean;
5383
+ /** GET only: how long a shared cache (a CDN) may keep a successful answer,
5384
+ * and the tags it can be purged by. See {@link RestCache}. */
5385
+ readonly cache?: RestCache;
4946
5386
  }
4947
5387
 
4948
5388
  declare interface RestRouteExample {
@@ -4983,6 +5423,18 @@ declare interface RestRouteProjection {
4983
5423
  readonly format: RestFormat;
4984
5424
  }
4985
5425
 
5426
+ /**
5427
+ * What a REST handler returns to stream its response instead of resolving one
5428
+ * value: `sse((emit) => unsubscribe)`. The route's schema `output` is not used
5429
+ * for a stream (each frame is framed as it is produced), and everything the
5430
+ * desugar does BEFORE the handler — method gate, sunset, input decode, guards,
5431
+ * subject resolution — still applies.
5432
+ */
5433
+ declare interface RestStreamResponse {
5434
+ readonly __voltroRestStream: true;
5435
+ readonly stream: PluginHttpRouteStream;
5436
+ }
5437
+
4986
5438
  /**
4987
5439
  * Rewrite a descriptor's schema into the shape the profile puts on the wire:
4988
5440
  * `timestampMs` (`DateFromNumber`) becomes an RFC 3339 string under
@@ -5072,7 +5524,7 @@ export declare const rowPatchOpSchema: Schema.Union<[Schema.Struct<{
5072
5524
  }>, Schema.Struct<{
5073
5525
  op: Schema.Literal<["mergeCells"]>;
5074
5526
  path: typeof Schema.String;
5075
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
5527
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
5076
5528
  }>]>;
5077
5529
 
5078
5530
  export declare const rowPatchSchema: Schema.Struct<{
@@ -5106,7 +5558,7 @@ export declare const rowPatchSchema: Schema.Struct<{
5106
5558
  }>, Schema.Struct<{
5107
5559
  op: Schema.Literal<["mergeCells"]>;
5108
5560
  path: typeof Schema.String;
5109
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
5561
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
5110
5562
  }>]>>;
5111
5563
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
5112
5564
  }>;
@@ -5370,8 +5822,18 @@ export declare interface ServerErrorEvent {
5370
5822
  readonly name?: string;
5371
5823
  /** Distributed-trace id when one is in scope (workflows carry it). */
5372
5824
  readonly traceId?: string;
5373
- /** Extra context — op, runId, executionId, http status, etc. */
5825
+ /** Extra context — op, runId, executionId, http status, etc. Identity only:
5826
+ * never the primitive's data. */
5374
5827
  readonly fields?: Record<string, unknown>;
5828
+ /**
5829
+ * The error's CONTENT may carry data of a primitive declared encrypted
5830
+ * (`workflow({ encrypted: true })`): its message, its cause, a typed
5831
+ * failure's fields. A reporter that sends the event out of the process sends
5832
+ * {@link withheldServerError} instead — the class, the stack frames and the
5833
+ * identity fields, not the text. Encrypted at rest and then mailed to a
5834
+ * third party in plaintext would make the declaration decorative.
5835
+ */
5836
+ readonly contentSensitive?: boolean;
5375
5837
  }
5376
5838
 
5377
5839
  declare type ServerErrorListener = (event: ServerErrorEvent) => void;
@@ -5405,6 +5867,132 @@ declare const ServerOnlyColumnWrite_base: Schema.TaggedErrorClass<ServerOnlyColu
5405
5867
  columns: Schema.Array$<typeof Schema.String>;
5406
5868
  }>;
5407
5869
 
5870
+ /** A service (app) name: what `serviceCallers`, `service.callers` and a service
5871
+ * credential all spell the same way. At most 56 characters: the caller writes
5872
+ * as `service:<name>`, which is an `actors.id` — an `id()` column, VARCHAR(64)
5873
+ * on three dialects — so a longer name would fail its first audited write. */
5874
+ export declare const SERVICE_NAME_PATTERN: RegExp;
5875
+
5876
+ /**
5877
+ * The runtime-erased form of a procedure's `service: { callers }` — "only these
5878
+ * apps of the project may call this, and nobody else".
5879
+ *
5880
+ * A guard entry for the reason `OpenAccessSpec` is one: every enforcement path
5881
+ * is handed the `guards` array and nothing else, so a decision that lived on a
5882
+ * separate descriptor field would be enforced by the paths somebody remembered
5883
+ * and skipped by the rest.
5884
+ *
5885
+ * It admits exactly a `service` subject whose name is listed. `admin:full` does
5886
+ * NOT bypass it: a service procedure is a machine contract between two apps,
5887
+ * and a browser session holding the admin scope is not one of the parties.
5888
+ */
5889
+ export declare interface ServiceAccessSpec {
5890
+ /** The calling apps' names, as the callee declares them in `serviceCallers`.
5891
+ * Non-empty by construction — the definers refuse an empty list. */
5892
+ readonly service: ReadonlyArray<string>;
5893
+ }
5894
+
5895
+ /** Build the erased `service:` entry. The definers call this. */
5896
+ export declare const serviceAccessSpec: (callers: ReadonlyArray<string>) => ServiceAccessSpec;
5897
+
5898
+ /**
5899
+ * A procedure map (`ServiceProcedures`) turned into the nested object a caller
5900
+ * writes: `'deployments.activate'` becomes `.deployments.activate(...)`. A tag
5901
+ * that is both a procedure and a namespace prefix is both.
5902
+ */
5903
+ export declare type ServiceApi<P> = 0 extends (1 & P) ? any : ServiceApiOf<P>;
5904
+
5905
+ declare type ServiceApiOf<P> = {
5906
+ readonly [H in ServiceTagHead<keyof P & string>]: (H extends keyof P ? ServiceCall<P[H]> : unknown) & ServiceApiOf<{
5907
+ [K in keyof P & string as K extends `${H}.${infer R}` ? R : never]: P[K];
5908
+ }>;
5909
+ };
5910
+
5911
+ /** One procedure as the caller sees it — callable, `.effect`, and `.subscribe` for a query. */
5912
+ export declare type ServiceCall<D> = D extends ServiceDescriptorShape ? ((input: ServiceDecoded<D['input']>, options?: ServiceCallOptions) => Promise<ServiceDecoded<D['output']>>) & {
5913
+ /** The same call as an Effect. Inside a workflow step its write key is
5914
+ * derived from the step, so a re-sent step replays instead of writing twice. */
5915
+ readonly effect: (input: ServiceDecoded<D['input']>, options?: ServiceCallOptions) => Effect.Effect<ServiceDecoded<D['output']>, unknown>;
5916
+ } & (D extends {
5917
+ readonly kind: 'query';
5918
+ } ? {
5919
+ /** Hold the live query open; `onValue` gets each materialized value. */
5920
+ readonly subscribe: (input: ServiceDecoded<D['input']>, handlers: ServiceSubscribeHandlers<ServiceDecoded<D['output']>>) => ServiceSubscription;
5921
+ } : unknown) : never;
5922
+
5923
+ /** The apps a guard array admits as service callers, or `undefined` when the
5924
+ * procedure is not a service procedure. */
5925
+ export declare const serviceCallersOf: (guards: ReadonlyArray<AnyCheckSpec> | undefined) => ReadonlyArray<string> | undefined;
5926
+
5927
+ export declare interface ServiceCallOptions {
5928
+ /** Deadline for this call. Default 10 seconds. */
5929
+ readonly timeoutMs?: number;
5930
+ /** Explicit idempotency key for a write. Overrides the ambient and the fresh one. */
5931
+ readonly idempotencyKey?: string;
5932
+ /** Interrupts the call when aborted. */
5933
+ readonly signal?: AbortSignal;
5934
+ }
5935
+
5936
+ /** The decoded (application-side) type of a schema slot. */
5937
+ export declare type ServiceDecoded<S> = S extends {
5938
+ readonly Type: infer T;
5939
+ } ? T : never;
5940
+
5941
+ /** What a service procedure's descriptor type carries that the facade reads. */
5942
+ export declare interface ServiceDescriptorShape {
5943
+ readonly kind: string;
5944
+ readonly input: unknown;
5945
+ readonly output: unknown;
5946
+ }
5947
+
5948
+ /**
5949
+ * A service procedure's declaration: which apps of the project may call it.
5950
+ *
5951
+ * service: { callers: ['worker'] }
5952
+ *
5953
+ * The names are the CALLING apps' names as this app lists them in
5954
+ * `serviceCallers` — the same name the caller's credential resolves to.
5955
+ */
5956
+ export declare interface ServiceSpec {
5957
+ readonly callers: ReadonlyArray<string>;
5958
+ }
5959
+
5960
+ /**
5961
+ * The subject a verified service credential resolves to. Built by the service
5962
+ * strategy only — an app never constructs one, and nothing grants it scopes.
5963
+ */
5964
+ export declare const serviceSubject: (service: string) => Extract<Subject, {
5965
+ readonly type: "service";
5966
+ }>;
5967
+
5968
+ export declare interface ServiceSubscribeHandlers<T> {
5969
+ /** Every materialized value of the live query, starting with the first snapshot. */
5970
+ readonly onValue: (value: T) => void;
5971
+ /** The subscription's own in-band error (e.g. an access refusal). */
5972
+ readonly onError?: (error: unknown) => void;
5973
+ }
5974
+
5975
+ export declare interface ServiceSubscription {
5976
+ readonly unsubscribe: () => void;
5977
+ }
5978
+
5979
+ /** The first segment of a dotted rpc tag. */
5980
+ export declare type ServiceTagHead<K extends string> = K extends `${infer H}.${string}` ? H : K;
5981
+
5982
+ /** The request header carrying a framework session value outside a cookie. */
5983
+ export declare const SESSION_HEADER_NAME: "x-voltro-session";
5984
+
5985
+ /**
5986
+ * The session value a request presents: the dedicated header when set,
5987
+ * otherwise the named cookie. Pure — no verification, no IO.
5988
+ *
5989
+ * The header wins when both are present. A client that sets the header did so
5990
+ * deliberately; a cookie is ambient and may belong to a different app on the
5991
+ * same host (the reason `sessionExpiryFromHeaders` tolerates one that does
5992
+ * not verify). Explicit beats ambient.
5993
+ */
5994
+ export declare const sessionValueFromHeaders: (headers: Readonly<Record<string, string | undefined>>, cookieName: string, readCookie: (header: string | undefined, name: string) => string | undefined) => string | undefined;
5995
+
5408
5996
  /**
5409
5997
  * Publish the caller's fully-resolved scope set for this request (raw subject
5410
5998
  * scopes ∪ role-derived scopes ∪ any extra grants). Called by the rbac
@@ -5442,6 +6030,12 @@ export declare const shapeDigest: (text: string) => string;
5442
6030
  */
5443
6031
  export declare const sourceKeys: (source: ReactivitySourceValue | ReadonlyArray<ReactivitySourceValue> | undefined) => ReadonlyArray<string>;
5444
6032
 
6033
+ export declare const STORE_SCREENSHOT_PRESET_NAMES: [string, ...string[]];
6034
+
6035
+ export declare const STORE_SCREENSHOT_PRESETS: ReadonlyArray<StoreScreenshotPreset>;
6036
+
6037
+ export declare const STORE_SCREENSHOT_STORES: readonly ["app-store", "play", "mac-app-store", "microsoft-store"];
6038
+
5445
6039
  /**
5446
6040
  * Discriminated union of all framework-owned store errors. Use it on
5447
6041
  * a mutation's `error:` schema when you want every kind surfaced
@@ -5495,6 +6089,33 @@ declare const StoreOperationFailed_base: Schema.TaggedErrorClass<StoreOperationF
5495
6089
  cause: typeof Schema.String;
5496
6090
  }>;
5497
6091
 
6092
+ /** The PNG size the store receives: viewport × scale factor. */
6093
+ export declare const storeScreenshotPixels: (preset: StoreScreenshotPreset) => {
6094
+ readonly width: number;
6095
+ readonly height: number;
6096
+ };
6097
+
6098
+ export declare interface StoreScreenshotPreset {
6099
+ readonly name: string;
6100
+ readonly label: string;
6101
+ readonly kind: 'phone' | 'tablet' | 'desktop';
6102
+ /** The CSS viewport the page is laid out in. */
6103
+ readonly viewport: {
6104
+ readonly width: number;
6105
+ readonly height: number;
6106
+ };
6107
+ readonly deviceScaleFactor: number;
6108
+ /** Which stores accept this size. */
6109
+ readonly stores: ReadonlyArray<StoreScreenshotStore>;
6110
+ }
6111
+
6112
+ export declare const storeScreenshotPreset: (name: string) => StoreScreenshotPreset | undefined;
6113
+
6114
+ /** The presets a store takes, in table order. */
6115
+ export declare const storeScreenshotPresetsFor: (store: StoreScreenshotStore) => ReadonlyArray<StoreScreenshotPreset>;
6116
+
6117
+ export declare type StoreScreenshotStore = (typeof STORE_SCREENSHOT_STORES)[number];
6118
+
5498
6119
  export declare type StrategyResolution = {
5499
6120
  readonly kind: 'matched';
5500
6121
  readonly subject: Subject;
@@ -5617,6 +6238,14 @@ export declare const Subject: Schema.Union<[Schema.Struct<{
5617
6238
  tenantId: typeof Schema.Null;
5618
6239
  scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
5619
6240
  metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
6241
+ }>, Schema.Struct<{
6242
+ type: Schema.Literal<["service"]>;
6243
+ id: typeof Schema.String;
6244
+ /** The calling app's name, as the callee lists it in `serviceCallers`. */
6245
+ service: typeof Schema.String;
6246
+ tenantId: typeof Schema.Null;
6247
+ scopes: Schema.optional<Schema.Array$<typeof Schema.String>>;
6248
+ metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
5620
6249
  }>]>;
5621
6250
 
5622
6251
  export declare type Subject = typeof Subject.Type;
@@ -5670,6 +6299,12 @@ export declare const SubjectIdentity: Schema.Union<[Schema.Struct<{
5670
6299
  id: typeof Schema.String;
5671
6300
  tenantId: typeof Schema.Null;
5672
6301
  metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
6302
+ }>, Schema.Struct<{
6303
+ type: Schema.Literal<["service"]>;
6304
+ id: typeof Schema.String;
6305
+ tenantId: typeof Schema.Null;
6306
+ metadata: Schema.optional<Schema.Record$<typeof Schema.String, typeof Schema.Unknown>>;
6307
+ service: typeof Schema.String;
5673
6308
  }>]>;
5674
6309
 
5675
6310
  export declare type SubjectIdentity = typeof SubjectIdentity.Type;
@@ -5742,6 +6377,15 @@ declare const SubjectService_base: Context.TagClass<SubjectService, "@voltro/Sub
5742
6377
  readonly metadata?: {
5743
6378
  readonly [x: string]: unknown;
5744
6379
  } | undefined;
6380
+ } | {
6381
+ readonly type: "service";
6382
+ readonly id: string;
6383
+ readonly tenantId: null;
6384
+ readonly scopes?: readonly string[] | undefined;
6385
+ readonly metadata?: {
6386
+ readonly [x: string]: unknown;
6387
+ } | undefined;
6388
+ readonly service: string;
5745
6389
  }>;
5746
6390
 
5747
6391
  /** Subscribe to server-primitive errors. Returns an unsubscribe fn. */
@@ -5805,7 +6449,7 @@ export declare const subscriptionEvent: <D extends Schema.Schema.Any>(data: D) =
5805
6449
  }>, Schema.Struct<{
5806
6450
  op: Schema.Literal<["mergeCells"]>;
5807
6451
  path: typeof Schema.String;
5808
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
6452
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
5809
6453
  }>]>>;
5810
6454
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
5811
6455
  }>;
@@ -6108,7 +6752,7 @@ export declare const toRpc: <Name extends string, Input extends Schema.Schema.An
6108
6752
  }>, Schema.Struct<{
6109
6753
  op: Schema.Literal<["mergeCells"]>;
6110
6754
  path: typeof Schema.String;
6111
- value: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
6755
+ value: Schema.Record$<typeof Schema.String, Schema.transform<typeof Schema.Unknown, typeof Schema.Unknown>>;
6112
6756
  }>]>>;
6113
6757
  order: Schema.Array$<Schema.Union<[typeof Schema.String, typeof Schema.Number]>>;
6114
6758
  }>;
@@ -6419,6 +7063,10 @@ export declare interface VoltroPlugin {
6419
7063
  * merge to the stricter `required` and the safer `secret`.
6420
7064
  */
6421
7065
  readonly declaredEnv?: ReadonlyArray<PluginEnvVar>;
7066
+ /** What this plugin collects and uses — see `PluginPrivacyDeclaration`. */
7067
+ readonly privacy?: PluginPrivacyDeclaration;
7068
+ /** Where this plugin sends data outside the installation — see `PluginEgressDeclaration`. Omit when it sends nothing. */
7069
+ readonly egress?: ReadonlyArray<PluginEgressDeclaration>;
6422
7070
  /**
6423
7071
  * The authorization scopes this plugin DEFINES for the app — its scope
6424
7072
  * vocabulary. Distinct from `permissions` above, which is what the plugin
@@ -6554,8 +7202,10 @@ export declare interface VoltroPlugin {
6554
7202
  * that wants the enablers declares `(store, ctx)` and reads
6555
7203
  * `ctx?.sql` / `ctx?.scheduleCoordinated(...)`; one that ignores `ctx`
6556
7204
  * keeps working unchanged.
7205
+ * A returned Promise is awaited before the app begins serving requests.
7206
+ * Store-dependent initialization may run here when activation happened first.
6557
7207
  */
6558
- readonly bindDataStore?: (store: DataStore, ctx?: PluginBindContext) => void;
7208
+ readonly bindDataStore?: (store: DataStore, ctx?: PluginBindContext) => void | Promise<void>;
6559
7209
  /**
6560
7210
  * Capabilities this plugin offers to OTHER framework machinery, by name.
6561
7211
  *
@@ -6666,6 +7316,29 @@ export declare interface VoltroPlugin {
6666
7316
  * specific permission name in the error.
6667
7317
  */
6668
7318
  readonly extendSchema?: PluginSchemaContribution;
7319
+ /**
7320
+ * A directory of APP-SHAPED primitives this plugin brings with it — the same
7321
+ * file conventions an app uses (`*.query.ts` + `.server.ts`, `*.mutation.ts`,
7322
+ * `*.workflow.tsx`, `*.cron.tsx`, `*.subscribe.ts`, `*.reaction.ts`,
7323
+ * `schema.ts` / `*.entity.ts`, …).
7324
+ *
7325
+ * sources: fileURLToPath(new URL('./app', import.meta.url))
7326
+ *
7327
+ * Every app that installs the plugin discovers those files exactly as it
7328
+ * discovers its own: the same codegen, browser-safety guard, access gate,
7329
+ * migrations and boot paths. That is what makes a reusable BUILDING BLOCK — a
7330
+ * ledger, an approval flow, an audit trail — shareable between apps of a
7331
+ * project without copying it.
7332
+ *
7333
+ * It shares CODE, never data: each installing app gets its own copy of the
7334
+ * block's tables, in its own database. Two apps that need each other's data
7335
+ * call each other (`apis:` + `service:` procedures).
7336
+ *
7337
+ * The block's procedures keep the names their descriptors declare; a name
7338
+ * that collides with the app's own is refused at codegen, as any collision is.
7339
+ * An absolute path to a directory; a missing one refuses the boot.
7340
+ */
7341
+ readonly sources?: string;
6669
7342
  /**
6670
7343
  * Cross-cutting error schemas merged into every procedure's wire error
6671
7344
  * union. Lets a plugin interceptor fail with a typed error (e.g.
@@ -6742,6 +7415,26 @@ export declare interface VoltroPlugin {
6742
7415
  readonly onProcedureSurface?: (procedures: ReadonlyArray<ProcedureDescriptor>) => void;
6743
7416
  }
6744
7417
 
7418
+ /**
7419
+ * The plugins' cross-cutting errors, at the TYPE level.
7420
+ *
7421
+ * A plugin's `errorSchemas` reach every procedure's wire union at boot — a
7422
+ * runtime fact the type system cannot see. The codegen closes that gap: it
7423
+ * augments this interface in `rpcGroup.generated.ts` with one entry per
7424
+ * registered schema, so `defineExecutor` accepts a failure a plugin registers
7425
+ * exactly where the server will deliver it typed. Empty until the first
7426
+ * codegen run, which makes an unregistered plugin error a compile error rather
7427
+ * than a silent `InternalError`.
7428
+ *
7429
+ * declare module '@voltro/protocol' {
7430
+ * interface VoltroPluginErrors {
7431
+ * readonly '@acme/errors#NotFoundError': NotFoundError
7432
+ * }
7433
+ * }
7434
+ */
7435
+ export declare interface VoltroPluginErrors {
7436
+ }
7437
+
6745
7438
  /**
6746
7439
  * The table names THIS app declares — filled in by codegen, empty here.
6747
7440
  *
@@ -6834,10 +7527,28 @@ export declare interface WebSocketGatewayRoute {
6834
7527
  readonly onConnection: (connection: WebSocketGatewayConnection) => void | (() => void) | Promise<void | (() => void)>;
6835
7528
  }
6836
7529
 
7530
+ /**
7531
+ * Who may call a procedure over the wire: the app's own clients, or only other
7532
+ * apps of the project.
7533
+ *
7534
+ * A `service:` procedure IS on the wire — the server routes it on the same
7535
+ * socket — but it belongs to a different client group. Codegen emits it into
7536
+ * `serviceGroup.generated.ts` (imported by server processes of other apps) and
7537
+ * never into `rpcGroup.generated.ts` (imported by browsers), so no browser
7538
+ * bundle names a machine contract. The server refuses every non-service caller
7539
+ * regardless; keeping it out of the browser group is what keeps it out of the
7540
+ * code people ship.
7541
+ */
7542
+ export declare type WireAudience = 'client' | 'service';
7543
+
7544
+ export declare const wireAudienceOf: (descriptor: {
7545
+ readonly service?: ServiceSpec | undefined;
7546
+ }) => WireAudience;
7547
+
6837
7548
  /**
6838
7549
  * The error union a procedure ACTUALLY puts on the wire — `descriptor.error`
6839
7550
  * plus everything the framework can produce for it before or around the
6840
- * executor.
7551
+ * executor, plus the plugins' cross-cutting errors (`extraErrors`).
6841
7552
  *
6842
7553
  * ── Why this is exported rather than inlined in the lifters ────────────────
6843
7554
  *
@@ -6867,13 +7578,34 @@ export declare interface WebSocketGatewayRoute {
6867
7578
  * `ScopeError` themselves — is what made the two sets agree by hand.
6868
7579
  *
6869
7580
  * One function now, so they cannot disagree again.
7581
+ *
7582
+ * The plugins' errors are part of that answer for the same reason. A plugin's
7583
+ * `errorSchemas` entry is on every lifted union, and the judgement above was
7584
+ * handed the union WITHOUT them — so an interceptor's `RateLimited`, or an
7585
+ * app-wide error a local plugin registers, decoded typed on a query and was
7586
+ * collapsed to `InternalError` on every mutation and action, on both boot
7587
+ * paths. Pass the same `extraErrors` the lifter receives.
6870
7588
  */
6871
7589
  export declare const wireErrorUnion: (descriptor: {
6872
7590
  readonly error: Schema.Schema.All;
6873
7591
  readonly guards?: DeclaredAccess | undefined;
6874
7592
  readonly requiresApproval?: AnyApprovalPolicy | undefined;
6875
7593
  readonly idempotencyScope?: MutationIdempotencyScope | undefined;
6876
- }, kind: "query" | "mutation" | "action" | "stream") => Schema.Schema.All;
7594
+ }, kind: "query" | "mutation" | "action" | "stream", extraErrors?: ExtraErrors) => Schema.Schema.All;
7595
+
7596
+ /** A procedure kind that has a wire error union. */
7597
+ export declare type WireProcedureKind = keyof typeof FRAMEWORK_WIRE_ERRORS;
7598
+
7599
+ /**
7600
+ * The error of a {@link ServerErrorEvent} with its content withheld: same
7601
+ * `name` (the class, or a typed failure's tag), the original stack FRAMES —
7602
+ * code locations, which carry no data — and a message saying where the full
7603
+ * error can be read by someone allowed to. No `cause`, no own properties.
7604
+ *
7605
+ * The stack's first line repeats the message, so it is rebuilt from the frames
7606
+ * rather than copied.
7607
+ */
7608
+ export declare const withheldServerError: (event: ServerErrorEvent) => Error;
6877
7609
 
6878
7610
  /**
6879
7611
  * Re-tag a procedure descriptor for the wire.
@@ -7367,6 +8099,147 @@ export declare const WorkflowQueuedControlTargetSchema: Schema.Struct<{
7367
8099
  requestId: typeof Schema.NonEmptyString;
7368
8100
  }>;
7369
8101
 
8102
+ export declare type WorkflowRestartCommand = typeof WorkflowRestartCommandSchema.Type;
8103
+
8104
+ export declare const WorkflowRestartCommandSchema: Schema.Struct<{
8105
+ selection: Schema.Union<[Schema.Struct<{
8106
+ kind: Schema.Literal<["failed-steps"]>;
8107
+ }>, Schema.Struct<{
8108
+ kind: Schema.Literal<["from-step"]>;
8109
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8110
+ }>]>;
8111
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8112
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8113
+ }>;
8114
+
8115
+ export declare type WorkflowRestartObservation = typeof WorkflowRestartObservationSchema.Type;
8116
+
8117
+ /** Pending is a durable reservation, not proof of reset or engine entry.
8118
+ * Entered confirms this command's generation; it does not claim run success. */
8119
+ export declare const WorkflowRestartObservationSchema: Schema.Union<[Schema.Struct<{
8120
+ kind: Schema.Literal<["not-found"]>;
8121
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8122
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8123
+ }>, Schema.Struct<{
8124
+ kind: Schema.Literal<["withdrawn"]>;
8125
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8126
+ withdrawnAt: Schema.filter<typeof Schema.Number>;
8127
+ selection: Schema.Union<[Schema.Struct<{
8128
+ kind: Schema.Literal<["failed-steps"]>;
8129
+ }>, Schema.Struct<{
8130
+ kind: Schema.Literal<["from-step"]>;
8131
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8132
+ }>]>;
8133
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8134
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8135
+ }>, Schema.Struct<{
8136
+ kind: Schema.Literal<["pending"]>;
8137
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8138
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8139
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8140
+ requestedAt: Schema.filter<typeof Schema.Number>;
8141
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8142
+ selection: Schema.Union<[Schema.Struct<{
8143
+ kind: Schema.Literal<["failed-steps"]>;
8144
+ }>, Schema.Struct<{
8145
+ kind: Schema.Literal<["from-step"]>;
8146
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8147
+ }>]>;
8148
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8149
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8150
+ }>, Schema.filter<Schema.Struct<{
8151
+ kind: Schema.Literal<["entered"]>;
8152
+ enteredGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8153
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8154
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8155
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8156
+ requestedAt: Schema.filter<typeof Schema.Number>;
8157
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8158
+ selection: Schema.Union<[Schema.Struct<{
8159
+ kind: Schema.Literal<["failed-steps"]>;
8160
+ }>, Schema.Struct<{
8161
+ kind: Schema.Literal<["from-step"]>;
8162
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8163
+ }>]>;
8164
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8165
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8166
+ }>>]>;
8167
+
8168
+ export declare type WorkflowRestartResult = typeof WorkflowRestartResultSchema.Type;
8169
+
8170
+ /** A refused reservation never reset the journal. Transport failures instead
8171
+ * require reading the original target; they are not proof of refusal. */
8172
+ export declare const WorkflowRestartResultSchema: Schema.Union<[Schema.Union<[Schema.Struct<{
8173
+ kind: Schema.Literal<["not-found"]>;
8174
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8175
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8176
+ }>, Schema.Struct<{
8177
+ kind: Schema.Literal<["withdrawn"]>;
8178
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8179
+ withdrawnAt: Schema.filter<typeof Schema.Number>;
8180
+ selection: Schema.Union<[Schema.Struct<{
8181
+ kind: Schema.Literal<["failed-steps"]>;
8182
+ }>, Schema.Struct<{
8183
+ kind: Schema.Literal<["from-step"]>;
8184
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8185
+ }>]>;
8186
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8187
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8188
+ }>, Schema.Struct<{
8189
+ kind: Schema.Literal<["pending"]>;
8190
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8191
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8192
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8193
+ requestedAt: Schema.filter<typeof Schema.Number>;
8194
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8195
+ selection: Schema.Union<[Schema.Struct<{
8196
+ kind: Schema.Literal<["failed-steps"]>;
8197
+ }>, Schema.Struct<{
8198
+ kind: Schema.Literal<["from-step"]>;
8199
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8200
+ }>]>;
8201
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8202
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8203
+ }>, Schema.filter<Schema.Struct<{
8204
+ kind: Schema.Literal<["entered"]>;
8205
+ enteredGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8206
+ workflowName: Schema.filter<typeof Schema.NonEmptyString>;
8207
+ executionId: Schema.filter<typeof Schema.NonEmptyString>;
8208
+ originalGeneration: Schema.filter<typeof Schema.NonEmptyString>;
8209
+ requestedAt: Schema.filter<typeof Schema.Number>;
8210
+ dispatchAttempts: Schema.refine<number, typeof Schema.NonNegative>;
8211
+ selection: Schema.Union<[Schema.Struct<{
8212
+ kind: Schema.Literal<["failed-steps"]>;
8213
+ }>, Schema.Struct<{
8214
+ kind: Schema.Literal<["from-step"]>;
8215
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8216
+ }>]>;
8217
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8218
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8219
+ }>>]>, Schema.Struct<{
8220
+ kind: Schema.Literal<["not-reserved"]>;
8221
+ reason: Schema.Literal<["admission", "journal"]>;
8222
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8223
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8224
+ }>]>;
8225
+
8226
+ export declare type WorkflowRestartSelection = typeof WorkflowRestartSelectionSchema.Type;
8227
+
8228
+ export declare const WorkflowRestartSelectionSchema: Schema.Union<[Schema.Struct<{
8229
+ kind: Schema.Literal<["failed-steps"]>;
8230
+ }>, Schema.Struct<{
8231
+ kind: Schema.Literal<["from-step"]>;
8232
+ stepName: Schema.filter<typeof Schema.NonEmptyString>;
8233
+ }>]>;
8234
+
8235
+ export declare type WorkflowRestartTarget = typeof WorkflowRestartTargetSchema.Type;
8236
+
8237
+ /** Persist before submitting; read this same target after an uncertain reply. */
8238
+ export declare const WorkflowRestartTargetSchema: Schema.Struct<{
8239
+ requestId: Schema.filter<Schema.filter<typeof Schema.NonEmptyString>>;
8240
+ runId: Schema.filter<typeof Schema.NonEmptyString>;
8241
+ }>;
8242
+
7370
8243
  export declare const workflowResumeDescriptor: ActionProcedureDescriptor<"__voltro.workflow.resume", Schema.Struct<{
7371
8244
  workflowName: typeof Schema.String;
7372
8245
  executionId: typeof Schema.String;