Skip to content

Commit

Permalink
feat!: allow invocation configuration to be generated on demand (#1507)
Browse files Browse the repository at this point in the history
Since the blob protocol landed, it's become harder to obtain a
delegation to upload a specific CAR.

It was tricky before because of sharding, but assuming you created a CAR
below the shard threshold, its easy enough to obtain a `store/add`
delegation tied to the CAR CID.

With the blob protocol, we now generate an index, and `blob/add` that as
well. It means we can't just create a CAR and delegate `can: blob/add,
nb: { link, size }` for the CAR, because we also need to delegate
`blob/add` for the index, but we don't know the index CID or it's size
up front...

It's ok if the delegation is not restricted to the specific CAR, but for
certain use cases it's necessary to delegate just what is needed.

This PR alters the client to allow invocation configuration to be
generated on demand, when the proofs are required. This is a backwards
compatible change.

So, for example instead of calling: `uploadFile({ issuer, with, proofs
}, file)` you now call:

```js
const configure = caps => {
  const proofs = []
  for (const { can, nb } of caps) {
    // decide if want to delegate and create delegation
    proofs.push(...)
  }
  if (!proofs.length) throw new Error('no proofs available')
  return { issuer, with, proofs }
}

uploadFile(configure, file)
```

...and `configure` will be called for each proof that is needed
(`blob/add`, `index/add` and `upload/add`).

It means also that we no longer have to recommend folks to create a CAR,
get the CID and then upload it, they can just use `uploadFile` and pass
in the invocation config function to obtain the correct delegations when
required.
  • Loading branch information
Alan Shaw authored Jun 18, 2024
1 parent 028e8d2 commit fd74cbb
Show file tree
Hide file tree
Showing 25 changed files with 837 additions and 469 deletions.
59 changes: 53 additions & 6 deletions packages/upload-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ await Upload.add(conf, rootCID, carCIDs)
- [`CARMetadata`](#carmetadata)
- [`DirectoryEntryLinkCallback`](#directoryentrylinkcallback)
- [`InvocationConfig`](#invocationconfig)
- [`InvocationConfigurator`](#invocationconfigurator)
- [`ShardStoredCallback`](#shardstoredcallback)
- [Contributing](#contributing)
- [License](#license)
Expand All @@ -190,7 +191,7 @@ await Upload.add(conf, rootCID, carCIDs)

```ts
function uploadDirectory(
conf: InvocationConfig,
conf: InvocationConfig | InvocationConfigurator,
files: File[],
options: {
retries?: number
Expand All @@ -207,13 +208,13 @@ Uploads a directory of files to the service and returns the root data CID for th

Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer`

More information: [`InvocationConfig`](#invocationconfig), [`ShardStoredCallback`](#shardstoredcallback)
More information: [`InvocationConfig`](#invocationconfig), [`InvocationConfigurator`](#invocationconfigurator), [`ShardStoredCallback`](#shardstoredcallback)

### `uploadFile`

```ts
function uploadFile(
conf: InvocationConfig,
conf: InvocationConfig | InvocationConfigurator,
file: Blob,
options: {
retries?: number
Expand All @@ -229,13 +230,13 @@ Uploads a file to the service and returns the root data CID for the generated DA

Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer`

More information: [`InvocationConfig`](#invocationconfig)
More information: [`InvocationConfig`](#invocationconfig), [`InvocationConfigurator`](#invocationconfigurator)

### `uploadCAR`

```ts
function uploadCAR(
conf: InvocationConfig,
conf: InvocationConfig | InvocationConfigurator,
car: Blob,
options: {
retries?: number
Expand All @@ -252,7 +253,7 @@ Uploads a CAR file to the service. The difference between this function and [Blo

Required delegated capability proofs: `blob/add`, `index/add`, `upload/add`, `filecoin/offer`

More information: [`InvocationConfig`](#invocationconfig), [`ShardStoredCallback`](#shardstoredcallback)
More information: [`InvocationConfig`](#invocationconfig), [`InvocationConfigurator`](#invocationconfigurator), [`ShardStoredCallback`](#shardstoredcallback)

### `Blob.add`

Expand Down Expand Up @@ -512,6 +513,52 @@ This is the configuration for the UCAN invocation. It's values can be obtained f
- The `issuer` is the signing authority that is issuing the UCAN invocation(s). It is typically the user _agent_.
- The `proofs` are a set of capability delegations that prove the issuer has the capability to perform the action.

### `InvocationConfigurator`

A function that generates [invocation configuration](#invocationconfig) for the requested capabilities. The intention is for the client to be able to [request, on demand, delegated capabilities from an application server](https://github.com/storacha-network/w3up-examples/tree/main/delegated-upload).

```ts
interface InvocationConfigurator {
(caps: CapabilityQuery[]): Await<InvocationConfig>
}
interface CapabilityQuery {
can: ServiceAbility
nb?: unknown
}
// "space/blob/add", "space/index/add" etc.
type ServiceAbility = string
```

The function may be called multiple times with different requested capabilities.

Example:

```js
import { Agent } from '@web3-storage/access'
import * as Space from '@web3-storage/access/space'
const agent = await Agent.create()
const space = await Space.generate({ name: 'myspace' })
const configure = async (caps) => ({
issuer: agent.issuer,
with: space.did(),
proofs: [
// delegate from the space to the agent the requested capabilities
await Delegation.delegate({
issuer: space.signer,
audience: agent.did(),
capabilities: caps.map(c => ({ can: c.can, with: space.did(), nb: c.nb })),
expiration: Math.floor(Date.now() / 1000) + (60 * 60) // 1h in seconds
})
]
})
await uploadFile(configure, new Blob(['Hello World!']))
```

### `ShardStoredCallback`

A function called after a DAG shard has been successfully stored by the service:
Expand Down
12 changes: 8 additions & 4 deletions packages/upload-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,32 @@
},
"exports": {
".": "./dist/src/index.js",
"./blob": "./dist/src/blob.js",
"./blob": "./dist/src/blob/index.js",
"./car": "./dist/src/car.js",
"./fetch-with-upload-progress": "./dist/src/fetch-with-upload-progress.js",
"./index": "./dist/src/index/index.js",
"./sharding": "./dist/src/sharding.js",
"./upload": "./dist/src/upload.js",
"./upload": "./dist/src/upload/index.js",
"./store": "./dist/src/store.js",
"./unixfs": "./dist/src/unixfs.js",
"./types": "./dist/src/types.js"
},
"typesVersions": {
"*": {
"blob": [
"dist/src/blob.d.ts"
"dist/src/blob/index.d.ts"
],
"car": [
"dist/src/car.d.ts"
],
"index": [
"dist/src/index/index.d.ts"
],
"sharding": [
"dist/src/sharding.d.ts"
],
"upload": [
"dist/src/upload.d.ts"
"dist/src/upload/index.d.ts"
],
"store": [
"dist/src/store.d.ts"
Expand Down
Loading

0 comments on commit fd74cbb

Please sign in to comment.