@voltro/protocol 0.71.1 → 0.73.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
@@ -835,6 +835,32 @@ export declare const composeAuthStrategies: (strategies: ReadonlyArray<AuthStrat
835
835
  * `fallback` is supplied (the fallback owns that decision).
836
836
  */
837
837
  readonly anonymousTenantRequired?: boolean;
838
+ /**
839
+ * The ONE tenant every anonymous caller resolves to — chosen by the server,
840
+ * never by the request. When set, the `x-tenant` header is not consulted:
841
+ * a client naming its own tenant is exactly the thing a fixed anonymous
842
+ * tenant exists to rule out. Ignored when `fallback` is supplied (that
843
+ * function owns the anonymous subject).
844
+ */
845
+ readonly anonymousTenantId?: string;
846
+ /**
847
+ * The subject's memberships — the ONE source, strategy-independent. Read
848
+ * only when a stored tenant selection exists for the subject and names a
849
+ * tenant other than the credential's, and then on every such request: a
850
+ * membership revoked a moment ago must not keep a selection alive, and a
851
+ * switch made a moment ago must be visible on the next call, so there is
852
+ * no staleness window to configure here. Requires `tenantSelection`.
853
+ */
854
+ readonly memberships?: MembershipsSource;
855
+ /**
856
+ * Where a subject's tenant selection is stored. Read for every matched
857
+ * user / apiKey / serviceAccount subject (one point lookup); a stored
858
+ * tenant the memberships no longer cover is ignored AND cleared here —
859
+ * the revocation of a membership revokes the selection. Requires
860
+ * `memberships`; with only one of the two configured the step does not
861
+ * run, and the composer says so at compose time.
862
+ */
863
+ readonly tenantSelection?: TenantSelectionStore;
838
864
  /**
839
865
  * Add scopes to a resolved Subject from a source the strategy could not
840
866
  * see — typically a role stored in the app's own database.
@@ -1828,6 +1854,12 @@ export declare interface DeleteTarget<Input = unknown> extends NestedTargetField
1828
1854
  readonly identify?: ((input: Input) => string | ReadonlyArray<string>) | undefined;
1829
1855
  }
1830
1856
 
1857
+ /** Pair, list and revoke devices (`devices: true`) for the caller's tenant.
1858
+ * One scope, not three: a device always acts as itself, in the pairer's
1859
+ * tenant, so there is no self / org / other distinction to draw.
1860
+ * `admin:full` satisfies it, as it does every scope. */
1861
+ export declare const DEVICES_MANAGE_SCOPE = "devices:manage";
1862
+
1831
1863
  export declare const diffRows: (prev: ReadonlyArray<PatchRow>, next: ReadonlyArray<PatchRow>, options?: DiffRowsOptions) => RowPatch;
1832
1864
 
1833
1865
  /**
@@ -2622,6 +2654,8 @@ export declare const isReactivityChannelKey: (key: string) => boolean;
2622
2654
 
2623
2655
  export declare const isSystemSubject: (subject: Subject) => boolean;
2624
2656
 
2657
+ export declare const isTenantSelectable: (subject: Subject) => subject is TenantSelectableSubject;
2658
+
2625
2659
  /**
2626
2660
  * Is this descriptor allowed on the wire at all?
2627
2661
  *
@@ -2670,6 +2704,19 @@ export declare const makeScopeCache: (options?: ScopeCacheOptions) => ScopeCache
2670
2704
  */
2671
2705
  export declare const MAX_EVENT_ENVELOPE_BYTES = 7500;
2672
2706
 
2707
+ /** One tenant a subject belongs to. `role` is whatever the app's membership
2708
+ * source knows — it rides along onto `metadata.memberships` and nothing here
2709
+ * interprets it. */
2710
+ export declare interface Membership {
2711
+ readonly tenantId: string;
2712
+ readonly role?: string;
2713
+ }
2714
+
2715
+ /** The app's ONE source of memberships, strategy-independent: the auth plugin
2716
+ * answers from its `memberships` table (`authMemberships(userStore)`), an IdP
2717
+ * project from wherever it keeps them. `input.store` is the app's DataStore. */
2718
+ export declare type MembershipsSource = (subject: Subject, input: AuthStrategyInput) => Promise<ReadonlyArray<Membership>> | ReadonlyArray<Membership>;
2719
+
2673
2720
  /** In-memory store — the default for single-process dev + the test double. */
2674
2721
  export declare const memoryIdempotencyStore: () => IdempotencyStore;
2675
2722
 
@@ -3121,6 +3168,10 @@ export declare interface PluginCacheHandle {
3121
3168
  }) => Effect.Effect<void, unknown>;
3122
3169
  }
3123
3170
 
3171
+ export declare interface PluginCapabilities {
3172
+ readonly mediaPersist?: PluginMediaPersistCapability;
3173
+ }
3174
+
3124
3175
  /**
3125
3176
  * Post-commit change event delivered to a plugin's `onChangeEvent` tap.
3126
3177
  * Minimal, browser-safe shape (protocol must not depend on the database
@@ -3788,6 +3839,31 @@ export declare interface PluginLifecycleContext {
3788
3839
  readonly config: unknown;
3789
3840
  }
3790
3841
 
3842
+ /** Where the offloaded-inference dispatcher puts a media artifact a provider
3843
+ * produced. Every call names its `operationId` — the queued job, its attempt
3844
+ * and the artifact's index — so a store that supports resumable puts stores
3845
+ * one object however often the dispatcher is retried. */
3846
+ export declare interface PluginMediaPersistCapability {
3847
+ readonly put: (args: {
3848
+ readonly bytes: Uint8Array;
3849
+ readonly mediaType: string;
3850
+ readonly tenantId: string | null;
3851
+ readonly operationId: string;
3852
+ }) => Promise<{
3853
+ readonly url: string;
3854
+ readonly refId: string;
3855
+ }>;
3856
+ /** Re-host a provider URL. Absent → the provider's URL is handed on as-is. */
3857
+ readonly ingestUrl?: (args: {
3858
+ readonly url: string;
3859
+ readonly tenantId: string | null;
3860
+ readonly operationId: string;
3861
+ }) => Promise<{
3862
+ readonly url: string;
3863
+ readonly refId: string;
3864
+ }>;
3865
+ }
3866
+
3791
3867
  export declare interface PluginMigration {
3792
3868
  readonly id: string;
3793
3869
  readonly description?: string;
@@ -5217,6 +5293,23 @@ declare const ScopeError_base: Schema.TaggedErrorClass<ScopeError, "ScopeError",
5217
5293
  * resolver already has. */
5218
5294
  export declare type ScopeResolverResult = ReadonlyArray<string> | ScopeDecision;
5219
5295
 
5296
+ /**
5297
+ * The subject with `tenantId` selected — or a refusal.
5298
+ *
5299
+ * The memberships are the caller's to supply because they are the caller's to
5300
+ * know: the auth plugin reads its `memberships` table, an IdP project reads
5301
+ * its own. Whatever the source, the check is this one line, and the list is
5302
+ * written onto `metadata.memberships` in the shape the auth plugin already
5303
+ * uses (`subjectMemberships(subject)` reads it), so a client can render the
5304
+ * switch from any subject the same way.
5305
+ *
5306
+ * Everything else on the subject — scopes, other metadata — passes through
5307
+ * untouched: a selection changes WHERE the subject acts, not what it may do.
5308
+ * Authority for the selected tenant is the resolver's business, which is why
5309
+ * `composeAuthStrategies` applies the selection BEFORE `resolveScopes`.
5310
+ */
5311
+ export declare const selectSubjectTenant: (subject: Subject, tenantId: string, memberships: ReadonlyArray<Membership>) => Subject;
5312
+
5220
5313
  export declare interface ServerErrorEvent {
5221
5314
  /** The thrown value (Error | tagged error | anything). */
5222
5315
  readonly error: unknown;
@@ -5861,6 +5954,48 @@ declare const TenantScopeViolation_base: Schema.TaggedErrorClass<TenantScopeViol
5861
5954
  reason: typeof Schema.String;
5862
5955
  }>;
5863
5956
 
5957
+ /** The subject types a selection can apply to: those with an id AND a tenant.
5958
+ * `anonymous` has no id to remember a selection under; `system` has no
5959
+ * tenant by construction. */
5960
+ export declare type TenantSelectableSubject = Extract<Subject, {
5961
+ type: 'user' | 'apiKey' | 'serviceAccount';
5962
+ }>;
5963
+
5964
+ /** The identity a selection is remembered under — the two fields that name a
5965
+ * subject across credentials. A selection outlives the credential that made
5966
+ * it, which is the point: the next JWT, the next API-key call, finds it. */
5967
+ export declare interface TenantSelectionIdentity {
5968
+ readonly type: TenantSelectableSubject['type'];
5969
+ readonly id: string;
5970
+ }
5971
+
5972
+ /** Refused by name: the target is not among the subject's memberships, or the
5973
+ * subject is of a type that has no tenant to select. A typed error so a
5974
+ * mutation can map it to a response instead of matching on a message. */
5975
+ export declare class TenantSelectionRefused extends TenantSelectionRefused_base {
5976
+ }
5977
+
5978
+ declare const TenantSelectionRefused_base: Schema.TaggedErrorClass<TenantSelectionRefused, "TenantSelectionRefused", {
5979
+ readonly _tag: Schema.tag<"TenantSelectionRefused">;
5980
+ } & {
5981
+ subjectType: typeof Schema.String;
5982
+ subjectId: Schema.NullOr<typeof Schema.String>;
5983
+ tenantId: typeof Schema.String;
5984
+ reason: Schema.Literal<["not-a-member", "subject-has-no-tenant"]>;
5985
+ }>;
5986
+
5987
+ /** Where `composeAuthStrategies` finds a stored selection. `@voltro/runtime`'s
5988
+ * `tenantSelectionStore(store)` is the framework's implementation over the
5989
+ * `_voltro_tenant_selections` table; the interface is here so the composer
5990
+ * stays free of any store. */
5991
+ export declare interface TenantSelectionStore {
5992
+ /** The selected tenant, or null when none is stored. */
5993
+ readonly read: (identity: TenantSelectionIdentity) => Promise<string | null>;
5994
+ /** Forget a selection — called by the composer when the stored tenant is no
5995
+ * longer among the subject's memberships. */
5996
+ readonly clear: (identity: TenantSelectionIdentity) => Promise<void>;
5997
+ }
5998
+
5864
5999
  export declare const toRpc: <Name extends string, Input extends Schema.Schema.Any, Output extends Schema.Schema.Any, Err extends Schema.Schema.All>(descriptor: QueryProcedureDescriptor<Name, Input, Output, Err> | MutationProcedureDescriptor<Name, Input, Output, Err> | ActionProcedureDescriptor<Name, Input, Output, Err> | StreamProcedureDescriptor<Name, Input, Output, Err>) => Rpc.Rpc<Name, Input extends Schema.Struct.Fields ? Schema.Struct<Input> : Input, Stream<Schema.Union<[Schema.Struct<{
5865
6000
  _tag: Schema.Literal<["snapshot"]>;
5866
6001
  revision: typeof Schema.Number;
@@ -6350,6 +6485,17 @@ export declare interface VoltroPlugin {
6350
6485
  * keeps working unchanged.
6351
6486
  */
6352
6487
  readonly bindDataStore?: (store: DataStore, ctx?: PluginBindContext) => void;
6488
+ /**
6489
+ * Capabilities this plugin offers to OTHER framework machinery, by name.
6490
+ *
6491
+ * The framework reads them from the plugin instance at boot — a plugin that
6492
+ * holds the app's storage can hand the offloaded-inference dispatcher the
6493
+ * function that persists a generated media artifact, without the dispatcher
6494
+ * depending on the plugin or the plugin on the dispatcher. Functions here
6495
+ * close over the plugin's bound state, so they are read lazily and called
6496
+ * after `bindDataStore`.
6497
+ */
6498
+ readonly capabilities?: PluginCapabilities;
6353
6499
  /**
6354
6500
  * Post-commit ChangeEvent tap — an `Effect<void, E>` the runtime SUPERVISES.
6355
6501
  * Fires for EVERY committed store change (insert/update/delete on any table),