The main JavaScript client for the w3up platform by https://web3.storage
@web3-storage/w3up-client
is a JavaScript library that provides a convenient interface to the w3up platform, a simple "on-ramp" to the content-addressed decentralized IPFS network.
This library is the user-facing "porcelain" client for interacting with w3up services from JavaScript. It wraps the lower-level @web3-storage/access
and @web3-storage/upload-client
client packages, which target individual w3up services. We recommend using w3up-client
instead of using those "plumbing" packages directly, but you may find them useful if you need more context on w3up's architecture and internals.
w3up-client
requires Node 18 or higher.
⚠️❗ Public Data 🌎: All data uploaded to w3up is available to anyone who requests it using the correct CID. Do not store any private or sensitive information in an unencrypted form using w3up.
⚠️❗ Permanent Data ♾️: Removing files from w3up will remove them from the file listing for your account, but that doesn’t prevent nodes on the decentralized storage network from retaining copies of the data indefinitely. Do not use w3up for data that may need to be permanently deleted in the future.
You can add the @web3-storage/w3up-client
package to your JavaScript or TypeScript project with npm
:
npm install @web3-storage/w3up-client
Most users' usage of w3up-client
will be for interacting with web3.storage, a hosted storage product that developed w3up for their upload APIs. However, any user that has an implementation of w3up (specs, protocol) can configure w3up-client
for their usage.
For authorization, w3up services use ucanto, a Remote Procedure Call (RPC) framework built around UCAN, or User Controlled Authorization Networks. UCANs are a powerful capability-based authorization system that allows fine-grained sharing of permissions through a process called delegation on top of public key cryptography. See our intro to UCAN blog post for an overview of UCAN.
You can think about UCAN replacing bearer tokens in traditional APIs for authorization with w3up. Since any actor can be represented by a cryptographic keypair and permissions can be delegated to them, users can interact with w3up directly in cases where a developer might have needed to previously run additional back-end infrastructure to keep API keys secure. This can be extended even to have end users using applications integrated with w3up using their own keypair-based identity.
w3up-client
and ucanto
take care of the details of UCANs for you, but a few of the underlying terms and concepts may "bubble up" to the surface of the API, so we'll cover the basics here. We'll also go over some terms that are specific to w3up that you might not have encountered elsewhere.
UCAN-based APIs are centered around capabilities, which are comprised of an ability and a resource. Together, the ability and resource determine what action a client can perform and what objects in the system can be acted upon. When invoking a service method, a client will present a UCAN token that includes an ability and resource, along with proofs that verify that they should be allowed to exercise the capability. The proof might be signed directly by the capability owner, or have a chain of signatures (delegations) where the actor invoking the capability has been verifiably delegated permission to do so.
When you upload data to w3up, your uploads are linked to a unique Space that acts as a "namespace" for the data you upload. Each Space corresponds to a DID, or Decentralized Identity Document. In web3.storage's implementation of w3up, these Space DIDs generally use the key DID method, of the form did:key:publicKey
with a corresponding private signing key.
When creating a Space using w3up-client
, it generates this private key and did:key
for you locally. To use web3.storage, you then register a Space by associating it with your email address. From there, when invoking storage capabilities with web3.storage, the Space did:key
is the "resource" portion of the capability, while the ability is an action like store/add
or store/remove
. (A Space registered with web3.storage is imperfectly analogous to an "account" with web3.storage.)
Under the hood in the email registration process, your Space delegates the capabilities needed to use w3up to your email address, and this delegation is stored by web3.storage. If you need access to your Space in the future from any device, web3.storage allows you to reclaim those capabilities the same way you would reset a password in other services - using an email verification process. This means you don't need to store or manage Space private keys to use w3up - just create a new space, register it with w3up and use it from as many devices as you like. More on this "sign in" process is detailed in the next section on Agents.
To invoke a capability like store/add
on a Space using w3up-client
, the client must have an Agent. Like a Space, an Agent corresponds to a did:key
whose private key is generated locally. An Agent is useful once w3up-client
has a UCAN delegation where a registered Space(s) delegates the Agent its capabilities. (An imperfect analogy is Agent with login session.)
The first time w3up-client
is instantiated on a device, it creates an Agent automatically. Alternatively, if you have your own Agent corresponding to a specific private key locally available, you can pass it to the client.
The delegation from a Space to your Agent that w3up-client
needs can be passed either by verifying the email address the Space is registered to and claiming the UCAN delegation (login(email)
then capability.access.claim
) or directly if you have the UCAN delegation available locally (addSpace(delegation)
).
flowchart TD
A[w3up-client instance] -->|Automatic if specific Agent is not passed when client object created|B(Create local Agent DID and key)
B --> |If Space has not yet been created|S(Create local Space, login client with your email address, and register Space + email address with web3.storage)
S --> C(Get UCAN delegation from Space to Agent)
C --> D(Upload to Space using Agent)
All uses of w3up-client
to upload with web3.storage follow the flow above. This section shows the most basic way to use the client to start storing data. For more complex integration options, check out the [integration options][https://github.com/web3-storage/w3up/blob/main/packages/w3up-client/README.md#integration-options] docs. For reference, check out the API reference docs or the source code of the w3cli
package, which uses w3up-client
throughout.
By you or your users registering a w3up Space via email confirmation with web3.storage, you agree to the Terms of Service.
The package provides a static create
function that returns a Client
object.
import { create } from '@web3-storage/w3up-client'
const client = await create()
By default, clients will create a new Agent
and put it in a persistent local Store
if it can't find an existing one to load (so the next time the client is initialized on the same device, it will use the same Agent
).
create
accepts an optional ClientFactoryOptions
object that can be used configured to use a non-default persistent Store
. See the @web3-storage/access
docs for more about Store
configuration. If you'd like to bring your own Agent, you can initialize the client with your own storage Driver. An example would be using Signer
from the ucanto package.
import { create } from '@web3-storage/w3up-client'
import * as Signer from '@ucanto/principal/ed25519' // Agents on Node should use Ed25519 keys
const principal = Signer.parse(agentPrivateKey) // created by `npx ucan-key ed --json` in command line
const client = await create({ principal })
Once initialized, you can access the client's Agent
with the agent
getter.
A Space
acts as a namespace for your uploads, and what your Agent will need a delegation from to store data with w3up. The first thing to do is login your Agent with your email address. Calling login
will cause an email to be sent to the given address. Once a user clicks the confirmation link in the email, the login
method will resolve. Make sure to check for errors, as login
will fail if the email is not confirmed within the expiration timeout. Authorization needs to happen only once per agent.
const account = await client.login('zaphod@beeblebrox.galaxy')
If your account does not yet have a payment plan, you'll be prompted to choose one after your email address has been verified. You will need a payment plan in order to provision your space. You can use the following code to wait for a payment plan to be selected:
// wait for payment plan to be selected
while (true) {
const res = await account.plan.get()
if (res.ok) break
console.log('Waiting for payment plan to be selected...')
await new Promise(resolve => setTimeout(resolve, 1000))
}
Spaces can be created using the createSpace
client method:
const space = await client.createSpace('my-awesome-space')
or using the w3cli's w3 space create
.
The name parameter is optional. If provided, it will be stored in your client's local state store and can be used to provide a friendly name for user interfaces.
Before anything can be stored with a space using web3.storage, the space must also be provisioned by a specific account that is responsible for the stored data. Note: after this succeeds, account
's payment method will be charged for data stored in space
.
await account.provision(space.did())
If provisioning succeeds, you're ready to use the Space. Save your space to your agent's state store:
await space.save()
If your agent has no other spaces, saving the space will set it as the "current space" in your agent. If you already have other spaces, you may want to set it as the current:
await client.setCurrentSpace(space.did())
One last thing - now that you've saved your space locally, it's a good idea to setup recovery, so that when you move to a different device you can still access your space:
const recovery = await space.createRecovery(account.did())
await client.capability.access.delegate({
space: space.did(),
delegations: [recovery],
})
In order to store data with w3up, your Agent will need a delegation from a Space. This automatically happens if you called createSpace
. However, if you are initializing the client with a previously created Space, you can login(email)
then claim a delegation granted to the account associated with your email:
await client.login('zaphod@beeblebrox.galaxy')
await client.setCurrentSpace(space.did()) # select the relevant Space DID that is associated with your account
sequenceDiagram
Client->>web3.storage w3up service: Here is my email address and Agent DID
web3.storage w3up service-->>Client: Please click the link to validate
Client-->>web3.storage w3up service: Email address validated
web3.storage w3up service->>Client: Here is a UCAN delegating permission from Space DID to Agent DID
For uses of w3up-client
in environments where the Agent is not persisted and/or the email verification step would be prohibitive (e.g., serverless backend environment where local Store with the Agent is dropped in between runs, and going through the email verification flow isn't practical), you can manually add a delegation for access to a Space created by a different authorized agent (see the addSpace
client method). An example (where w3cli is set up with the Space that we want to delegate permissions from in our client instance):
import * as Signer from '@ucanto/principal/ed25519' // Agents on Node should use Ed25519 keys
import { importDAG } from '@ucanto/core/delegation'
import { CarReader } from '@ipld/car'
import * as Client from '@web3-storage/w3up-client'
import { StoreMemory } from '@web3-storage/w3up-client/stores/memory'
async function main () {
// from "bring your own Agent" example in `Creating a client object" section`
// used command line to generate KEY and PROOF (stored in env variables)
// KEY: `npx ucan-key ed --json` in command line, which returns private key and DID for Agent (the private key is stored in KEY)
// PROOF: w3cli used to run `w3 delegation create <did_from_ucan-key_command_above> --can 'store/add' --can 'upload/add' | base64`, which returns the delegation from Space to the Agent we're using (stored in PROOF)
const principal = Signer.parse(process.env.KEY)
const store = new StoreMemory()
const client = await Client.create({ principal, store })
// now give Agent the delegation from the Space
const proof = await parseProof(process.env.PROOF)
const space = await client.addSpace(proof)
await client.setCurrentSpace(space.did())
// READY to go!
}
/** @param {string} data Base64 encoded CAR file */
async function parseProof (data) {
const blocks = []
const reader = await CarReader.fromBytes(Buffer.from(data, 'base64'))
for await (const block of reader.blocks()) {
blocks.push(block)
}
return importDAG(blocks)
}
Once you've created and registered a Space and authorized your Agent, you can upload files to the w3up platform.
Call uploadFile
to upload a single file, or uploadDirectory
to upload multiple files.
uploadFile
expects a "Blob like" input, which can be a Blob
or File
when running in a browser. On node.js, see the filesFromPath
library, which can load compatible objects from the local filesystem.
uploadDirectory
requires File
-like objects instead of Blob
s, as the file's name
property is used to build the directory hierarchy.
You can control the directory layout and create nested directory structures by using /
delimited paths in your filenames:
const files = [
new File(['some-file-content'], 'readme.md'),
new File(['import foo'], 'src/main.py'),
new File([someBinaryData], 'images/example.png'),
]
const directoryCid = await client.uploadDirectory(files)
In the example above, directoryCid
resolves to an IPFS directory with the following layout:
.
├── images
│ └── example.png
├── readme.md
└── src
└── main.py
As mentioned, UCAN opens up a number of options in how to integrate with w3up: Should you, the developer, own the Space? Should you delegate permissions to your users? Or should your user own their own Space? Broadly, there are three ways to integrate:
You can implement each of these in a number of ways, but we talk through some considerations when implementing a given option.
sequenceDiagram
participant User
w3up-client in backend->>w3up-client in backend: Client set with Agent with delegation from Space
User->>w3up-client in backend: Upload data
w3up-client in backend->>web3.storage w3up service: Upload data
w3up-client
to do so (especially if your backend isn't persistent); you can then generate your own Agent and delegate the ability to upload to your Space using something like this exampleupload
methodssequenceDiagram
participant w3up-client in user
participant w3up-client in backend
participant web3.storage w3up service
w3up-client in backend->>w3up-client in backend: Client created with Agent and delegation from Space
w3up-client in user->>w3up-client in user: Client instantiated with default Agent
w3up-client in user->>w3up-client in backend: Request delegation with user's Agent DID
w3up-client in backend->>w3up-client in user: Send delegation from Space to user's Agent DID
w3up-client in user->>web3.storage w3up service: Upload data
w3up-client
running in your end-user's client code, as well as backend code that's able to generate UCANs that delegate the ability to upload and pass them to your users (e.g., w3up-client
running in a serverless worker)w3up-client
to do so (especially if your backend isn't persistent); you can then generate your own Agent and delegate the ability to upload to your Space using something like this examplew3up-client
in the end user environment should have a unique Agent for each user, which should happen by default (since when w3up-client
is instantiated it creates a new Agent anyway, or uses the one in local Store)client.agent
)client.createDelegation()
passing in the Agent object from client.agent()
in your end user's instance, and passing through options?
params to limit the scope of the delegation (e.g., store/add
, upload/add
, expiration time)delegation.archive()
and send it to your userclient.login(email)
, as it is not claiming any delegations via email address (but rather getting the delegation directly from your backend)ucanto.Delegation.extract()
and pass it in using client.addSpace()
, and from there they can run any of the upload
methodsupload
and get back a content CID, you can have them send that CID to you for tracking)import { CarReader } from '@ipld/car';
import * as DID from '@ipld/dag-ucan/did';
import * as Delegation from '@ucanto/core/delegation';
import { importDAG } from '@ucanto/core/delegation';
import * as Signer from '@ucanto/principal/ed25519';
import * as Client from '@web3-storage/w3up-client';
async function backend(did: string) {
// Load client with specific private key
const principal = Signer.parse(process.env.KEY);
const client = await Client.create({ principal });
// Add proof that this agent has been delegated capabilities on the space
const proof = await parseProof(process.env.PROOF);
const space = await client.addSpace(proof);
await client.setCurrentSpace(space.did());
// Create a delegation for a specific DID
const audience = DID.parse(did);
const abilities = ['store/add', 'upload/add'];
const expiration = Math.floor(Date.now() / 1000) + 60 * 60 * 24; // 24 hours from now
const delegation = await client.createDelegation(audience, abilities, {
expiration,
});
// Serialize the delegation and send it to the client
const archive = await delegation.archive();
return archive.ok;
}
/** @param {string} data Base64 encoded CAR file */
async function parseProof(data) {
const blocks = [];
const reader = await CarReader.fromBytes(Buffer.from(data, 'base64'));
for await (const block of reader.blocks()) {
blocks.push(block);
}
return importDAG(blocks);
}
async function frontend() {
// Create a new client
const client = await Client.create();
// Fetch the delegation from the backend
const apiUrl = `/api/w3up-delegation/${client.agent().did()}`;
const response = await fetch(apiUrl);
const data = await response.arrayBuffer();
// Deserialize the delegation
const delegation = await Delegation.extract(new Uint8Array(data));
if (!delegation.ok) {
throw new Error('Failed to extract delegation');
}
// Add proof that this agent has been delegated capabilities on the space
const space = await client.addSpace(delegation.ok);
client.setCurrentSpace(space.did());
// READY to go!
}
sequenceDiagram
participant User
participant Application backend
participant web3.storage w3up service
Application backend->>User: Front end code that includes w3up-client
User->>web3.storage w3up service: (If needed) Create Space and register it
User->>web3.storage w3up service: (If needed) Use Agent email verification to "log in" to Space
User->>web3.storage w3up service: Upload data using w3up-client
w3up-client
methods to create a Space, authorize the Space, and authorize the Agent on the end user-side; from there they can run any of the upload
methodsupload
and get back a content CID, you can have them send that CID to you for tracking)Some environments (for instance Cloudflare Workers) require wasm bytecode to be imported. All other paths to load wasmm are disallowed by embedder. For these use cases, the default export of w3up-client
(most compatible) won't work out of the box. A custom build will need to be created to get the client working.
We created a esbuild-plugin
esbuild-plugin-w3up-client-wasm-import that you can easily use. There is also an example repository.
create
Client
uploadDirectory
uploadFile
uploadCAR
agent
login
accounts
currentSpace
setCurrentSpace
spaces
createSpace
addSpace
proofs
addProof
delegations
createDelegation
remove
capability.access.authorize
capability.access.claim
capability.space.info
capability.store.add
capability.store.list
capability.store.remove
capability.upload.add
capability.upload.list
capability.upload.remove
capability.filecoin.offer
capability.filecoin.info
create
function create (options?: ClientFactoryOptions): Promise<Client>
Create a new w3up client.
If no backing store is passed one will be created that is appropriate for the environment.
If the backing store is empty, a new signing key will be generated and persisted to the store. In the browser an unextractable RSA key will be generated by default. In other environments an Ed25519 key is generated.
If the backing store already has data stored, it will be loaded and used.
More information: ClientFactoryOptions
uploadDirectory
function uploadDirectory (
files: File[],
options: {
retries?: number
signal?: AbortSignal
onShardStored?: ShardStoredCallback
shardSize?: number
concurrentRequests?: number
} = {}
): Promise<CID>
Uploads a directory of files to the service and returns the root data CID for the generated DAG. All files are added to a container directory, with paths in file names preserved.
More information: ShardStoredCallback
uploadFile
function uploadFile (
file: Blob,
options: {
retries?: number
signal?: AbortSignal
onShardStored?: ShardStoredCallback
shardSize?: number
concurrentRequests?: number
} = {}
): Promise<CID>
Uploads a file to the service and returns the root data CID for the generated DAG.
More information: ShardStoredCallback
uploadCAR
function uploadCAR (
car: Blob,
options: {
retries?: number
signal?: AbortSignal
onShardStored?: ShardStoredCallback
shardSize?: number
concurrentRequests?: number
rootCID?: CID
} = {}
): Promise<CID>
Uploads a CAR file to the service. The difference between this function and capability.store.add is that the CAR file is automatically sharded and an "upload" is registered (see capability.upload.add
), linking the individual shards. Use the onShardStored
callback to obtain the CIDs of the CAR file shards.
More information: ShardStoredCallback
agent
function agent (): Signer
The user agent. The agent is a signer - an entity that can sign UCANs with keys from a Principal
using a signing algorithm.
authorize
function authorize (email: string, options?: { signal?: AbortSignal }): Promise<void>
Authorize the current agent to use capabilities granted to the passed email account.
accounts
function accounts (): Record<DID, Account>
List all accounts the agent has stored access to.
currentSpace
function currentSpace (): Space|undefined
The current space in use by the agent.
setCurrentSpace
function setCurrentSpace (did: DID): Promise<void>
Use a specific space.
spaces
function spaces (): Space[]
Spaces available to this agent.
createSpace
async function createSpace (name?: string): Promise<Space>
Create a new space with an optional name.
addSpace
async function addSpace (proof: Delegation): Promise<Space>
Add a space from a received proof. Proofs are delegations with an audience matching the agent DID.
proofs
function proofs (capabilities?: Capability[]): Delegation[]
Get all the proofs matching the capabilities. Proofs are delegations with an audience matching the agent DID.
addProof
function addProof (proof: Delegation): Promise<void>
Add a proof to the agent. Proofs are delegations with an audience matching the agent DID. Note: addSpace
should be used for delegating from Space
to Agent
(i.e., you want the Agent to fully be able to act on behalf of the Space), as it calls addProof
with some additional client logic. addProof
is for more generically adding delegations to the Agent (e.g., delegation targets a resource other than a Space).
delegations
function delegations (capabilities?: Capability[]): Delegation[]
Get delegations created by the agent for others. Filtered optionally by capability.
createDelegation
function createDelegation (
audience: Principal,
abilities: string[],
options?: UCANOptions
): Promise<Delegation>
Create a delegation to the passed audience for the given abilities with the current space as the resource.
remove
function remove (
contentCID?: CID
options: {
shards?: boolean
} = {}
): Promise<void>
Removes association of a content CID with the space. Optionally, also removes association of CAR shards with space.
⚠️ If shards
option is true
all shards will be deleted even if there is another upload(s) that reference same shards, which in turn could corrupt those uploads.
getReceipt
function getReceipt (
taskCid: CID
): Promise<Receipt>
Get a receipt for an executed task by its CID.
capability.access.authorize
function authorize (
email: string,
options: { signal?: AbortSignal } = {}
): Promise<void>
Authorize the current agent to use capabilities granted to the passed email account.
capability.access.claim
function claim (): Promise<Delegation<Capabilities>[]>
Claim delegations granted to the account associated with this agent. Note: the received delegations are added to the agent's persistent store.
capability.store.add
function add (
car: Blob,
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<CID>
Store a CAR file to the service.
capability.store.list
function list (
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<ListResponse<StoreListResult>>
List CAR files stored in the current space.
More information: StoreListResult
, ListResponse
capability.store.remove
function remove (
link: CID,
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<void>
Remove a stored CAR file by CAR CID.
capability.upload.add
function add (
root: CID,
shards: CID[],
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<UploadAddResponse>
Register a set of stored CAR files as an "upload" in the system. A DAG can be split between multiple CAR files. Calling this function allows multiple stored CAR files to be considered as a single upload.
capability.upload.list
function list(
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<ListResponse<UploadListResult>>
List uploads created in the current space.
More information: UploadListResult
, ListResponse
capability.upload.remove
function remove(
link: CID,
options: { retries?: number; signal?: AbortSignal } = {}
): Promise<void>
Remove a upload by root data CID.
capability.filecoin.offer
function offer (
content: CID,
piece: PieceLink,
): Promise<FilecoinOfferResponse>
Offer a Filecoin "piece" to be added to an aggregate that will be offered for Filecoin deal(s).
capability.filecoin.info
function info (
piece: PieceLink
): Promise<FilecoinInfoResponse>
Get know deals and aggregate info of a Filecoin "piece" previously offered.
Capability
An object describing a UCAN capability, which specifies what action the UCAN holder can
perform with
some resource.
Defined by the @ipld/dag-ucan
package.
export interface Capability<
Can extends Ability = Ability,
With extends Resource = Resource,
Caveats extends unknown = unknown
> {
with: With
can: Can
nb?: Caveats
}
export type Ability = `${string}/${string}` | "*"
export type Resource = `${string}:${string}`
The can
field contains a string ability identifier, e.g. store/add
or space/info
.
The with
field contains a resource URI, often a did:key
URI that identifies a Space.
The optional nb
(nota bene) field contains "caveats" that add supplemental information to a UCAN invocation or delegation.
See the @web3-storage/capabilities
package for more information about capabilities and how they are defined in w3up services.
CARMetadata
Metadata pertaining to a CAR file.
export interface CARMetadata {
/**
* CAR version number.
*/
version: number
/**
* Root CIDs present in the CAR header.
*/
roots: CID[]
/**
* CID of the CAR file (not the data it contains).
*/
cid: CID
/**
* Size of the CAR file in bytes.
*/
size: number
}
Delegation
An in-memory view of a UCAN delegation, including proofs that can be used to invoke capabilities or delegate to other agents.
import { Delegation as CoreDelegation } from '@ucanto/core/delegation'
export interface Delegation extends CoreDelegation {
/**
* User defined delegation metadata.
*/
meta(): Record<string, any>
}
The Delegation
type in w3up-client
extends the Delegation
type defined by ucanto
:
export interface Delegation<C extends Capabilities = Capabilities> {
readonly root: UCANBlock<C>
readonly blocks: Map<string, Block>
readonly cid: UCANLink<C>
readonly bytes: ByteView<UCAN.UCAN<C>>
readonly data: UCAN.View<C>
asCID: UCANLink<C>
export(): IterableIterator<Block>
issuer: UCAN.Principal
audience: UCAN.Principal
capabilities: C
expiration?: UCAN.UTCUnixTimestamp
notBefore?: UCAN.UTCUnixTimestamp
nonce?: UCAN.Nonce
facts: Fact[]
proofs: Proof[]
iterate(): IterableIterator<Delegation>
}
Delegations can be serialized by calling export()
and piping the returned Block
iterator into a CarWriter
from the @ipld/car
package.
Driver
Storage drivers can be obtained from @web3-storage/access/stores
. They persist data created and managed by an agent.
ListResponse
A paginated list of items.
interface ListResponse<R> {
cursor?: string
size: number
results: R[]
}
ServiceConf
Service DID and URL configuration.
ShardStoredCallback
A function called after a DAG shard has been successfully stored by the service:
type ShardStoredCallback = (meta: CARMetadata) => void
More information: CARMetadata
Space
An object representing a storage location. Spaces must be registered with the service before they can be used for storage.
interface Space {
/**
* The given space name.
*/
name(): string
/**
* The DID of the space.
*/
did(): string
/**
* Whether the space has been registered with the service.
*/
registered(): boolean
/**
* User defined space metadata.
*/
meta(): Record<string, any>
}
StoreListResult
interface StoreListResult {
link: CID
size: number
origin?: CID
}
UploadListResult
interface UploadListResult {
root: CID
shards?: CID[]
}
Feel free to join in. All welcome. Please open an issue!
Dual-licensed under MIT + Apache 2.0
Generated using TypeDoc
The main entry point for the
@web3-storage/w3up-client
package.Use the static create function to create a new Client object.