@@ -110,11 +110,9 @@ exceeding it returns `storage.ErrMaxBytesExceeded`.
110110
111111## Upload Controls
112112
113- Use ` storage.UploadOptions ` when the default upload is not enough :
113+ ` Service.Upload ` performs automatic target selection. Its options include :
114114
115- - ` Copies ` : requested provider copies. Zero means the selection default.
116- - ` ProviderIDs ` : pin copies to specific providers.
117- - ` DataSetIDs ` : write to specific existing datasets.
115+ - ` Copies ` : required number of provider copies. It must be greater than zero.
118116- ` ExcludeProviderIDs ` : skip providers only during automatic selection.
119117- ` DataSetMetadata ` : metadata used when creating or reusing datasets.
120118- ` PieceMetadata ` : metadata stored with the committed piece.
@@ -133,20 +131,40 @@ to know whether every requested copy succeeded.
133131Dataset metadata must match exactly for automatic dataset reuse. Use stable
134132metadata values when you want uploads to share payment rails.
135133
134+ Use ` NewProviderContext ` or ` NewDataSetContext ` for a known target. Use
135+ ` UploadToContexts ` when the caller, rather than the SDK, must determine the
136+ exact providers and their primary-to-secondary order.
137+
136138## Funding Preflight
137139
138140` Prepare ` is optional. Use it before a first upload, before a large batch, or
139141when your UI needs to show whether the account has enough USDFC deposit and
140- FWSS approval. If ` Transaction ` is nil, the account is already ready for the
141- requested size and contexts. Match options that affect context selection, such
142- as CDN .
142+ FWSS approval. Select the upload targets first, then pass the same contexts to
143+ ` Prepare ` and ` UploadToContexts ` . This ensures the estimate and upload use the
144+ same providers, datasets, payer, chain, and record keeper .
143145
144146``` go
145147withCDN := true
146148
149+ selection , selectErr := client.Storage ().SelectUploadContexts (ctx,
150+ storage.SelectUploadContextsOptions {
151+ Copies : 2 ,
152+ WithCDN : &withCDN,
153+ DataSetMetadata : map [string ]string {
154+ " project" : " photos" ,
155+ },
156+ },
157+ )
158+ if selectErr != nil && !errors.Is (selectErr, storage.ErrInsufficientUploadContexts ) {
159+ return selectErr
160+ }
161+ if selection == nil {
162+ return errors.New (" no upload contexts available" )
163+ }
164+
147165prep , err := client.Storage ().Prepare (ctx, &storage.PrepareOptions {
148- DataSize : uint64 (payloadSize),
149- EnableCDN : &withCDN ,
166+ DataSize : uint64 (payloadSize),
167+ Contexts : selection. Contexts ,
150168})
151169if err != nil {
152170 return err
@@ -159,62 +177,91 @@ if prep.Transaction != nil {
159177 }
160178 fmt.Println (" prepare tx:" , tx.Hash )
161179}
180+
181+ result , err := client.Storage ().UploadToContexts (
182+ ctx,
183+ file,
184+ selection.Contexts ,
185+ &storage.UploadOptions {
186+ PieceMetadata : map [string ]string {" name" : " payload.bin" },
187+ },
188+ )
189+ if err != nil {
190+ return err
191+ }
162192```
163193
164- If you already selected contexts, pass them through ` PrepareOptions.Contexts `
165- so the estimate matches the exact providers and datasets. ` CreateContexts `
166- returns managed ` *storage.Context ` values; convert the slice element-by-element
167- when filling the ` []storage.UploadContext ` field.
194+ When selection finds at least one but fewer than the requested targets, it
195+ returns both a usable ` UploadContextSelection ` and an
196+ ` InsufficientUploadContextsError ` . The application can continue with the
197+ available contexts or stop before funding. With ` UploadToContexts ` , the
198+ selection length becomes ` UploadResult.RequestedCopies ` and no replacement
199+ provider is selected automatically.
168200
169201For read-only cost and account state, use ` GetStorageInfo ` or
170202` CalculateMultiContextCosts ` .
171203
172204## Contexts And Datasets
173205
174- Use contexts when you need provider or dataset control before uploading.
206+ There are two immutable context types:
207+
208+ - ` ProviderContext ` identifies one provider and no dataset. ` Commit ` and
209+ ` Pull ` create a new dataset.
210+ - ` DataSetContext ` identifies one provider and one existing dataset. ` Commit `
211+ and ` Pull ` always target that dataset.
212+
213+ Provider-scoped methods such as ` Store ` and ` Download ` are shared. For example,
214+ both ` ProviderContext.Download ` and ` DataSetContext.Download ` retrieve a piece
215+ from the same configured provider or CDN; the dataset binding does not change
216+ piece retrieval. Dataset inspection, deletion, and termination methods exist
217+ only on ` DataSetContext ` .
218+
219+ Select one approved, active, healthy provider without looking up datasets:
175220
176221``` go
177- contexts , err := client.Storage ().CreateContexts (ctx, &storage.CreateContextsOptions {
178- Copies : 2 ,
179- DataSetMetadata : map [string ]string {
180- " project" : " photos" ,
222+ providerCtx , err := client.Storage ().SelectProviderContext (ctx,
223+ storage.SelectProviderContextOptions {
224+ DataSetMetadata : map [string ]string {
225+ " project" : " photos" ,
226+ },
181227 },
182- } )
228+ )
183229if err != nil {
184230 return err
185231}
186-
187- fmt.Println (" contexts:" , len (contexts))
188232```
189233
190- ``` go
191- prepareContexts := make ([]storage.UploadContext , len (contexts))
192- for i , c := range contexts {
193- prepareContexts[i] = c
194- }
234+ Open a registered provider by ID without checking approval, activity, endpoint
235+ health, or existing datasets:
195236
196- prep , err := client.Storage ().Prepare (ctx, &storage.PrepareOptions {
197- DataSize : uint64 (payloadSize),
198- Contexts : prepareContexts,
199- })
237+ ``` go
238+ providerID := types.NewBigInt (123 )
239+ providerCtx , err := client.Storage ().NewProviderContext (ctx, providerID,
240+ storage.NewProviderContextOptions {
241+ DataSetMetadata : map [string ]string {" project" : " photos" },
242+ },
243+ )
200244if err != nil {
201245 return err
202246}
203- fmt.Println (" ready:" , prep.Costs .Ready )
204247```
205248
206- For one provider or one dataset:
249+ Open an existing dataset owned by the current payer. The optional provider ID
250+ is an ownership assertion. Opening a terminated or currently unwritable
251+ dataset is allowed for inspection and cleanup; a later ` Commit ` or ` Upload `
252+ still checks writability.
207253
208254``` go
209255providerID := types.NewBigInt (123 )
210- ctx1 , err := client.Storage ().CreateContext (ctx, &storage.CreateContextOptions {
211- ProviderID : &providerID,
212- })
256+ dataSetID := types.NewBigInt (456 )
257+ dataSetCtx , err := client.Storage ().NewDataSetContext (ctx, dataSetID,
258+ storage.NewDataSetContextOptions {ProviderID: &providerID},
259+ )
213260if err != nil {
214261 return err
215262}
216263
217- result , err := ctx1 .Upload (ctx, file, &storage.UploadOptions {
264+ result , err := dataSetCtx .Upload (ctx, file, &storage.UploadOptions {
218265 PieceMetadata : map [string ]string {" name" : " payload.bin" },
219266})
220267if err != nil {
@@ -223,65 +270,80 @@ if err != nil {
223270fmt.Println (result.PieceCID )
224271```
225272
226- When resuming a known dataset, pass ` DataSetID ` . If you also pass
227- ` ProviderID ` , the SDK checks that the dataset belongs to that provider.
273+ ` DataSetRef ` is the persistent reference for a complete provider and dataset
274+ target. Its zero value is invalid; construct it explicitly and use accessors to
275+ read IDs.
228276
229277``` go
230- dataSetID := types.NewBigInt (456 )
231- providerID := types.NewBigInt (123 )
232- ctx1 , err := client.Storage ().CreateContext (ctx, &storage.CreateContextOptions {
233- DataSetID : &dataSetID,
234- ProviderID : &providerID,
235- })
278+ ref , err := storage.NewDataSetRef (providerID, dataSetID, dataSetCtx.ClientDataSetID ())
236279if err != nil {
237280 return err
238281}
282+ fmt.Println (" dataset:" , ref.DataSetID ())
239283```
240284
241- To create an empty dataset first, persist the submission if your process may
242- restart before confirmation:
285+ To create an empty dataset first, persist the submission if the process may
286+ restart before confirmation. Creation is available only on ` ProviderContext ` .
243287
244288``` go
245289var submitted storage.CreateDataSetSubmission
246290
247- created , err := ctx1 .CreateDataSet (ctx, &storage.CreateDataSetOptions {
291+ created , err := providerCtx .CreateDataSet (ctx, &storage.CreateDataSetOptions {
248292 OnSubmitted : func (s storage.CreateDataSetSubmission ) {
249293 submitted = s
250294 },
251295})
252296if err != nil {
253297 return err
254298}
255- fmt.Println (" dataset:" , created.DataSetID )
299+ fmt.Println (" dataset:" , created.DataSet . DataSetID () )
256300```
257301
258- Resume a submitted create transaction:
302+ Resume a submitted create transaction with any fresh ` ProviderContext ` for the
303+ same provider, then convert the returned reference without mutating that
304+ context:
259305
260306``` go
261- created , err := ctx1 .WaitForDataSetCreated (ctx, submitted)
307+ created , err := providerCtx .WaitForDataSetCreated (ctx, submitted)
262308if err != nil {
263309 return err
264310}
265- fmt.Println (" dataset:" , created.DataSetID )
311+ dataSetCtx , err := providerCtx.ForDataSet (created.DataSet )
312+ if err != nil {
313+ return err
314+ }
315+ fmt.Println (" dataset:" , dataSetCtx.DataSetID ())
266316```
267317
268- Use ` GetDefaultContext ` when the context resolver defaults are enough.
269- Advanced callers can split a context upload into ` Store ` , ` Pull ` ,
318+ The receiver never binds or changes target after creation. Concurrent creates
319+ on one ` ProviderContext ` are independent; adds on one ` DataSetContext ` may run
320+ in parallel. Advanced callers can split a context upload into ` Store ` , ` Pull ` ,
270321` PresignForCommit ` , and ` Commit ` .
271322
323+ ### Migrating From The Previous Context API
324+
325+ | Previous call | Replacement |
326+ | ---------------| -------------|
327+ | ` CreateContext(nil) ` / ` GetDefaultContext() ` | ` SelectProviderContext(...) ` |
328+ | ` CreateContext ` with ` ProviderID ` | ` NewProviderContext(...) ` |
329+ | ` CreateContext ` with ` DataSetID ` | ` NewDataSetContext(...) ` |
330+ | ` CreateContexts ` for a new upload | ` SelectUploadContexts(...) ` |
331+ | ` Upload ` with provider or dataset IDs | construct/select contexts, then call ` UploadToContexts(...) ` |
332+ | ` Prepare ` without contexts | select contexts first and pass the same slice to ` Prepare ` |
333+
272334## Discovery And Lifecycle
273335
274336Common management calls:
275337
276338- ` FindDataSets ` : list datasets owned by the signer or another payer.
277339- ` GetStorageInfo ` : inspect providers, pricing, limits, and allowances.
278- - ` Context .Download` : download from a known provider and dataset context .
279- - ` Context .DeletePieceByID` : schedule exact removal by on-chain piece ID.
280- - ` Context .DeletePiece` : schedule removal by piece CID convenience lookup. Prefer
340+ - ` ProviderContext .Download` / ` DataSetContext.Download ` : download from a known provider.
341+ - ` DataSetContext .DeletePieceByID` : schedule exact removal by on-chain piece ID.
342+ - ` DataSetContext .DeletePiece` : schedule removal by piece CID convenience lookup. Prefer
281343 ` DeletePieceByID ` when available, because repeated uploads can share a CID.
282- - ` Context .TerminateService` / ` Service.TerminateService ` : terminate service
344+ - ` DataSetContext .TerminateService` / ` Service.TerminateService ` : terminate service
283345 through the provider by default; use ` SkipProvider ` for direct FWSS fallback.
284- - ` Context .Terminate` / ` Service.TerminateDataSet ` : legacy direct FWSS
346+ - ` DataSetContext .Terminate` / ` Service.TerminateDataSet ` : legacy direct FWSS
285347 termination write.
286348
287349Termination and removal are storage lifecycle actions. Treat them as
0 commit comments