@voltro/runtime 0.79.1 → 0.81.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
@@ -909,6 +909,12 @@ export declare const API_CSP = "default-src 'none'; frame-ancestors 'none'; base
909
909
 
910
910
  export declare const API_KEYS_TABLE = "_voltro_api_keys";
911
911
 
912
+ /** Caller side: the token this app presents to the api it binds as `name`. */
913
+ export declare const apiBindingTokenEnv: (name: string) => string;
914
+
915
+ /** Caller side: where the api it binds as `name` listens (production). */
916
+ export declare const apiBindingUrlEnv: (name: string) => string;
917
+
912
918
  /** What `ApiKeyStore.ensureActor` is handed for the row it plants. */
913
919
  export declare interface ApiKeyActor {
914
920
  readonly id: string;
@@ -1107,6 +1113,19 @@ export declare interface AppAccess {
1107
1113
  readonly scopes: ReadonlyArray<string>;
1108
1114
  }
1109
1115
 
1116
+ /**
1117
+ * The typed clients of the apps this app calls — augmented per app by the
1118
+ * codegen'd `apis.generated.ts`:
1119
+ *
1120
+ * declare module '@voltro/runtime' {
1121
+ * interface AppApis { readonly cloud: ServiceApi<CloudServiceProcedures> }
1122
+ * }
1123
+ *
1124
+ * Empty until an app declares `apis:`.
1125
+ */
1126
+ export declare interface AppApis {
1127
+ }
1128
+
1110
1129
  export declare interface AppContext {
1111
1130
  /** Run database work with a freshly transaction-bound context. Reuses an
1112
1131
  * existing mutation transaction (no savepoint). The callback may be retried;
@@ -1204,6 +1223,19 @@ export declare interface AppContext {
1204
1223
  * subject", by construction.
1205
1224
  */
1206
1225
  readonly connections?: ConnectionsFacade;
1226
+ /**
1227
+ * The other apps of the project this app calls (`ctx.apis.<name>`) — one
1228
+ * typed client per `apis:` binding in `app.config.ts`, typed from the
1229
+ * callee's `service:` procedures by the codegen'd `apis.generated.ts`.
1230
+ *
1231
+ * await ctx.apis.cloud.deployments.activate({ deploymentId, tenantId })
1232
+ * yield* ctx.apis.cloud.deployments.activate.effect(input) // in a workflow step
1233
+ *
1234
+ * Always present — an app with no bindings gets an empty object, so the field
1235
+ * never reads as "maybe wired". A call without an answer fails with
1236
+ * `ApiUnavailable`; every answer the callee gives arrives as itself.
1237
+ */
1238
+ readonly apis: AppApis;
1207
1239
  /**
1208
1240
  * Request-scoped batching (`ctx.load` / `ctx.loadMany`). Coalesces
1209
1241
  * same-tick reads of one table into a single `WHERE id IN (...)`.
@@ -1460,6 +1492,9 @@ export declare const assertCan: (subject: RebacSubject, action: string, resource
1460
1492
  */
1461
1493
  export declare const assertConnectionCipherConfigured: (definitions: ReadonlyArray<ConnectionDefinition>) => void;
1462
1494
 
1495
+ /** Refuse a caller map that could not be what the author meant. */
1496
+ export declare const assertServiceCallersConfig: (callers: ServiceCallersConfig) => void;
1497
+
1463
1498
  /**
1464
1499
  * Boot gate: every mounted incoming-webhook route must carry a verification
1465
1500
  * declaration. Throws `UnverifiedWebhookRoute` for the first that does not.
@@ -1640,6 +1675,19 @@ export declare interface AuditableQuery {
1640
1675
 
1641
1676
  export declare interface AuthMiddlewareLayerOptions {
1642
1677
  readonly resolveSubject: ResolveSubjectFn;
1678
+ /**
1679
+ * The apps allowed to call one rpc tag, or `undefined` when the tag is not a
1680
+ * `service:` procedure. Both boot paths pass the same lookup over the
1681
+ * procedures they registered.
1682
+ *
1683
+ * This is the OUTER wall for a service credential: the guard check refuses a
1684
+ * service subject at every procedure that does not name it, but framework
1685
+ * built-ins (`__voltro.*`) and plugin routes reach their handlers without a
1686
+ * descriptor guard. Here, before any handler, a service subject reaches only
1687
+ * the tags that name it — so a leaked service token opens exactly the doors
1688
+ * the callee declared and nothing the framework adds.
1689
+ */
1690
+ readonly serviceCallersFor?: (tag: string) => ReadonlyArray<string> | undefined;
1643
1691
  }
1644
1692
 
1645
1693
  /**
@@ -2245,6 +2293,8 @@ export declare interface ClientAddressInput {
2245
2293
  readonly socketAddress: string | undefined;
2246
2294
  /** Raw `x-forwarded-for` header, if present. */
2247
2295
  readonly xForwardedFor: string | undefined;
2296
+ /** Raw value of `config.clientAddressHeader`, if the request carried it. */
2297
+ readonly clientAddressHeader?: string | undefined;
2248
2298
  readonly config: TrustedProxyConfig;
2249
2299
  }
2250
2300
 
@@ -3301,6 +3351,10 @@ export declare const DEFAULT_HSTS = "max-age=15552000; includeSubDomains";
3301
3351
  */
3302
3352
  export declare const DEFAULT_KEEP_ALIVE_MS = 72000;
3303
3353
 
3354
+ export declare const DEFAULT_LEVEL_RESYNC_MS: number;
3355
+
3356
+ export declare const DEFAULT_LEVEL_STUCK_MS: number;
3357
+
3304
3358
  /** Ceiling the idle backoff climbs to. Deliberately short enough to be a
3305
3359
  * FLOOR under a missed wake rather than a substitute for one. */
3306
3360
  export declare const DEFAULT_MAX_IDLE_INTERVAL_MS = 30000;
@@ -3539,7 +3593,12 @@ export declare const defineOutboxHandler: <R = never>(definition: OutboxHandlerD
3539
3593
 
3540
3594
  /** Declare a reaction. Validates that the MANDATORY guard is present — an
3541
3595
  * ungated reaction is a spend-storm footgun, so this fails LOUD at boot. */
3542
- export declare const defineReaction: (def: ReactionDefinition) => ReactionDefinition;
3596
+ export declare function defineReaction(def: LevelReactionDefinition): LevelReactionDefinition;
3597
+
3598
+ export declare function defineReaction(def: EdgeReactionDefinition): EdgeReactionDefinition;
3599
+
3600
+ /** A value already typed as either form — narrowed at runtime by its `watch`. */
3601
+ export declare function defineReaction(def: ReactionDefinition): ReactionDefinition;
3543
3602
 
3544
3603
  /** Register a resource policy (the static, discoverable relationship registry
3545
3604
  * the capability map exposes). Validates + fails LOUD on a malformed or
@@ -4062,6 +4121,15 @@ export declare interface DrainResult {
4062
4121
  */
4063
4122
  export declare const dueWakeups: (store: DataStore, now: Date, limit?: number) => Promise<ReadonlyArray<Wakeup>>;
4064
4123
 
4124
+ /** A reaction to each CHANGE of a table (edge-triggered). */
4125
+ export declare interface EdgeReactionDefinition extends ReactionBase {
4126
+ /** The table whose changes trigger the reaction (+ optional op filter). */
4127
+ readonly watch: ReactionEdgeWatch;
4128
+ /** Optional predicate over the change — skip when it returns false. */
4129
+ readonly when?: (event: ReactionEvent) => boolean;
4130
+ readonly guards: ReactionGuards;
4131
+ }
4132
+
4065
4133
  /** The minimal Effect-store surface `insertIgnoreRowOutcomeEffect` needs. */
4066
4134
  export declare interface EffectInsertIgnoreOutcomeStore {
4067
4135
  readonly insertIgnoreWithOutcome: (table: string, row: Row, options: {
@@ -5453,13 +5521,12 @@ export declare const forbiddenColumnsForSubject: (tableName: string, subject: Su
5453
5521
  export declare const forgetCredential: (store: VaultStore, connectionId: string, subjectId: string) => Promise<boolean>;
5454
5522
 
5455
5523
  /**
5456
- * Drop rows for a replica GENERATION that is gone.
5524
+ * Drop this replica's rows on a graceful shutdown.
5457
5525
  *
5458
- * Not a retention sweep — the table has no history to age out. This removes
5459
- * the row of a process that restarted, because the replica's NEW row is
5460
- * written under the same `(replicaId, kind)` key and the old generation is
5461
- * simply overwritten. What this handles is a replica that went away for good
5462
- * (a scale-down), whose row would otherwise be counted forever.
5526
+ * A restart under the same replica ID overwrites its row anyway; this handles
5527
+ * a replica that goes away for good (a scale-down). It cannot run for a process
5528
+ * that crashed or was killed — those rows are aged out by the retention sweep
5529
+ * on `writtenAt`, which every live replica refreshes each publish interval.
5463
5530
  */
5464
5531
  export declare const forgetReplicaObservations: (store: DataStore, replicaId: string) => Promise<void>;
5465
5532
 
@@ -5576,6 +5643,8 @@ export declare interface GroupState {
5576
5643
  readonly extreme?: number;
5577
5644
  }
5578
5645
 
5646
+ export declare const guardUndeclaredTables: (underlying: DataStore, isKnownTable: (table: string) => boolean) => DataStore;
5647
+
5579
5648
  /** What a user handler may return. */
5580
5649
  export declare type HandlerBody<R = never> = void | Promise<unknown> | Effect.Effect<unknown, unknown, R>;
5581
5650
 
@@ -5862,7 +5931,10 @@ export { InspectStore }
5862
5931
  */
5863
5932
  export declare interface InspectStream {
5864
5933
  readonly replicaId?: string;
5865
- readonly select?: (url: string, headers: Readonly<Record<string, string>>) => Stream.Stream<Uint8Array, unknown> | undefined;
5934
+ /** Undefined serves this process's own feed. Decided before the response
5935
+ * starts: an event stream is a 200 from its first byte, so a refusal found
5936
+ * inside the body could only reach the client as a feed that closes. */
5937
+ readonly select?: (url: string, headers: Readonly<Record<string, string>>) => Effect.Effect<InspectStreamSelection | undefined>;
5866
5938
  /** Same shape as the inspect auth resolver — returns ok or reason. */
5867
5939
  readonly authorize: (headers: Readonly<Record<string, string>>) => {
5868
5940
  readonly ok: true;
@@ -5884,6 +5956,16 @@ export declare interface InspectStream {
5884
5956
  readonly keepAliveMs?: number;
5885
5957
  }
5886
5958
 
5959
+ /** A named process's feed, or the reasoned refusal to open it. */
5960
+ export declare type InspectStreamSelection = {
5961
+ readonly kind: 'stream';
5962
+ readonly stream: Stream.Stream<Uint8Array>;
5963
+ } | {
5964
+ readonly kind: 'refused';
5965
+ readonly status: number;
5966
+ readonly body: unknown;
5967
+ };
5968
+
5887
5969
  export { inspectWorkflow }
5888
5970
 
5889
5971
  export declare const installApprovalGate: (gate: ApprovalGateApi) => void;
@@ -6002,6 +6084,12 @@ export declare const isIdleNow: (opts: IdleCheckOptions) => Promise<boolean>;
6002
6084
 
6003
6085
  export declare const isInfraError: (e: unknown) => boolean;
6004
6086
 
6087
+ /** Narrow a reaction definition to the level form. */
6088
+ export declare const isLevelReaction: (def: ReactionDefinition) => def is LevelReactionDefinition;
6089
+
6090
+ /** Narrow a reaction to the level form. */
6091
+ export declare const isLevelWatch: (watch: ReactionWatch) => watch is ReactionLevelWatch;
6092
+
6005
6093
  export declare const isObservingGraph: () => boolean;
6006
6094
 
6007
6095
  /**
@@ -6109,6 +6197,8 @@ export declare const isTransactionReadConflict: (err: unknown) => err is Transac
6109
6197
  /** Is `address` one of the peers we are willing to take a forwarded chain from? */
6110
6198
  export declare const isTrustedProxy: (address: string | undefined, config: TrustedProxyConfig) => boolean;
6111
6199
 
6200
+ export declare const isWellFormedServiceToken: (value: string) => boolean;
6201
+
6112
6202
  export declare const isWorkflowEventTriggerDefinition: (value: unknown) => value is WorkflowEventTriggerDefinition;
6113
6203
 
6114
6204
  /** A maintainable aggregate op. (countDistinct / window funcs are NOT here —
@@ -6232,6 +6322,34 @@ export declare const lastWriteProvenance: (row: Record<string, unknown> | null |
6232
6322
  readonly traceId?: string | null;
6233
6323
  }) => ProvenanceResult;
6234
6324
 
6325
+ /**
6326
+ * A reaction to every ROW of a live query's current result (level-triggered).
6327
+ *
6328
+ * The act is a workflow, and the workflow's `idempotencyKey` is what makes it
6329
+ * exactly-once across the fleet: every replica that sees the row may try to
6330
+ * start it, and the engine collapses them into one execution. So there is no
6331
+ * `dedupeKey` here, and the workflow's key must identify the unit of work — a
6332
+ * row that can come BACK into the set (an org suspended, unsuspended, suspended
6333
+ * again) needs a key carrying that generation (`suspend:${orgId}:${suspendedAt}`).
6334
+ *
6335
+ * The workflow must move the row OUT of the set (its first step flips a status,
6336
+ * its failure path records a terminal one). A row still in the set long after
6337
+ * it was started is reported as `stuck`, never silently restarted.
6338
+ */
6339
+ export declare interface LevelReactionDefinition extends ReactionBase {
6340
+ readonly watch: ReactionLevelWatch;
6341
+ /** The identity of a row in the set — what `stuck` reports and the runner tracks. */
6342
+ readonly key: (row: never) => string;
6343
+ /** Only rows it returns true for act. */
6344
+ readonly when?: (row: never) => boolean;
6345
+ /** Re-read the whole set this often even when nothing changed, so a start that
6346
+ * failed is retried. Default 5 minutes, minimum 30 seconds. */
6347
+ readonly resync?: number | string;
6348
+ /** Report a row still in the set this long after it was started. Default 15 minutes. */
6349
+ readonly stuckAfter?: number | string;
6350
+ readonly guards?: Omit<ReactionGuards, 'dedupeKey'>;
6351
+ }
6352
+
6235
6353
  export declare class LinearScanMatcher implements Matcher {
6236
6354
  private readonly byTable;
6237
6355
  register(sub: SubscriptionRecord): void;
@@ -7055,6 +7173,11 @@ export declare type MetricsMode = 'off' | 'console' | 'otlp' | 'memory';
7055
7173
  /** The lowest cadence any platform scheduler can express. */
7056
7174
  export declare const MIN_EXTERNALIZABLE_INTERVAL_MS = 60000;
7057
7175
 
7176
+ export declare const MIN_LEVEL_RESYNC_MS = 30000;
7177
+
7178
+ /** Mint a fresh service token. `voltro dev` and `voltro secret generate` call this. */
7179
+ export declare const mintServiceToken: () => string;
7180
+
7058
7181
  /**
7059
7182
  * Ordering token for one mirrored write.
7060
7183
  *
@@ -8268,17 +8391,23 @@ export declare type ReactionAct = {
8268
8391
  readonly payload?: (event: ReactionEvent) => unknown;
8269
8392
  };
8270
8393
 
8271
- export declare interface ReactionDefinition {
8394
+ declare interface ReactionBase {
8272
8395
  readonly name: string;
8273
- /** The table whose changes trigger the reaction (+ optional op filter). */
8274
- readonly watch: {
8275
- readonly table: string;
8276
- readonly on?: ReactionOp | ReadonlyArray<ReactionOp>;
8277
- };
8278
- /** Optional predicate over the change — skip when it returns false. */
8279
- readonly when?: (event: ReactionEvent) => boolean;
8280
8396
  readonly act: ReactionAct;
8281
- readonly guards: ReactionGuards;
8397
+ }
8398
+
8399
+ export declare type ReactionDefinition = EdgeReactionDefinition | LevelReactionDefinition;
8400
+
8401
+ /** `'5 minutes'` / `'30s'` / `'8 days'` / `300000` → milliseconds. */
8402
+ export declare const reactionDurationMs: (value: number | string, field: string, reaction: string) => number;
8403
+
8404
+ /**
8405
+ * The EDGE form: react to each change of one table, as it happens.
8406
+ * Best-effort — a change made while the reacting process is down is not seen.
8407
+ */
8408
+ export declare interface ReactionEdgeWatch {
8409
+ readonly table: string;
8410
+ readonly on?: ReactionOp | ReadonlyArray<ReactionOp>;
8282
8411
  }
8283
8412
 
8284
8413
  /** The change a reaction reacts to (the framework's CDC shape). */
@@ -8324,6 +8453,23 @@ export declare interface ReactionGuards {
8324
8453
  };
8325
8454
  }
8326
8455
 
8456
+ /**
8457
+ * The LEVEL form: react to every ROW of a live query's current result — the set
8458
+ * of open work — for as long as it is in the set.
8459
+ *
8460
+ * Each delivery of the query is the whole current set, so work that appeared
8461
+ * while the reacting process was down, restarting or deploying is seen on the
8462
+ * first delivery after it comes back: nothing is lost. `api` names a binding in
8463
+ * `apis:` (the query is another app's `service:` query); omit it to watch one
8464
+ * of this app's own queries.
8465
+ */
8466
+ export declare interface ReactionLevelWatch {
8467
+ readonly query: string;
8468
+ readonly api?: string;
8469
+ /** The query's input. Default `{}`. */
8470
+ readonly input?: unknown;
8471
+ }
8472
+
8327
8473
  export declare type ReactionOp = 'insert' | 'update' | 'delete';
8328
8474
 
8329
8475
  export declare type ReactionOutcome = 'acted' | 'skipped-op' | 'skipped-when' | 'skipped-dedupe' | 'skipped-ratelimit' | 'skipped-budget';
@@ -8382,6 +8528,8 @@ export declare interface ReactionRunDeps {
8382
8528
  readonly now?: () => number;
8383
8529
  }
8384
8530
 
8531
+ export declare type ReactionWatch = ReactionEdgeWatch | ReactionLevelWatch;
8532
+
8385
8533
  /** The reactive tunables a caller may declare. Every field optional; every
8386
8534
  * default above. */
8387
8535
  export declare interface ReactiveConfigInput {
@@ -9425,6 +9573,14 @@ export declare const resolveReactiveConfig: (config?: ReactiveConfigInput | unde
9425
9573
 
9426
9574
  export declare const resolveRedirectUri: (definition: OAuth2ConnectionDefinition, publicUrl: string) => string;
9427
9575
 
9576
+ /** The client address of a request, read from its headers per `config`. The
9577
+ * one call every listener makes — which headers matter is decided here. */
9578
+ export declare const resolveRequestClientAddress: (input: {
9579
+ readonly socketAddress: string | undefined;
9580
+ readonly headers: Readonly<Record<string, string | undefined>>;
9581
+ readonly config: TrustedProxyConfig;
9582
+ }) => string | undefined;
9583
+
9428
9584
  /**
9429
9585
  * Whether the request reached the app over TLS.
9430
9586
  *
@@ -9500,7 +9656,8 @@ export declare type ResolveSubjectFn = (input: {
9500
9656
  export declare const resolveTimelineMode: (env?: Record<string, string | undefined>) => TimelineMode;
9501
9657
 
9502
9658
  /** Select identity before opening a transaction, never while one owns writes.
9503
- * Only the framework's system subject may select a different tenant. */
9659
+ * Only a cross-tenant machine subject — the framework's system subject, or a
9660
+ * service calling a `service:` procedure — may select a different tenant. */
9504
9661
  export declare const resolveTransactionSubject: (subject: Subject, tenantId: string | undefined, transactionActive: boolean) => Subject;
9505
9662
 
9506
9663
  /**
@@ -9513,7 +9670,7 @@ export declare const resolveTransactionSubject: (subject: Subject, tenantId: str
9513
9670
  * rebuild; the config field exists because it is a tunable and tunables belong
9514
9671
  * in `app.config.ts`.
9515
9672
  */
9516
- export declare const resolveTrustedProxies: (configured?: ReadonlyArray<string> | undefined, env?: Readonly<Record<string, string | undefined>>) => TrustedProxyConfig;
9673
+ export declare const resolveTrustedProxies: (configured?: ReadonlyArray<string> | undefined, env?: Readonly<Record<string, string | undefined>>, configuredHeader?: string | undefined) => TrustedProxyConfig;
9517
9674
 
9518
9675
  /** Per-resource-type policy. `actions` maps an action to the relations that
9519
9676
  * grant it (ANY-of). `implies` is relation implication (owner ⇒ editor ⇒
@@ -9944,6 +10101,9 @@ export declare interface RowHistoryEntry {
9944
10101
  readonly traceId?: string | null;
9945
10102
  }
9946
10103
 
10104
+ /** Internal header name. Its value is meaningless without the process mark. */
10105
+ export declare const RPC_CLIENT_ADDRESS_HEADER = "x-voltro-client-address";
10106
+
9947
10107
  /** Server-owned lifetime, not a user identity or a client-supplied claim.
9948
10108
  * WebSockets retain it across calls; HTTP retains it only within one request.
9949
10109
  * Reconnects and server restarts receive a new id. */
@@ -9951,6 +10111,11 @@ export declare interface RpcConnection {
9951
10111
  readonly id: string;
9952
10112
  readonly transport: 'websocket' | 'http';
9953
10113
  readonly signal: AbortSignal;
10114
+ /** The caller's address, resolved by the rpc listener through
10115
+ * `security.trustedProxies` / `clientAddressHeader` — the same value its
10116
+ * per-IP shield counts. Absent when the call did not arrive through that
10117
+ * listener (an internal invocation, an embedder's own transport). */
10118
+ readonly remoteAddr?: string;
9954
10119
  }
9955
10120
 
9956
10121
  export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
@@ -10005,6 +10170,9 @@ export declare interface RpcServerOptions<Rpcs extends Rpc.Any> {
10005
10170
  * boot-path parity rule asks for.
10006
10171
  */
10007
10172
  readonly publishSurfaceDigest?: boolean;
10173
+ /** The bound tags that are `service:` procedures — published as
10174
+ * `serviceSurface`, kept out of the client `surface`. */
10175
+ readonly serviceSurfaceTags?: ReadonlySet<string>;
10008
10176
  /** Non-sensitive fleet identity written on the rpc WebSocket open/close
10009
10177
  * records. Defaults to the process identity resolver (POD_NAME/HOSTNAME).
10010
10178
  * Embedders may state it explicitly when they own process identity. */
@@ -10218,7 +10386,7 @@ export declare const runProvidedEffect: <A>(effect: Effect.Effect<A, unknown, ne
10218
10386
  * `agentActor(via:'agent')` scoped to the row's tenant. Idempotent: the same
10219
10387
  * dedupe key never acts twice.
10220
10388
  */
10221
- export declare const runReaction: (reaction: ReactionDefinition, event: ReactionEvent, deps: ReactionRunDeps) => Promise<ReactionOutcome>;
10389
+ export declare const runReaction: (reaction: EdgeReactionDefinition, event: ReactionEvent, deps: ReactionRunDeps) => Promise<ReactionOutcome>;
10222
10390
 
10223
10391
  /**
10224
10392
  * Start the full single-node orchestrator: bind the router on the public
@@ -10250,6 +10418,9 @@ export declare interface RuntimeContext {
10250
10418
  /** Server-owned transport identity and close signal. Not authentication;
10251
10419
  * session membership remains an application's explicit access decision. */
10252
10420
  readonly connection?: RpcConnection;
10421
+ /** `connection.remoteAddr`, lifted so the serve pipeline — which also serves
10422
+ * REST calls that have no connection — reads one field for both. */
10423
+ readonly remoteAddr?: string;
10253
10424
  }
10254
10425
 
10255
10426
  /**
@@ -11099,6 +11270,11 @@ export declare interface ServeRequestContext {
11099
11270
  readonly surface?: 'rest';
11100
11271
  /** The descriptor's `publicApi` spec, on a projected call. */
11101
11272
  readonly publicApi?: PublicApiSpec;
11273
+ /** The caller's address as the listener resolved it through
11274
+ * `security.trustedProxies` — carried to the plugin interceptor so a
11275
+ * per-endpoint limit can count per client. Absent for calls that did not
11276
+ * arrive over the network. */
11277
+ readonly remoteAddr?: string;
11102
11278
  /**
11103
11279
  * Caller-side cancellation (the gRPC deadline / client-cancel path).
11104
11280
  * Honoured at the ONE place the executor effect runs to a promise, so an
@@ -11163,11 +11339,56 @@ export declare type ServeRpcInterceptor = (base: Effect.Effect<unknown, unknown,
11163
11339
  /** See `ServeRequestContext.surface` — present on a REST-projected call. */
11164
11340
  readonly surface?: 'rest';
11165
11341
  readonly publicApi?: PublicApiSpec;
11342
+ /** See `ServeRequestContext.remoteAddr`. */
11343
+ readonly remoteAddr?: string;
11166
11344
  }) => Effect.Effect<unknown, unknown, never>;
11167
11345
 
11168
11346
  /** The published tracer, or `undefined` when tracing is off / no server booted. */
11169
11347
  export declare const serverTracer: () => Tracer.Tracer | undefined;
11170
11348
 
11349
+ /** The bearer prefix every service credential carries. */
11350
+ export declare const SERVICE_TOKEN_PREFIX = "vsvc_";
11351
+
11352
+ /**
11353
+ * One accepted caller. Declared in the callee's `app.config.ts`:
11354
+ *
11355
+ * serviceCallers: { worker: {} }
11356
+ *
11357
+ * The object is the per-caller configuration slot; today it carries nothing,
11358
+ * because the token comes from the environment by convention and the grant
11359
+ * comes from the procedures. It is an object rather than `true` so a later
11360
+ * per-caller option (a rate limit, an mTLS identity) is an addition, not a
11361
+ * change of shape.
11362
+ */
11363
+ export declare type ServiceCallerConfig = Readonly<Record<string, never>>;
11364
+
11365
+ /** Callee side: the second accepted value during a rotation. */
11366
+ export declare const serviceCallerNextTokenEnv: (caller: string) => string;
11367
+
11368
+ export declare type ServiceCallersConfig = Readonly<Record<string, ServiceCallerConfig>>;
11369
+
11370
+ /**
11371
+ * The strategy an app with `serviceCallers` puts FIRST in its chain.
11372
+ *
11373
+ * It claims only `Bearer vsvc_…`. Every other request is a `skip`, so an app's
11374
+ * users never pay more than a prefix check.
11375
+ */
11376
+ export declare const serviceCallerStrategy: (options: ServiceCallerStrategyOptions) => AuthStrategy;
11377
+
11378
+ export declare interface ServiceCallerStrategyOptions {
11379
+ readonly callers: ServiceCallersConfig;
11380
+ /** Read lazily — at request time and in `verifyConfig` — never while the
11381
+ * config is imported, so `voltro build` needs no production secret. */
11382
+ readonly env?: () => Readonly<Record<string, string | undefined>>;
11383
+ }
11384
+
11385
+ /** Callee side: the env var holding the token a caller presents. */
11386
+ export declare const serviceCallerTokenEnv: (caller: string) => string;
11387
+
11388
+ /** `worker` → `WORKER`, `build-runner` → `BUILD_RUNNER`, `voltroCloudWorker` →
11389
+ * `VOLTRO_CLOUD_WORKER`. The env-var spelling of an app name. */
11390
+ export declare const serviceEnvSegment: (name: string) => string;
11391
+
11171
11392
  export declare const setBufferedBytes: (label: string, bytes: number) => void;
11172
11393
 
11173
11394
  /** Register the process-wide connection resolver (or clear with `undefined`).
@@ -12182,6 +12403,12 @@ export declare interface TransportSecurityOptions {
12182
12403
  * CIDRs, the presets `loopback` / `private`, a hop count (`['2']`), or
12183
12404
  * `['*']` to trust any peer. Env override: `VOLTRO_TRUSTED_PROXIES`. */
12184
12405
  readonly trustedProxies?: ReadonlyArray<string>;
12406
+ /** A header a trusted proxy SETS to the client's single address
12407
+ * (`'cf-connecting-ip'`, `'x-real-ip'`). Believed only from a peer
12408
+ * `trustedProxies` covers, and only when it holds one valid IP; otherwise
12409
+ * the `x-forwarded-for` chain applies. Env override:
12410
+ * `VOLTRO_CLIENT_ADDRESS_HEADER`. */
12411
+ readonly clientAddressHeader?: string;
12185
12412
  /** Security response headers. Env override: `VOLTRO_SECURITY_HEADERS`
12186
12413
  * (`off|default|strict`), plus `VOLTRO_CSP` / `VOLTRO_CSP_HTML` /
12187
12414
  * `VOLTRO_HSTS`. */
@@ -12227,6 +12454,10 @@ export declare interface TrustedProxyConfig {
12227
12454
  readonly trustAll: boolean;
12228
12455
  /** Hop count (express's numeric `trust proxy`). `undefined` → not hop mode. */
12229
12456
  readonly hops?: number;
12457
+ /** Lower-cased name of a single-address header a trusted proxy SETS
12458
+ * (`cf-connecting-ip`, `x-real-ip`). Preferred over the forwarded chain when
12459
+ * it holds one valid IP; `x-forwarded-for` remains the fallback. */
12460
+ readonly clientAddressHeader?: string;
12230
12461
  }
12231
12462
 
12232
12463
  /**
@@ -12315,6 +12546,13 @@ declare interface TypedUpsertOptions<T extends AnyWritableTable> {
12315
12546
  */
12316
12547
  export declare const unbindConnection: (clientId: number) => void;
12317
12548
 
12549
+ export declare class UndeclaredTableAccess extends Error {
12550
+ readonly table: string;
12551
+ readonly operation: string;
12552
+ readonly _tag = "UndeclaredTableAccess";
12553
+ constructor(table: string, operation: string);
12554
+ }
12555
+
12318
12556
  /** The store surface an inverse op needs (a subset of the framework DataStore). */
12319
12557
  export declare interface UndoApplyStore {
12320
12558
  insert(table: string, row: Record<string, unknown>): Promise<unknown> | unknown;
@@ -12806,15 +13044,12 @@ export declare type WorkflowAdmissionOutcome = {
12806
13044
  * that filled up on this arrival. */
12807
13045
  readonly payload: unknown;
12808
13046
  /** Dispatch the resident submission and return its durably acknowledged
12809
- * execution ID. Recovery owns retries; callers must not execute separately. */
12810
- readonly accept: () => Promise<string>;
12811
- } | {
12812
- readonly kind: 'queued';
12813
- readonly receiptId: string;
12814
- readonly intentId: string;
12815
- readonly mode: string;
12816
- readonly dueAt: number;
12817
- } | {
13047
+ * execution ID — or, when the dispatch went unacknowledged, the receipt
13048
+ * recovery completes it under (`mode: 'unacknowledged'`). That start may
13049
+ * still run, so it is neither an execution ID nor a failure. Recovery owns
13050
+ * retries; callers must not execute separately. */
13051
+ readonly accept: () => Promise<string | WorkflowQueuedAdmission>;
13052
+ } | WorkflowQueuedAdmission | {
12818
13053
  readonly kind: 'dropped';
12819
13054
  readonly retryAfterMs: number;
12820
13055
  } | {
@@ -13118,6 +13353,15 @@ export declare class WorkflowPayloadError extends Error {
13118
13353
  missingFields: ReadonlyArray<string>, detail: string);
13119
13354
  }
13120
13355
 
13356
+ /** A start the caller follows through its receipt instead of an execution ID. */
13357
+ export declare interface WorkflowQueuedAdmission {
13358
+ readonly kind: 'queued';
13359
+ readonly receiptId: string;
13360
+ readonly intentId: string;
13361
+ readonly mode: string;
13362
+ readonly dueAt: number;
13363
+ }
13364
+
13121
13365
  export declare interface WorkflowQueuedControlOptions {
13122
13366
  readonly callerContext?: WorkflowCallerContext;
13123
13367
  readonly cause?: NonNullable<WorkflowControlRequest['cause']>;