@voltro/plugin-storage 0.86.0 → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +5484 -0
  2. package/README.md +27 -0
  3. package/THIRD-PARTY-NOTICES.md +2503 -690
  4. package/dist/access.js +26 -27
  5. package/dist/assetRef.js +1 -2
  6. package/dist/avatar.js +1 -2
  7. package/dist/byteSecurity.d.ts +3 -0
  8. package/dist/byteSecurity.js +6 -0
  9. package/dist/connection.js +98 -0
  10. package/dist/constraints.js +1 -2
  11. package/dist/custody.js +131 -0
  12. package/dist/derivatives.js +1 -2
  13. package/dist/env.js +8 -3
  14. package/dist/grant.js +90 -71
  15. package/dist/index.d.ts +453 -215
  16. package/dist/index.js +709 -537
  17. package/dist/ingest.js +7 -2
  18. package/dist/ingestPolicy.js +1 -1
  19. package/dist/metadataError.js +8 -9
  20. package/dist/objectAbandon.js +15 -0
  21. package/dist/objectLifecycle.js +135 -102
  22. package/dist/objectMetadata.js +253 -96
  23. package/dist/objectWriter.js +51 -23
  24. package/dist/pathDurability.js +1 -2
  25. package/dist/physicalReferences.js +53 -0
  26. package/dist/providers.js +451 -307
  27. package/dist/referenceCatalogue.js +1 -2
  28. package/dist/renditionProof.js +81 -0
  29. package/dist/retainedCapture.js +390 -0
  30. package/dist/retainedObjects.js +189 -0
  31. package/dist/rpc.d.ts +1 -15
  32. package/dist/rpc.js +1 -2
  33. package/dist/scanners.js +1 -2
  34. package/dist/server.d.ts +153 -0
  35. package/dist/server.js +4 -0
  36. package/dist/service.js +879 -546
  37. package/dist/sourceIdentity.js +13 -0
  38. package/dist/sourcePin.js +34 -0
  39. package/dist/storageNamespaces.js +52 -29
  40. package/dist/storageNumbers.js +1 -2
  41. package/dist/storageObjectNamespaces.js +170 -122
  42. package/dist/storageReferenceInstances.js +1 -2
  43. package/dist/storageRequest.js +9 -0
  44. package/dist/storageRouting.js +45 -0
  45. package/dist/streamBridge.js +5 -3
  46. package/dist/table.js +2 -2
  47. package/dist/transcodeVideo.js +85 -48
  48. package/dist/transform.js +30 -27
  49. package/dist/transformCache.js +1 -2
  50. package/dist/types.d.ts +115 -137
  51. package/dist/types.js +15 -16
  52. package/package.json +22 -11
package/dist/index.d.ts CHANGED
@@ -1,16 +1,32 @@
1
+ import { ByteStream } from '@voltro/kv/storage';
1
2
  import { ColumnBuilder } from '@voltro/database';
2
3
  import { ColumnType } from '@voltro/database';
4
+ import { ConnectionRef } from '@voltro/kv/connection';
5
+ import { ConnectionRegistryShape } from '@voltro/kv/connection';
3
6
  import { Context } from 'effect';
4
7
  import { DataStore } from '@voltro/database';
5
8
  import { Effect } from 'effect';
6
9
  import { FieldDefinitions } from '@voltro/database';
10
+ import { isStorageKey } from '@voltro/kv/storage';
7
11
  import { Layer } from 'effect';
8
12
  import { mixin } from '@voltro/database';
13
+ import { ObjectMeta } from '@voltro/kv/storage';
9
14
  import { PluginEgressDeclaration } from '@voltro/protocol';
10
15
  import { Readable } from 'node:stream';
11
16
  import { Schema } from 'effect';
12
17
  import { StorageAccessPolicy } from '@voltro/protocol';
13
18
  import { StorageAccessRule } from '@voltro/protocol';
19
+ import { StorageAddress } from '@voltro/kv/storage';
20
+ import { storageAddressIdentity } from '@voltro/kv/storage';
21
+ import { StorageConnectionConnector } from '@voltro/kv/storage';
22
+ import { StorageDestination } from '@voltro/kv/storage';
23
+ import { StorageDestinationRouter } from '@voltro/kv/storage';
24
+ import { StorageError } from '@voltro/kv/storage';
25
+ import { StorageObjectLocation } from '@voltro/kv/storage';
26
+ import { StorageProvider } from '@voltro/kv/storage';
27
+ import { StorageProviderName } from '@voltro/kv/storage';
28
+ import { StoredObject } from '@voltro/kv/storage';
29
+ import { StoredStream } from '@voltro/kv/storage';
14
30
  import { Stream } from 'effect';
15
31
  import { Subject } from '@voltro/protocol';
16
32
  import { Table } from '@voltro/database';
@@ -35,12 +51,24 @@ export declare type AccessPolicy = StorageAccessPolicy;
35
51
  */
36
52
  export declare type AccessRule = StorageAccessRule;
37
53
 
38
- /**
39
- * The provider the APP configured, falling back to the env-derived default when no
40
- * `storagePlugin(...)` is installed. Use this — not `resolveStorageProvider({})` —
41
- * anywhere outside the plugin that needs "the storage this app actually uses".
42
- */
43
- export declare const appStorageProvider: () => StorageProvider;
54
+ /** The app Storage plugin's bound service and durable object metadata. */
55
+ export declare interface AppStorageObjects {
56
+ readonly service: StorageServiceShape;
57
+ readonly metadata: StorageObjectMetadata;
58
+ /** The destination new objects land in. */
59
+ readonly bucket: string;
60
+ }
61
+
62
+ /** Borrow the bound service and object metadata of the app's one Storage plugin, for server code that runs
63
+ * outside the request layer (a Rooms Host, a geometry source). Read it after `bindDataStore`. Refuses with no
64
+ * or several Storage installs, and before binding or with app-supplied ref/grant stores (no metadata boundary). */
65
+ export declare const appStorageObjects: (plugins: ReadonlyArray<VoltroPlugin>) => AppStorageObjects;
66
+
67
+ /** Borrow the app's configured destination. Absence is an error, never a fresh memory store. */
68
+ export declare const appStorageProvider: (plugins: ReadonlyArray<VoltroPlugin>) => StorageProvider;
69
+
70
+ /** Resolve persisted ownership across this app, independently of new defaults. */
71
+ export declare const appStorageRouter: (plugins: ReadonlyArray<VoltroPlugin>) => StorageDestinationRouter;
44
72
 
45
73
  /** A column binding an entity to a stored asset (a `_voltro_storage_refs` id). */
46
74
  export declare function assetRef(options: AssetRefOptions & {
@@ -96,14 +124,7 @@ export declare const buildStorageService: (options: StorageServiceOptions) => St
96
124
  * stream. `subarray` keeps the chunks as views (no copy). */
97
125
  export declare const bytesToStream: (bytes: Uint8Array, chunkSize?: number) => ByteStream;
98
126
 
99
- /**
100
- * A lazy stream of bytes — the streaming counterpart to {@link StoredObject}'s
101
- * eager `bytes`. Backpressured: chunks are pulled on demand, so a multi-GB blob
102
- * never fully materialises in memory. The error channel is {@link StorageError}
103
- * so a stream handed to `putStream` carries the same typed failure the rest of
104
- * the transport does (callers map their source errors into it).
105
- */
106
- export declare type ByteStream = Stream.Stream<Uint8Array, StorageError>;
127
+ export { ByteStream }
107
128
 
108
129
  /** Check an upload against the limits. Returns `null` when ok, else a
109
130
  * `{ reason, detail }` describing the violation. */
@@ -141,24 +162,13 @@ export declare const collectStream: (stream: ByteStream) => Effect.Effect<Uint8A
141
162
  /** Concatenate byte chunks into one contiguous Uint8Array. */
142
163
  export declare const concatBytes: (chunks: ReadonlyArray<Uint8Array>) => Uint8Array;
143
164
 
144
- /**
145
- * The app's provider, or `undefined` when NOBODY named one.
146
- *
147
- * `resolveProvider({})` cannot answer this: its final `default:` arm returns an
148
- * in-process `memoryProvider`, so it is total by construction and every caller
149
- * checking `if (!provider)` is checking a condition that cannot hold. Three of
150
- * them existed — the `--assets` refusals in `voltro data backup` / `export` /
151
- * `restore`, whose whole job is to refuse a flag the app cannot honour. They
152
- * were dead, and what they let through is worse than an unchecked flag: with no
153
- * storage configured, `backup --assets` captured from a FRESH memory provider,
154
- * found nothing in it, and wrote an artefact stamped `assets: { count: 0 }` —
155
- * a rollback story that says the blobs are in there.
156
- *
157
- * "Configured" means one of the two things a person actually did: installed
158
- * `storagePlugin(...)` (which registers here at construction), or set
159
- * `STORAGE_PROVIDER`. A memory provider nobody asked for is not a decision.
160
- */
161
- export declare const configuredStorageProvider: () => StorageProvider | undefined;
165
+ /** Select only transports contributed by this app's plugin instances.
166
+ * Multiple destinations require explicit asset routing, never last-writer wins.
167
+ * An implicit in-memory fallback is never reported as configured storage. */
168
+ export declare const configuredStorageProvider: (plugins: ReadonlyArray<VoltroPlugin>) => StorageProvider | undefined;
169
+
170
+ /** The CLI injects this server adapter into the leaf registry. No plugin imports enter KV. */
171
+ export declare const connectStorage: StorageConnectionConnector;
162
172
 
163
173
  /** `image/*` matches `image/png`; exact otherwise. */
164
174
  export declare const contentTypeAllowed: (contentType: string, allowed: ReadonlyArray<string> | undefined) => boolean;
@@ -173,10 +183,13 @@ export declare const databaseProvider: () => StorageProvider & {
173
183
  bindDataStore: (store: unknown) => void;
174
184
  };
175
185
 
176
- export declare const dataStoreGrantStore: (store: DataStore, configuredScope: StorageReferenceScope) => GrantStore;
186
+ /** Dynamic grants resolve through a currently visible reference. The concrete
187
+ * store retains its namespace through cleanup; grant rows alone never authorize
188
+ * bytes in a newly rebound destination. */
189
+ export declare const dataStoreGrantStore: (store: DataStore, configuredView: StorageReferenceView) => GrantStore;
177
190
 
178
191
  /** Ref store backed by the app's `DataStore` (`_voltro_storage_refs`). */
179
- export declare const dataStoreRefStore: (store: DataStore, configuredScope: StorageReferenceScope) => RefStore;
192
+ export declare const dataStoreRefStore: (store: DataStore, configuredScope: StorageReferenceView) => RefStore;
180
193
 
181
194
  /** Decode a `data:<mime>;base64,<data>` URI to bytes + content type. */
182
195
  export declare const decodeDataUri: (dataUri: string) => {
@@ -207,6 +220,17 @@ export declare const encodeInstream: (bytes: Uint8Array, chunkSize?: number) =>
207
220
  * describes the persisted reference catalogue, not its provider bytes or host. */
208
221
  export declare const ensureStorageReferenceCatalogue: (store: DataStore) => Promise<void>;
209
222
 
223
+ /**
224
+ * The identity a filesystem volume ALREADY carries — never a new one.
225
+ *
226
+ * `filesystemObjectNamespace` mints the marker on an empty root, which is right
227
+ * for a serving process and wrong for a migration: a migrate job started
228
+ * without the volume mounted read an empty directory, minted an identity no
229
+ * serving replica has, and an assignment to it exited 0. A metadata upgrade
230
+ * resolves the namespace through this function, so a missing volume refuses.
231
+ */
232
+ export declare const existingFilesystemObjectNamespace: (root: string) => string;
233
+
210
234
  export declare const fetchIngestBody: (url: string, configuredMaxBytes?: number, policy?: IngestPolicy) => Effect.Effect<StoredObject, StorageError | StorageRejected>;
211
235
 
212
236
  export declare interface FfmpegRenditionSpec {
@@ -311,7 +335,7 @@ export declare interface ImageEdit {
311
335
  readonly crop?: Region;
312
336
  }
313
337
 
314
- /** Where `ingestUrl` may fetch from — see `ssrfReason` in `@voltro/runtime`. */
338
+ /** Where `ingestUrl` may fetch from — see `ssrfReason` in `@voltro/protocol`. */
315
339
  export declare interface IngestPolicy {
316
340
  /** Hosts (or `host:port`, or `*.suffix`) reachable although internal — a
317
341
  * deliberate internal source, or a local test server. Everything else that
@@ -319,8 +343,13 @@ export declare interface IngestPolicy {
319
343
  readonly allowHosts?: ReadonlyArray<string>;
320
344
  /** Redirect hops followed, each checked again. Default 5. */
321
345
  readonly maxRedirects?: number;
346
+ /** Refuse HTTP at the initial source and every redirect before fetching.
347
+ * Host allowlisting does not bypass this transport requirement. Default false. */
348
+ readonly requireHttps?: boolean;
322
349
  }
323
350
 
351
+ export { isStorageKey }
352
+
324
353
  /**
325
354
  * Filter + page a `StorageService.listRefs` / media-library query. Every
326
355
  * filter is optional and ANDed; omit them all to page the whole (tenant-scoped)
@@ -356,50 +385,88 @@ export declare interface ListRefsResult {
356
385
  readonly nextOffset: number | null;
357
386
  }
358
387
 
359
- /** DataStore-backed metadata boundary, intentionally no provider access. The
360
- * service must use reserve -> provider.put -> confirm and retain failed intents. */
361
- export declare const makeStorageObjectMetadata: (store: DataStore, configuredScope: StorageReferenceScope) => {
362
- scope: Readonly<{
363
- storageInstance: string;
364
- objectNamespace: string;
365
- }>;
366
- /** The put intent a reference was written under, if it was written through an operation at all. */
388
+ /** Each call captures its request tenant; no request changes the selected cell. */
389
+ export declare const makeStorageDestinationRouter: (binding: StorageDestinationBinding) => StorageDestinationRouter;
390
+
391
+ /** Resolve every operation to one concrete physical namespace. Metadata-only
392
+ * discovery stays instance scoped; the persisted intent is validated against
393
+ * the retained destination before it can authorize reads, claims or deletion. */
394
+ export declare const makeStorageObjectMetadata: (store: DataStore, configuredView: StorageReferenceView, admit?: StorageObjectAdmission) => {
395
+ scopeFor: (input: {
396
+ readonly bucket: string;
397
+ readonly tenantId: string | null;
398
+ } | {
399
+ readonly refId: string;
400
+ }) => Promise<StorageReferenceScope>;
367
401
  intent: (refId: string) => Promise<{
368
402
  identity: {
369
- readonly storageInstance: string;
370
- readonly objectNamespace: string;
371
403
  readonly tenantId: string | null;
372
404
  readonly bucket: string;
405
+ readonly objectNamespace: string;
406
+ readonly fingerprint: string;
407
+ readonly checksum: string;
408
+ readonly storageInstance: string;
373
409
  readonly operationId: string;
410
+ readonly logicalKey: string;
411
+ readonly retainedSource?: {
412
+ readonly refId: string;
413
+ readonly digest: string;
414
+ };
415
+ };
416
+ ref: StorageRef;
417
+ } | undefined>;
418
+ operationStatus: (input: {
419
+ readonly bucket: string;
420
+ readonly tenantId: string | null;
421
+ readonly operationId: string;
422
+ }) => Promise<{
423
+ observation: StorageOperationState;
424
+ scope: StorageReferenceScope;
425
+ }>;
426
+ lookup: (input: Omit<StorageObjectOperationScope, keyof StorageReferenceScope>, claim: string) => Promise<{
427
+ state: "ready" | "reserved" | "deleting" | "deleted";
428
+ ref: StorageRef;
429
+ held: boolean;
430
+ } | undefined>;
431
+ operation: (key: StorageObjectOperationKey) => Promise<{
432
+ state: "ready" | "reserved" | "deleting" | "deleted";
433
+ identity: {
434
+ readonly tenantId: string | null;
435
+ readonly bucket: string;
436
+ readonly objectNamespace: string;
374
437
  readonly fingerprint: string;
375
438
  readonly checksum: string;
439
+ readonly storageInstance: string;
440
+ readonly operationId: string;
376
441
  readonly logicalKey: string;
442
+ readonly retainedSource?: {
443
+ readonly refId: string;
444
+ readonly digest: string;
445
+ };
377
446
  };
378
447
  ref: StorageRef;
379
448
  } | undefined>;
380
- /** `claim` is registered only where the caller receives the ref: here when
381
- * the operation is already ready (`joined`), else on `confirm`. A claimant
382
- * whose provider write fails therefore holds nothing. */
383
449
  reserve: (identity: StorageObjectIdentity, proposed: StorageRef, quota?: StorageQuota, claim?: string) => Promise<{
384
450
  ref: StorageRef;
385
451
  ready: boolean;
386
452
  outcome: PutOutcome;
387
453
  }>;
388
454
  confirm: (identity: StorageObjectIdentity, claim?: string) => Promise<StorageRef>;
389
- /** With a `claim`, releases that claim and deletes the object only when no
390
- * other claimant holds it; `released` alone (no `operation`) means another
391
- * claimant still does. Without one, the whole object goes, claims included. */
392
455
  beginDelete: (refId: string, tenantId?: string | null, claim?: string) => Promise<{
393
456
  released: true;
394
457
  identity: {
395
- readonly storageInstance: string;
396
- readonly objectNamespace: string;
397
458
  readonly tenantId: string | null;
398
459
  readonly bucket: string;
399
- readonly operationId: string;
460
+ readonly objectNamespace: string;
400
461
  readonly fingerprint: string;
401
462
  readonly checksum: string;
463
+ readonly storageInstance: string;
464
+ readonly operationId: string;
402
465
  readonly logicalKey: string;
466
+ readonly retainedSource?: {
467
+ readonly refId: string;
468
+ readonly digest: string;
469
+ };
403
470
  };
404
471
  operation?: never;
405
472
  } | {
@@ -417,14 +484,18 @@ export declare const makeStorageObjectMetadata: (store: DataStore, configuredSco
417
484
  readonly deleteKey: string | null;
418
485
  };
419
486
  identity: {
420
- readonly storageInstance: string;
421
- readonly objectNamespace: string;
422
487
  readonly tenantId: string | null;
423
488
  readonly bucket: string;
424
- readonly operationId: string;
489
+ readonly objectNamespace: string;
425
490
  readonly fingerprint: string;
426
491
  readonly checksum: string;
492
+ readonly storageInstance: string;
493
+ readonly operationId: string;
427
494
  readonly logicalKey: string;
495
+ readonly retainedSource?: {
496
+ readonly refId: string;
497
+ readonly digest: string;
498
+ };
428
499
  };
429
500
  }>;
430
501
  confirmDelete: (identity: StorageObjectIdentity) => Promise<boolean>;
@@ -446,11 +517,7 @@ export declare interface MultipartTicketPayload extends Omit<UploadTicketPayload
446
517
  readonly size: number;
447
518
  }
448
519
 
449
- /** Result of a provider `put` / `head`. */
450
- export declare interface ObjectMeta {
451
- readonly size: number;
452
- readonly etag?: string;
453
- }
520
+ export { ObjectMeta }
454
521
 
455
522
  /** Parse a clamd INSTREAM reply: `stream: OK` → clean; `stream: <T> FOUND`
456
523
  * → infected; anything else → throws. Exposed for testing. */
@@ -487,6 +554,8 @@ export declare const putAvatar: (storage: StorageServiceShape, args: {
487
554
 
488
555
  /** Input to `StorageService.put`. */
489
556
  export declare interface PutInput {
557
+ /** Server-owned logical destination; omitted new writes use this plugin's selection. */
558
+ readonly destination?: string;
490
559
  readonly bytes: Uint8Array;
491
560
  readonly contentType: string;
492
561
  /** Active org id — scopes the key prefix + the ref row. */
@@ -578,9 +647,13 @@ export declare interface RefStatistics {
578
647
  }
579
648
 
580
649
  export declare interface RefStore {
650
+ /** Complete metadata-only ownership observation; no pagination or recent limit. */
651
+ readonly listObjectReferences: () => Effect.Effect<ReadonlyArray<StorageObjectReference>, StorageError>;
581
652
  /** Stable identity of this actual reference view. Shared catalogues expose
582
653
  * the same identity; independent/process-local stores must expose their own. */
583
654
  readonly catalogueId: Effect.Effect<string, StorageError>;
655
+ /** Durable reservations retain bytes before a reference is confirmed. */
656
+ readonly hasObjectReservation?: (namespace: string, key: string) => Effect.Effect<boolean, StorageError>;
584
657
  /** Aggregate the complete reference index without a recent-object limit. */
585
658
  readonly statistics: () => Effect.Effect<RefStatistics, StorageError>;
586
659
  readonly insert: (ref: StorageRef) => Effect.Effect<void, StorageError>;
@@ -590,10 +663,8 @@ export declare interface RefStore {
590
663
  * same id refund the tenant counter exactly once. */
591
664
  readonly delete: (id: string) => Effect.Effect<boolean, StorageError>;
592
665
  readonly listRecent: (limit: number) => Effect.Effect<ReadonlyArray<StorageRef>, StorageError>;
593
- /** How many refs (in this bucket) point at this provider key — drives
594
- * refcount-before-delete so a dedup'd object isn't yanked from under a
595
- * sibling ref. */
596
- readonly countByKey: (bucket: string, key: string) => Effect.Effect<number, StorageError>;
666
+ /** Logical address count; physical cleanup also observes other destinations. */
667
+ readonly countByAddress: (address: StorageAddress) => Effect.Effect<number, StorageError>;
597
668
  /** Total bytes + object count for a tenant — LIVE sum over the ref rows
598
669
  * (reporting / dashboards). The quota GATE does not read this — it rides
599
670
  * the atomic `consumeUsage` counter, which a read-then-compare here could
@@ -630,7 +701,7 @@ export declare interface RefStore {
630
701
  readonly setVisibility?: ((id: string, visibility: StorageVisibility) => Effect.Effect<StorageRef | null, StorageError>) | undefined;
631
702
  /** How many PUBLIC refs point at this provider key — whether the object must
632
703
  * stay publicly readable. */
633
- readonly countPublicByKey?: ((bucket: string, key: string) => Effect.Effect<number, StorageError>) | undefined;
704
+ readonly countPublicByAddress?: ((address: StorageAddress) => Effect.Effect<number, StorageError>) | undefined;
634
705
  }
635
706
 
636
707
  /** A region of an image in pixels. */
@@ -644,6 +715,8 @@ declare interface Region {
644
715
  /** One derived output a `Transcoder` produces from a source asset. Each becomes
645
716
  * a normal storage ref linked back to the original via `derivedFrom`. */
646
717
  export declare interface Rendition {
718
+ /** Optional structured producer attestation; unproven outputs keep it absent. */
719
+ readonly derivation?: RenditionDerivation;
647
720
  readonly bytes: Uint8Array;
648
721
  readonly contentType: string;
649
722
  /** Derivative class — e.g. `'rendition'` (a playable variant) or `'poster'`. */
@@ -652,7 +725,47 @@ export declare interface Rendition {
652
725
  readonly label?: string;
653
726
  }
654
727
 
655
- /** Nullable additions deliberately fail closed until the explicit upgrade. */
728
+ /** Producer attestation. Digests bind a normalized recipe and the observed
729
+ * engine build; the consumer still admits allowed recipes, engines and rights. */
730
+ export declare interface RenditionDerivation {
731
+ readonly recipe: {
732
+ readonly format: string;
733
+ readonly digest: string;
734
+ };
735
+ readonly engine: {
736
+ readonly name: string;
737
+ readonly version: string;
738
+ readonly digest: string;
739
+ };
740
+ }
741
+
742
+ /** Storage-verified input/output identity around a producer attestation.
743
+ * Not an approval, rights grant or proof of complete external dependencies. */
744
+ export declare interface RenditionProof extends RenditionDerivation {
745
+ readonly version: 1;
746
+ readonly source: {
747
+ readonly id: string;
748
+ readonly metadataDigest: string;
749
+ readonly checksum: string;
750
+ readonly size: number;
751
+ readonly contentType: string;
752
+ };
753
+ readonly output: {
754
+ readonly checksum: string;
755
+ readonly size: number;
756
+ readonly contentType: string;
757
+ };
758
+ readonly digest: string;
759
+ }
760
+
761
+ /**
762
+ * Nullable additions deliberately fail closed until the explicit upgrade.
763
+ *
764
+ * The refusal names every table still holding unassigned rows and how many,
765
+ * because the commonest way to meet it AFTER a completed assignment is a
766
+ * reference a previous-version replica wrote while the assignment ran — and a
767
+ * recorded file migration does not run again by itself.
768
+ */
656
769
  export declare const requireStorageObjectNamespaces: (store: DataStore) => Promise<void>;
657
770
 
658
771
  /** A `ListRefsInput` after the service normalizes its defaults / clamps — what
@@ -670,11 +783,7 @@ export declare interface ResolvedListQuery {
670
783
 
671
784
  export declare const resolveStorageProvider: (options: StoragePluginOptions) => StorageProvider;
672
785
 
673
- /**
674
- * The signing secret. A dedicated `VOLTRO_STORAGE_SECRET` is preferred so
675
- * grant tokens and session cookies can be rotated independently; absent
676
- * that we fall back to the session secret so it works out of the box.
677
- */
786
+ /** The secret new storage tokens are signed with — the first of {@link resolveStorageSecrets}. */
678
787
  export declare const resolveStorageSecret: () => string;
679
788
 
680
789
  /** What a resumable ticket authorizes: one chunked upload session, until `exp`. */
@@ -685,6 +794,63 @@ export declare interface ResumableTicketPayload extends Omit<UploadTicketPayload
685
794
  readonly size: number;
686
795
  }
687
796
 
797
+ export declare interface RetainedCaptureKey {
798
+ readonly id: string;
799
+ readonly instance: string;
800
+ readonly objectNamespace: string;
801
+ readonly tenantId: string | null;
802
+ readonly bucket: string;
803
+ /** Binds the caller's immutable publication/configuration admission. */
804
+ readonly contextDigest: string;
805
+ /** Different configured source; absent for same-instance custody. */
806
+ readonly source?: {
807
+ readonly instance: string;
808
+ readonly objectNamespace: string;
809
+ readonly bucket: string;
810
+ };
811
+ }
812
+
813
+ export declare interface RetainedCapturePin extends RetainedCaptureKey {
814
+ readonly count: number;
815
+ readonly digest: string;
816
+ }
817
+
818
+ export declare const retainedCaptureTables: readonly [ Table<"_voltro_storage_retained_captures", FieldDefinitions<{
819
+ readonly id: ColumnBuilder<string, "text", boolean>;
820
+ readonly captureId: ColumnBuilder<string, "text", boolean>;
821
+ readonly instance: ColumnBuilder<string, "text", boolean>;
822
+ readonly tenantId: ColumnBuilder<string | null, "text", boolean>;
823
+ readonly bucket: ColumnBuilder<string, "text", boolean>;
824
+ readonly contextDigest: ColumnBuilder<string, "text", boolean>;
825
+ readonly state: ColumnBuilder<"abandoned" | "complete" | "capturing", "text", boolean>;
826
+ readonly nonce: ColumnBuilder<string, "text", boolean>;
827
+ readonly config: ColumnBuilder<unknown, "json", boolean>;
828
+ readonly count: ColumnBuilder<number, "integer", boolean>;
829
+ readonly digest: ColumnBuilder<string | null, "text", boolean>;
830
+ readonly cleanupOrdinal: ColumnBuilder<number, "integer", true>;
831
+ }>, false, never>, Table<"_voltro_storage_retained_capture_items", FieldDefinitions<{
832
+ readonly id: ColumnBuilder<string, "text", boolean>;
833
+ readonly captureId: ColumnBuilder<string, "text", boolean>;
834
+ readonly ordinal: ColumnBuilder<number, "integer", boolean>;
835
+ readonly sourceId: ColumnBuilder<string, "text", boolean>;
836
+ readonly sourceDigest: ColumnBuilder<string, "text", boolean>;
837
+ readonly operationId: ColumnBuilder<string, "text", boolean>;
838
+ readonly pin: ColumnBuilder<unknown, "json", boolean>;
839
+ }>, false, "storage_retained_capture_page">];
840
+
841
+ export declare interface RetainedStorageObject {
842
+ readonly version: 1;
843
+ readonly tenantId: string | null;
844
+ readonly bucket: string;
845
+ readonly operationId: string;
846
+ readonly sourceId: string;
847
+ readonly sourceDigest: string;
848
+ readonly refId: string;
849
+ readonly checksum: string;
850
+ readonly size: number;
851
+ readonly contentType: string;
852
+ }
853
+
688
854
  export declare const s3Provider: (options: S3ProviderOptions) => StorageProvider;
689
855
 
690
856
  export declare interface S3ProviderOptions {
@@ -706,10 +872,7 @@ export declare interface S3ProviderOptions {
706
872
  readonly publicAcl?: boolean;
707
873
  }
708
874
 
709
- declare type ScopedStorageObjectIdentity = StorageObjectIdentity & StorageReferenceScope;
710
-
711
- /** Register the app's storage provider (or clear with `undefined`). */
712
- export declare const setActiveStorageProvider: (provider: StorageProvider | undefined) => void;
875
+ export declare type ScopedStorageObjectIdentity = StorageObjectIdentity & StorageReferenceScope;
713
876
 
714
877
  /** Sign a finalize token valid for `ttlSeconds`. */
715
878
  export declare const signFinalizeTicket: (fields: Omit<FinalizeTicketPayload, "exp" | "version">, ttlSeconds: number, secret?: string) => string;
@@ -753,6 +916,88 @@ declare const StorageAccessDenied_base: Schema.Class<StorageAccessDenied, Schema
753
916
  readonly reason: Schema.String;
754
917
  }>, YieldableError>;
755
918
 
919
+ export { StorageAddress }
920
+
921
+ export { storageAddressIdentity }
922
+
923
+ export declare const storageByteSecurityHeaders: (contentType: string) => Readonly<Record<string, string>>;
924
+
925
+ /** Private-byte custody for trusted server coordinators. storagePlugin supplies
926
+ * this service only for its exact instance. Pins confer no end-user authority. */
927
+ export declare class StorageCustody extends StorageCustody_base {
928
+ /** Reconstruct a transfer from configured services on each worker. Both must
929
+ * use the same application database; only the destination stores custody. */
930
+ static withSource(input: {
931
+ readonly source: StorageCustodyShape;
932
+ readonly destination: StorageCustodyShape;
933
+ }): Effect.Effect<StorageCustodyShape, StorageError>;
934
+ static forInstance(selection?: StorageCustodyInstance): Context.Service<StorageCustody, StorageCustodyShape>;
935
+ }
936
+
937
+ declare const StorageCustody_base: Context.ServiceClass<StorageCustody, string, StorageCustodyShape>;
938
+
939
+ /** Uses the same instance selector as storagePlugin. */
940
+ export declare interface StorageCustodyInstance {
941
+ readonly alias?: string;
942
+ readonly name?: string;
943
+ }
944
+
945
+ /** Trusted server selection. Source enumeration runs in the caller's DB TX. */
946
+ export declare interface StorageCustodyRegistration {
947
+ readonly id: string;
948
+ readonly tenantId: string | null;
949
+ readonly contextDigest: string;
950
+ readonly maxBytes: number;
951
+ readonly sources: () => Iterable<string | StorageSourcePin> | AsyncIterable<string | StorageSourcePin>;
952
+ }
953
+
954
+ export declare interface StorageCustodyShape {
955
+ /** Read and lock metadata from this configured source, including rendition
956
+ * provenance. No provider I/O, access grant or recipe/rights approval.
957
+ * Bind the pin to the caller's immutable candidate and contextDigest, then
958
+ * pass it to register in the same application transaction. */
959
+ readonly inspect: (tx: DataStore, input: {
960
+ readonly id: string;
961
+ readonly tenantId: string | null;
962
+ }) => Effect.Effect<StorageSourcePin, StorageError>;
963
+ /** Register stable work in the same transaction as the caller's source pin.
964
+ * The transaction must belong to this plugin's bound application database. */
965
+ readonly register: (tx: DataStore, input: StorageCustodyRegistration) => Effect.Effect<{
966
+ readonly key: RetainedCaptureKey;
967
+ readonly count: number;
968
+ }, StorageError>;
969
+ /** Copy/verify bytes outside a DB transaction; retries reuse registered work. */
970
+ readonly run: (key: RetainedCaptureKey) => Effect.Effect<void, StorageError>;
971
+ /** Seal with the caller's final manifest and durable result in one DB TX. */
972
+ readonly seal: (tx: DataStore, key: RetainedCaptureKey) => Effect.Effect<RetainedCapturePin, StorageError>;
973
+ /** Private pins. Consume through EOF and stage output until digest success.
974
+ * Pass the same application transaction when enumerating a just-sealed pin. */
975
+ readonly items: (pin: RetainedCapturePin, tx?: DataStore) => Stream.Stream<RetainedStorageObject, StorageError>;
976
+ /** Trusted server read of a verified member pin. Not an access grant.
977
+ * Tenant admission must come from the caller; finish byte verification at EOF. */
978
+ readonly read: (capture: RetainedCapturePin, object: RetainedStorageObject, scope: {
979
+ readonly tenantId: string | null;
980
+ readonly maxBytes: number;
981
+ }) => Effect.Effect<{
982
+ readonly stream: ByteStream;
983
+ readonly contentType: string;
984
+ readonly size: number;
985
+ }, StorageError>;
986
+ /** Close admission before cleanup. Repeat cleanup after uncertain/late I/O. */
987
+ readonly abandon: (key: RetainedCaptureKey) => Effect.Effect<void, StorageError>;
988
+ }
989
+
990
+ export { StorageDestination }
991
+
992
+ declare interface StorageDestinationBinding {
993
+ readonly selected: () => string;
994
+ readonly directDestination: string | undefined;
995
+ readonly directProvider: () => StorageProvider;
996
+ readonly registry: () => ConnectionRegistryShape | undefined;
997
+ }
998
+
999
+ export { StorageDestinationRouter }
1000
+
756
1001
  /**
757
1002
  * Where the bytes go, read from the same options and env `resolveProvider`
758
1003
  * reads: an object store is a destination outside the installation; the
@@ -760,20 +1005,7 @@ declare const StorageAccessDenied_base: Schema.Class<StorageAccessDenied, Schema
760
1005
  */
761
1006
  export declare const storageEgressOf: (options: StoragePluginOptions) => ReadonlyArray<PluginEgressDeclaration>;
762
1007
 
763
- /**
764
- * Storage failure. `transient` drives retry — `true` for 429 / 5xx /
765
- * network blips, `false` for 4xx / config / not-found. Surfaced typed so
766
- * handlers can `Effect.catchTag('StorageError', …)`.
767
- */
768
- export declare class StorageError extends StorageError_base {
769
- }
770
-
771
- declare const StorageError_base: Schema.Class<StorageError, Schema.TaggedStruct<"StorageError", {
772
- readonly provider: Schema.String;
773
- readonly message: Schema.String;
774
- readonly transient: Schema.Boolean;
775
- readonly status: Schema.optional<Schema.Number>;
776
- }>, YieldableError>;
1008
+ export { StorageError }
777
1009
 
778
1010
  /** A persisted, explicit share of one object with one principal. */
779
1011
  export declare interface StorageGrant {
@@ -803,9 +1035,11 @@ export declare type StorageGuard = (ctx: {
803
1035
  * and `BASE_NAME` is not exported, so a caller minting a ticket outside the
804
1036
  * plugin had to guess it. Guessing is exactly what a `string` parameter invites.
805
1037
  *
806
- * const writeScope = await Effect.runPromise(service.uploadTicketScope())
1038
+ * Use this name in the ticket's `instance` field. The ticket also captures
1039
+ * the selected destination, owner, tenant, content type and visibility.
1040
+ * const writeScope = await Effect.runPromise(service.uploadTicketScope({ tenantId, destination }))
807
1041
  * const ticket = signUploadTicket(
808
- * { writeScope, instance: storageInstanceName(options), key, ct: contentType, sub: ownerId, tenant: tenantId, vis: 'private' },
1042
+ * { writeScope, destination, instance: storageInstanceName(options), key, ct: contentType, sub: ownerId, tenant: tenantId, vis: 'private' },
809
1043
  * 300,
810
1044
  * )
811
1045
  *
@@ -815,16 +1049,24 @@ export declare type StorageGuard = (ctx: {
815
1049
  */
816
1050
  export declare const storageInstanceName: (options?: StoragePluginOptions) => string;
817
1051
 
818
- export declare interface StorageObjectIdentity {
819
- readonly tenantId: string | null;
820
- readonly bucket: string;
821
- readonly operationId: string;
1052
+ /** Internal database-only admission. Runs under the storage tenant lock, in
1053
+ * the same transaction as reserve/join/confirm. It must lock or write its own
1054
+ * durable owner row; observing a live flag before this transaction is unsafe.
1055
+ * No provider, engine or other external I/O belongs in this callback. */
1056
+ declare type StorageObjectAdmission = (tx: DataStore, identity: StorageObjectIdentity, phase: 'reserve' | 'confirm') => Promise<void>;
1057
+
1058
+ export declare interface StorageObjectIdentity extends StorageObjectOperationKey {
822
1059
  /** Hash of all immutable put inputs, including content and access metadata. */
823
1060
  readonly fingerprint: string;
824
1061
  /** SHA-256 of the actual normalized provider bytes, separate from metadata. */
825
1062
  readonly checksum: string;
826
1063
  /** Already tenant-scoped logical provider key. */
827
1064
  readonly logicalKey: string;
1065
+ /** Private retained-input provenance; not an access rule or CMS policy. */
1066
+ readonly retainedSource?: {
1067
+ readonly refId: string;
1068
+ readonly digest: string;
1069
+ };
828
1070
  }
829
1071
 
830
1072
  /** Internal persistence substrate. Service wiring must commit quota and refs in
@@ -855,6 +1097,8 @@ export declare const storageObjectLifecycleTables: ( Table<"_voltro_storage_oper
855
1097
  readonly claim: ColumnBuilder<string, "text", boolean>;
856
1098
  }>, false, never>)[];
857
1099
 
1100
+ export { StorageObjectLocation }
1101
+
858
1102
  export declare type StorageObjectMetadata = ReturnType<typeof makeStorageObjectMetadata>;
859
1103
 
860
1104
  /** Credential-independent identity of a physical object-key namespace.
@@ -870,11 +1114,42 @@ export declare interface StorageObjectNamespaceMigrationResult {
870
1114
  readonly pendingDeletes: readonly StorageObjectNamespaceAssignment[];
871
1115
  }
872
1116
 
1117
+ export declare interface StorageObjectOperationKey {
1118
+ readonly tenantId: string | null;
1119
+ readonly bucket: string;
1120
+ readonly operationId: string;
1121
+ }
1122
+
1123
+ export declare interface StorageObjectOperationScope extends StorageReferenceScope {
1124
+ readonly tenantId: string | null;
1125
+ readonly bucket: string;
1126
+ readonly operationId: string;
1127
+ }
1128
+
1129
+ export declare type StorageObjectReference = Pick<StorageRef, 'bucket' | 'tenantId' | 'key' | 'visibility'> & Partial<StorageReferenceScope>;
1130
+
1131
+ /** A durable metadata observation, not a claim on the object or a provider HEAD. */
1132
+ export declare type StorageOperationState = {
1133
+ readonly state: 'absent' | 'reserved' | 'deleting' | 'deleted';
1134
+ } | {
1135
+ readonly state: 'ready';
1136
+ readonly ref: StorageRef;
1137
+ };
1138
+
1139
+ /** Metadata state qualified by the actual selected storage destination. */
1140
+ export declare type StorageOperationStatus = StorageOperationState & {
1141
+ /** Configured instance, actual catalogue, physical namespace and logical bucket. */
1142
+ readonly scope: string;
1143
+ /** Selected logical destination; retain it with the scope before minting a ticket. */
1144
+ readonly destination: string;
1145
+ };
1146
+
873
1147
  export declare const storagePlugin: (options?: StoragePluginOptions) => VoltroPlugin;
874
1148
 
875
1149
  export declare interface StoragePluginOptions {
876
1150
  /** `'s3' | 'minio' | 'filesystem' | 'memory'` (built from env) or a
877
1151
  * `StorageProvider`. Default: `STORAGE_PROVIDER` env, else `'memory'`. */
1152
+ readonly connection?: ConnectionRef<'storage'>;
878
1153
  readonly provider?: StorageProviderName | StorageProvider;
879
1154
  /** Bucket name (s3/minio). Default `S3_BUCKET`/`STORAGE_BUCKET` env, else 'voltro'. */
880
1155
  readonly bucket?: string;
@@ -1018,100 +1293,9 @@ export declare interface StoragePluginOptions {
1018
1293
  readonly name?: string;
1019
1294
  }
1020
1295
 
1021
- /**
1022
- * A backend transport. Keyed by an opaque object key (the service owns key
1023
- * derivation + tenant prefixing); knows nothing about refs, tenants, or
1024
- * access policy.
1025
- */
1026
- export declare interface StorageProvider {
1027
- /** Stable physical object-key namespace; exclude credentials and plugin aliases. */
1028
- readonly objectNamespace: Effect.Effect<string, StorageError>;
1029
- /**
1030
- * Where a FILESYSTEM provider writes. Absent for every other provider.
1031
- *
1032
- * It exists so a caller can ask a question this interface otherwise cannot
1033
- * answer: does what I write here survive the process? A rollback capture
1034
- * written for a pod that then dies is worthless, and `filesystem` is the one
1035
- * provider that can be either — a mounted volume or an ephemeral container
1036
- * directory. A deployment pointed this out: their check for "storage is
1037
- * configured" passed on a `/tmp` path with no volume behind it.
1038
- */
1039
- readonly root?: string;
1040
- readonly name: string;
1041
- /** True when `getUrl` returns a real presigned URL (s3/minio). When
1042
- * false the service serves bytes via the `/_voltro/storage/:id`
1043
- * endpoint instead (filesystem/memory). */
1044
- readonly presigns: boolean;
1045
- readonly put: (key: string, bytes: Uint8Array, meta: {
1046
- readonly contentType: string;
1047
- readonly public?: boolean;
1048
- }) => Effect.Effect<ObjectMeta, StorageError>;
1049
- readonly get: (key: string) => Effect.Effect<StoredObject, StorageError>;
1050
- readonly delete: (key: string) => Effect.Effect<void, StorageError>;
1051
- /** Presigned GET (s3/minio) for private reads. */
1052
- readonly getUrl: (key: string, expiresInSec: number) => Effect.Effect<string, StorageError>;
1053
- /** A direct, cacheable URL for a PUBLIC object — virtual-hosted bucket
1054
- * URL or a configured CDN base. `null` when the provider can't serve
1055
- * publicly without the app (filesystem/memory) → the service falls
1056
- * back to the `/_voltro/storage/:id` route. */
1057
- readonly publicUrl?: (key: string) => string | null;
1058
- /** Make an object publicly readable at the provider, or not (s3/minio with
1059
- * per-object ACLs). Absent where the app decides per request (database,
1060
- * filesystem, memory) — there a ref's visibility is the whole answer. */
1061
- readonly setObjectPublic?: (key: string, isPublic: boolean) => Effect.Effect<void, StorageError>;
1062
- /** Presigned PUT for direct-to-bucket uploads (s3/minio). Absent on
1063
- * providers that can't presign uploads. */
1064
- readonly getUploadUrl?: (key: string, expiresInSec: number, contentType: string) => Effect.Effect<string, StorageError>;
1065
- readonly createMultipartUpload?: (key: string, contentType: string, opts?: {
1066
- readonly public?: boolean;
1067
- }) => Effect.Effect<{
1068
- readonly uploadId: string;
1069
- }, StorageError>;
1070
- readonly presignUploadPart?: (key: string, uploadId: string, partNumber: number, expiresInSec: number) => Effect.Effect<string, StorageError>;
1071
- readonly completeMultipartUpload?: (key: string, uploadId: string, parts: ReadonlyArray<{
1072
- readonly partNumber: number;
1073
- readonly etag: string;
1074
- }>) => Effect.Effect<{
1075
- readonly etag?: string;
1076
- }, StorageError>;
1077
- readonly abortMultipartUpload?: (key: string, uploadId: string) => Effect.Effect<void, StorageError>;
1078
- readonly head: (key: string) => Effect.Effect<ObjectMeta | null, StorageError>;
1079
- /**
1080
- * Stream an object's bytes in chunks instead of buffering the whole blob
1081
- * ({@link get}). Present on backends with a real streaming read (filesystem,
1082
- * s3, azure). OPTIONAL: when a provider omits it, {@link getObjectStream}
1083
- * transparently falls back to `get` + a chunked in-memory stream — correct
1084
- * for the small-blob backends (memory / database) that have nothing to
1085
- * stream from.
1086
- */
1087
- readonly getStream?: (key: string) => Effect.Effect<StoredStream, StorageError>;
1088
- /**
1089
- * Consume a {@link ByteStream} and store it via the backend's chunked /
1090
- * multipart upload, so a large upload never fully materialises in memory.
1091
- * Present on filesystem (streamed atomic write), s3 (multipart via
1092
- * `@aws-sdk/lib-storage`), azure (`uploadStream`). OPTIONAL: when absent,
1093
- * {@link putObjectStream} buffers the stream and calls `put`. `totalSize`,
1094
- * when the caller knows it (e.g. from an export manifest), lets the backend
1095
- * report an accurate size without a follow-up HEAD.
1096
- */
1097
- readonly putStream?: (key: string, stream: ByteStream, meta: {
1098
- readonly contentType: string;
1099
- readonly public?: boolean;
1100
- readonly totalSize?: number;
1101
- }) => Effect.Effect<ObjectMeta, StorageError>;
1102
- /**
1103
- * Read a byte range `[start, endInclusive]` (HTTP Range / fd offset). Enables
1104
- * seekable + resumable reads. OPTIONAL and best-effort: whole-object resume
1105
- * already works via content-addressing (skip an asset whose hash is present
1106
- * at the target), so this is an optimisation, not a correctness requirement.
1107
- */
1108
- readonly getRange?: (key: string, start: number, endInclusive: number) => Effect.Effect<Uint8Array, StorageError>;
1109
- /** Providers that store bytes via the app's DataStore (e.g. `database`)
1110
- * receive it here once it exists — bound by the plugin at boot. */
1111
- readonly bindDataStore?: (store: unknown) => void;
1112
- }
1296
+ export { StorageProvider }
1113
1297
 
1114
- export declare type StorageProviderName = 's3' | 'minio' | 'azure' | 'filesystem' | 'memory' | 'database';
1298
+ export { StorageProviderName }
1115
1299
 
1116
1300
  export declare const storagePutIntentsTable: Table<"_voltro_storage_put_intents", FieldDefinitions<{
1117
1301
  readonly id: ColumnBuilder<string, "text", boolean>;
@@ -1166,8 +1350,13 @@ export declare interface StorageRef {
1166
1350
  readonly derivedFrom?: string | null;
1167
1351
  /** Derivative label — e.g. `'rendition'` / `'poster'`; null on an original. */
1168
1352
  readonly kind?: string | null;
1353
+ /** Immutable provenance for an attested transcode; absent on older/unproven refs. */
1354
+ readonly renditionProof?: RenditionProof | null;
1169
1355
  }
1170
1356
 
1357
+ /** Persisted destination ownership is independent of the current write selection. */
1358
+ export declare const storageRefAddress: (ref: Pick<StorageRef, "bucket" | "tenantId" | "key">) => StorageAddress;
1359
+
1171
1360
  export declare const storageReferenceCatalogueTable: Table<"_voltro_storage_reference_catalogue", FieldDefinitions<{
1172
1361
  readonly id: ColumnBuilder<string, "text", boolean>;
1173
1362
  readonly namespace: ColumnBuilder<string, "text", boolean>;
@@ -1178,11 +1367,21 @@ export declare interface StorageReferenceInstanceAssignment {
1178
1367
  readonly storageInstance: string;
1179
1368
  }
1180
1369
 
1370
+ /** A plugin instance may retain several tenant-specific physical destinations.
1371
+ * Resolve the concrete namespace at the boundary; never substitute a sentinel
1372
+ * namespace for a tenant-scoped connection. */
1373
+ export declare interface StorageReferenceResolver {
1374
+ readonly storageInstance: string;
1375
+ readonly resolveNamespace: (address: StorageAddress) => Effect.Effect<string, StorageError>;
1376
+ }
1377
+
1181
1378
  export declare interface StorageReferenceScope {
1182
1379
  readonly storageInstance: string;
1183
1380
  readonly objectNamespace: string;
1184
1381
  }
1185
1382
 
1383
+ export declare type StorageReferenceView = StorageReferenceScope | StorageReferenceResolver;
1384
+
1186
1385
  /** An upload was rejected by a constraint (size / content-type / magic-byte
1187
1386
  * mismatch). `reason` is a stable code; `detail` is human-readable. */
1188
1387
  export declare class StorageRejected extends StorageRejected_base {
@@ -1217,6 +1416,9 @@ export declare interface StorageScanResult {
1217
1416
  readonly threat?: string;
1218
1417
  }
1219
1418
 
1419
+ /** A secret, or the set a rotation keeps accepting (the signing one first). */
1420
+ declare type StorageSecrets = string | ReadonlyArray<string>;
1421
+
1220
1422
  export declare class StorageService extends StorageService_base {
1221
1423
  }
1222
1424
 
@@ -1229,6 +1431,10 @@ export declare interface StorageServiceOptions {
1229
1431
  readonly refStore: RefStore;
1230
1432
  readonly grantStore: GrantStore;
1231
1433
  readonly bucket: string;
1434
+ /** New writes select a destination; reads retain SQL destination/tenant ownership. */
1435
+ readonly destinations?: StorageDestinationRouter;
1436
+ /** App-owned resolution of each persisted physical reference owner. */
1437
+ readonly resolveObjectReference?: (reference: StorageObjectReference) => Effect.Effect<StorageProvider, StorageError>;
1232
1438
  /** Canonical plugin instance; separates new plain keys on a shared provider. */
1233
1439
  readonly storageInstance?: string;
1234
1440
  /** Static prefix prepended to every key (e.g. an app/env namespace). */
@@ -1283,8 +1489,20 @@ export declare interface StorageServiceOptions {
1283
1489
 
1284
1490
  /** The service handlers consume: `const storage = yield* StorageService`. */
1285
1491
  export declare interface StorageServiceShape {
1492
+ /** Trusted server read of one raw durable put identity in this configured
1493
+ * instance/physical namespace/bucket and the explicitly supplied tenant.
1494
+ * No access-policy check, claim, byte read, retry write or provider HEAD.
1495
+ * Unbound/unsupported/corrupt metadata refuses; it never becomes absence. */
1496
+ readonly operationStatus: (input: {
1497
+ readonly operationId: string;
1498
+ readonly tenantId: string | null;
1499
+ readonly destination?: string;
1500
+ }) => Effect.Effect<StorageOperationStatus, StorageError>;
1286
1501
  /** Opaque destination binding for manually signed upload-family credentials. */
1287
- readonly uploadTicketScope: () => Effect.Effect<string, StorageError>;
1502
+ readonly uploadTicketScope: (input?: {
1503
+ readonly tenantId?: string | null;
1504
+ readonly destination?: string;
1505
+ }) => Effect.Effect<string, StorageError>;
1288
1506
  /** Store bytes. The result is the ref plus this call's `outcome`. */
1289
1507
  readonly put: (input: PutInput) => Effect.Effect<StoredRef, StorageError | StorageRejected | StorageScanRejected>;
1290
1508
  /** Fetch an object's bytes by id. Enforces the tenant guard ONLY — it does
@@ -1455,9 +1673,9 @@ export declare interface StorageServiceShape {
1455
1673
  readonly contentType: string;
1456
1674
  readonly tenantId?: string | null;
1457
1675
  readonly expiresInSec?: number;
1458
- }) => Effect.Effect<{
1676
+ }) => Effect.Effect<StorageAddress & {
1677
+ readonly writeScope: string;
1459
1678
  readonly uploadUrl: string;
1460
- readonly key: string;
1461
1679
  readonly expiresInSec: number;
1462
1680
  }, StorageError>;
1463
1681
  /** Promote a blob the client PUT directly to a TEMP `key` into a real ref:
@@ -1466,6 +1684,7 @@ export declare interface StorageServiceShape {
1466
1684
  * scanned + a non-degraded ref — out of band from the direct PUT. On a scan
1467
1685
  * reject the temp bytes are deleted (fail-closed, nothing promoted). */
1468
1686
  readonly finalizeUpload: (input: {
1687
+ readonly destination: string;
1469
1688
  readonly key: string;
1470
1689
  readonly contentType: string;
1471
1690
  readonly tenantId?: string | null;
@@ -1480,6 +1699,7 @@ export declare interface StorageServiceShape {
1480
1699
  /** Store one chunk of a resumable upload as its own durable `_incoming/<id>/
1481
1700
  * <index>` object. Idempotent — re-PUTting the same index overwrites it. */
1482
1701
  readonly putResumableChunk: (input: {
1702
+ readonly destination: string;
1483
1703
  readonly uploadId: string;
1484
1704
  readonly index: number;
1485
1705
  readonly bytes: Uint8Array;
@@ -1489,6 +1709,7 @@ export declare interface StorageServiceShape {
1489
1709
  * 0 and returns the received byte `offset` + the `nextIndex` to send. Lets a
1490
1710
  * reconnecting client resume instead of restarting. */
1491
1711
  readonly resumableStatus: (input: {
1712
+ readonly destination: string;
1492
1713
  readonly uploadId: string;
1493
1714
  readonly tenantId?: string | null;
1494
1715
  }) => Effect.Effect<{
@@ -1499,6 +1720,7 @@ export declare interface StorageServiceShape {
1499
1720
  * concatenate, run the FULL `put()` pipeline, then delete the chunk objects
1500
1721
  * (on success AND on reject — fail-closed). */
1501
1722
  readonly finalizeResumable: (input: {
1723
+ readonly destination: string;
1502
1724
  readonly uploadId: string;
1503
1725
  readonly count: number;
1504
1726
  readonly contentType: string;
@@ -1530,12 +1752,14 @@ export declare interface StorageServiceShape {
1530
1752
  readonly tenantId?: string | null;
1531
1753
  readonly visibility?: StorageVisibility;
1532
1754
  readonly expiresInSec?: number;
1533
- }) => Effect.Effect<{
1755
+ }) => Effect.Effect<StorageAddress & {
1756
+ readonly writeScope: string;
1534
1757
  readonly uploadId: string;
1535
- readonly key: string;
1536
1758
  }, StorageError>;
1537
1759
  /** Presign a PUT for one part (1-based `partNumber`). */
1538
1760
  readonly signMultipartPart: (input: {
1761
+ readonly destination: string;
1762
+ readonly tenantId: string | null;
1539
1763
  readonly key: string;
1540
1764
  readonly uploadId: string;
1541
1765
  readonly partNumber: number;
@@ -1545,6 +1769,7 @@ export declare interface StorageServiceShape {
1545
1769
  * registered straight from it (size via HEAD, etag as checksum). No fetch-back,
1546
1770
  * so NO scan/derivatives — scan multi-GB out of band. */
1547
1771
  readonly completeMultipart: (input: {
1772
+ readonly destination: string;
1548
1773
  readonly key: string;
1549
1774
  readonly uploadId: string;
1550
1775
  readonly parts: ReadonlyArray<{
@@ -1563,20 +1788,36 @@ export declare interface StorageServiceShape {
1563
1788
  }) => Effect.Effect<StorageRef, StorageError | StorageRejected>;
1564
1789
  /** Abort a multipart session (client cancelled) — frees the bucket's parts. */
1565
1790
  readonly abortMultipart: (input: {
1791
+ readonly destination: string;
1792
+ readonly tenantId: string | null;
1566
1793
  readonly key: string;
1567
1794
  readonly uploadId: string;
1568
1795
  }) => Effect.Effect<void, StorageError>;
1569
1796
  }
1570
1797
 
1798
+ /** Trusted build-input metadata, not a delivery DTO or access grant. The
1799
+ * caller persists this alongside its immutable publication/recipe selection. */
1800
+ export declare interface StorageSourcePin {
1801
+ readonly version: 1;
1802
+ readonly instance: string;
1803
+ readonly tenantId: string | null;
1804
+ readonly bucket: string;
1805
+ readonly id: string;
1806
+ readonly sourceDigest: string;
1807
+ readonly checksum: string;
1808
+ readonly size: number;
1809
+ readonly contentType: string;
1810
+ readonly derivedFrom: string | null;
1811
+ readonly kind: string | null;
1812
+ readonly renditionProof: RenditionProof | null;
1813
+ readonly digest: string;
1814
+ }
1815
+
1571
1816
  /** `public` = anyone, served direct from bucket/CDN; `private` = gated by
1572
1817
  * the ref's access policy + grants. Default for a new put is `private`. */
1573
1818
  export declare type StorageVisibility = 'public' | 'private';
1574
1819
 
1575
- /** Bytes + content type as returned by a provider `get`. */
1576
- export declare interface StoredObject {
1577
- readonly bytes: Uint8Array;
1578
- readonly contentType: string;
1579
- }
1820
+ export { StoredObject }
1580
1821
 
1581
1822
  /** What `put` returns: the stored ref, plus what this call did to it. The
1582
1823
  * `outcome` is a fact about the CALL, not the row — it is never persisted and
@@ -1585,13 +1826,7 @@ export declare type StoredRef = StorageRef & {
1585
1826
  readonly outcome: PutOutcome;
1586
1827
  };
1587
1828
 
1588
- /** A streamed read from a provider — the object as a {@link ByteStream} plus
1589
- * its content type and, when the backend reports it up front, its size. */
1590
- export declare interface StoredStream {
1591
- readonly stream: ByteStream;
1592
- readonly contentType: string;
1593
- readonly size?: number;
1594
- }
1829
+ export { StoredStream }
1595
1830
 
1596
1831
  /**
1597
1832
  * A ByteStream → Node `Readable`, for SDK uploaders that consume Node streams
@@ -1613,9 +1848,7 @@ export declare type Transcoder = (input: {
1613
1848
  readonly ref: StorageRef;
1614
1849
  }) => ReadonlyArray<Rendition> | Promise<ReadonlyArray<Rendition>> | Effect.Effect<ReadonlyArray<Rendition>>;
1615
1850
 
1616
- /** Transform with a small FIFO cache keyed by `(refId, params)` so repeated
1617
- * requests for the same variant don't re-encode. */
1618
- export declare const transformCached: (refId: string, bytes: Uint8Array, contentType: string, params: TransformParams) => Promise<Transformed | null>;
1851
+ export declare const transformCached: (addressIdentity: string, bytes: Uint8Array, contentType: string, params: TransformParams) => Promise<Transformed | null>;
1619
1852
 
1620
1853
  export declare interface Transformed {
1621
1854
  readonly bytes: Uint8Array;
@@ -1678,6 +1911,8 @@ export declare interface UploadTicketPayload {
1678
1911
  readonly issuerReplicaId?: string;
1679
1912
  /** Exact storage plugin instance authorized to accept the bytes. */
1680
1913
  readonly instance: string;
1914
+ /** Logical destination selected before issuing the upload/session. */
1915
+ readonly destination: string;
1681
1916
  /** ownerId (subject id) the stored object is attributed to. */
1682
1917
  readonly sub: string | null;
1683
1918
  /** tenantId that scopes the key prefix + ref row. */
@@ -1708,28 +1943,28 @@ export declare interface UsageDelta {
1708
1943
 
1709
1944
  /** Verify a finalize token. Returns the payload on success, null on any failure
1710
1945
  * (malformed, bad signature, expired, missing temp key). Never throws. */
1711
- export declare const verifyFinalizeTicket: (token: string, secret?: string) => FinalizeTicketPayload | null;
1946
+ export declare const verifyFinalizeTicket: (token: string, secret?: StorageSecrets) => FinalizeTicketPayload | null;
1712
1947
 
1713
1948
  /**
1714
1949
  * Verify a grant token. Returns the payload on success, null on any
1715
1950
  * failure (malformed, bad signature, expired). Never throws.
1716
1951
  */
1717
- export declare const verifyGrant: (token: string, secret?: string) => GrantTokenPayload | null;
1952
+ export declare const verifyGrant: (token: string, secret?: StorageSecrets) => GrantTokenPayload | null;
1718
1953
 
1719
1954
  /** Verify a multipart ticket. Returns the payload on success, null on any
1720
1955
  * failure (malformed, bad signature, expired, missing key/uploadId). */
1721
- export declare const verifyMultipartTicket: (token: string, secret?: string) => MultipartTicketPayload | null;
1956
+ export declare const verifyMultipartTicket: (token: string, secret?: StorageSecrets) => MultipartTicketPayload | null;
1722
1957
 
1723
1958
  /** Constant-time check of a plaintext password against a stored hash. */
1724
1959
  export declare const verifyPassword: (password: string | undefined, stored: string | null) => boolean;
1725
1960
 
1726
1961
  /** Verify a resumable ticket. Returns the payload on success, null on any
1727
1962
  * failure (malformed, bad signature, expired, missing id/size). Never throws. */
1728
- export declare const verifyResumableTicket: (token: string, secret?: string) => ResumableTicketPayload | null;
1963
+ export declare const verifyResumableTicket: (token: string, secret?: StorageSecrets) => ResumableTicketPayload | null;
1729
1964
 
1730
1965
  /** Verify an upload ticket. Returns the payload on success, null on any
1731
1966
  * failure (malformed, bad signature, expired). Never throws. */
1732
- export declare const verifyUploadTicket: (token: string, secret?: string) => UploadTicketPayload | null;
1967
+ export declare const verifyUploadTicket: (token: string, secret?: StorageSecrets) => UploadTicketPayload | null;
1733
1968
 
1734
1969
  /**
1735
1970
  * App-supplied media probe for video/audio uploads. The core deliberately does
@@ -1774,4 +2009,7 @@ export declare const _voltroStorageUsageTable: TableLike;
1774
2009
  */
1775
2010
  export declare const withStorage: () => ReturnType<typeof mixin>;
1776
2011
 
2012
+ /** All keyed operations share the same namespace, including presigned and multipart paths. */
2013
+ export declare const withStorageKeyPrefix: (provider: StorageProvider, prefix?: string) => StorageProvider;
2014
+
1777
2015
  export { }