-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: diagrams in readme, delegated example (#909)
Co-authored-by: Travis Vachon <[email protected]>
- Loading branch information
Showing
1 changed file
with
139 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,12 +27,19 @@ This library is the user-facing "porcelain" client for interacting with w3up ser | |
- [Install](#install) | ||
- [Usage](#usage) | ||
- [Core concepts](#core-concepts) | ||
- [Basic usage](#basic-usage) | ||
- [How w3up and w3up-client use UCANs](#how-w3up-and-w3up-client-use-ucans) | ||
- [Space](#space) | ||
- [Agent](#agent) | ||
- [Basic usage with web3.storage](#basic-usage-with-web3-storage) | ||
- [Creating a client object](#creating-a-client-object) | ||
- [Creating and registering Spaces](#creating-and-registering-spaces) | ||
- [Delegating from Space to Agent](#delegating-from-space-to-agent) | ||
- [Bringing your own Agent and delegation](#bringing-your-own-agent-and-delegation) | ||
- [Uploading data](#uploading-data) | ||
- [Alternate implementation options](#alternate-implementation-options) - _Coming soon!_ | ||
- [Integration options](#integration-options) | ||
- [Client-server](#client-server) | ||
- [Delegated](#delegated) | ||
- [User owned](#user-owned) | ||
- [API](#api) | ||
- [Contributing](#contributing) | ||
- [License](#license) | ||
|
@@ -79,7 +86,15 @@ The delegation from a Space to your Agent that `w3up-client` needs can be passed | |
|
||
### Basic usage with web3.storage | ||
|
||
This section shows the basic flows to start storing data using the `w3up-client` package with web3.storage. For more complex integration options, check out the [integration options][integration options] docs. For reference, check out the [API reference docs][docs] or the source code of the [`w3up-cli` package][w3up-cli-github], which uses `w3up-client` throughout. | ||
```mermaid | ||
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 and register it) | ||
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 basic flows to start storing data. For more complex integration options, check out the [integration options][#integration-options] docs. For reference, check out the [API reference docs][docs] or the source code of the [`w3up-cli` package][w3up-cli-github], which uses `w3up-client` throughout. | ||
|
||
> By you or your users registering a w3up beta Space via email confirmation with [web3.storage](http://web3.storage), you agree to the beta [Terms of Service](https://console.web3.storage/terms). Uploads to w3up will not appear in your web3.storage account (and vice versa). | ||
|
@@ -148,6 +163,14 @@ In order to store data with w3up, your Agent will need a delegation from a Space | |
await client.authorize('[email protected]') | ||
``` | ||
|
||
```mermaid | ||
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 | ||
``` | ||
|
||
Calling `authorize` will cause an email to be sent to the given address. Once a user clicks the confirmation link in the email, the `authorize` method will resolve. Make sure to check for errors, as `authorize` will fail if the email is not confirmed within the expiration timeout. | ||
|
||
##### Bringing your own Agent and delegation | ||
|
@@ -227,25 +250,122 @@ As mentioned, UCAN opens up a number of options in how to integrate with w3up: S | |
|
||
You can implement each of these in a number of ways, but we talk through some considerations when implementing a given option. | ||
|
||
1. Client-server | ||
#### Client-server | ||
```mermaid | ||
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 | ||
``` | ||
- For your backend to be scalable, you might consider using serverless workers or a queue in front of a server | ||
- In either case, it's likely easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli) rather than using `w3up-client` to do so | ||
- You can then generate your own Agent and delegate the ability to upload to your Space using something like [this example](#bringing-your-own-agent-and-delegation) | ||
- Once your user uploads data to your backend, you can run any of the `upload` methods | ||
|
||
2. Delegated | ||
- In either case, you'll need a registered Space, and your client instance in your backend to have an Agent with a delegation from this Space | ||
- (Recommended) It's likely easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli) rather than using `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 example](#bringing-your-own-agent-and-delegation) | ||
- If your backend is persistent, you can do this or do everything in the client directly ([create Space](#creating-and-registering-spaces) and [get delegation](#delegating-from-space-to-agent)) | ||
- After this, once your user uploads data to your backend, you can run any of the `upload` methods | ||
|
||
#### Delegated | ||
```mermaid | ||
sequenceDiagram | ||
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 | ||
``` | ||
- You will likely have `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) | ||
- As the developer, you should create and register a Space; it's likely easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli) rather than using `w3up-client` to do so | ||
- `w3up-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) | ||
- From there, when your end user is ready to upload, they should request from your backend a delegation from your developer-owned Space to their Agent (which can be derived via [`client.agent()`](#agent)) | ||
- If you are running `w3up-client` in your backend should follow the instructions in the "Client-server" section to set it up | ||
- From there, you can call [`client.createDelegation()`](#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 (__todo: documentation on options for delegations__) | ||
- For your backend to be scalable, you might consider using serverless workers or a queue in front of a server | ||
- As the developer, you'll need a registered Space, and your client instance in your backend to have an Agent with a delegation from this Space | ||
- (Recommended) It's likely easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli) rather than using `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 example](#bringing-your-own-agent-and-delegation) | ||
- If your backend is persistent, you can do this or do everything in the client directly ([create Space](#creating-and-registering-spaces) and [get delegation](#delegating-from-space-to-agent)) | ||
- Your user does not need a registered Space - just an Agent with a delegation from your Space | ||
- `w3up-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) | ||
- From there, when your end user is ready to upload, they should request from your backend a delegation from your developer-owned Space to their Agent (which can be derived via [`client.agent()`](docs-Client#agent)) | ||
- In your backend, you can call [`client.createDelegation()`](docs-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) | ||
- You can serialize this using `delegation.archive()` and send it to your user | ||
- The end user instance of the client should not need to call `client.authorize(email)`, as it is not claiming any delegations via email address (but rather getting the delegation directly from your backend) | ||
- Once your user receives the delegation, they can pass it in using `client.addSpace()`, and from there they can run any of the `upload` methods | ||
- Once your user receives the delegation, they can deserialize it using [`ucanto.Delegation.extract()`](https://github.com/web3-storage/ucanto/blob/c8999a59852b61549d163532a83bac62290b629d/packages/core/src/delegation.js#L399) and pass it in using `client.addSpace()`, and from there they can run any of the `upload` methods | ||
- Note that this alone does not give visibility into which of your end users are uploading what; to track this, you'll probably need them to send you that information separately (e.g., once they've run `upload` and get back a content CID, you can have them send that CID to you for tracking) | ||
- If you have code snippet(s) that works for you, please share them in a PR or [Github issue](https://github.com/web3-storage/w3up/issues) and we'll link them here! | ||
- A code example that does this can be found below | ||
|
||
3. User-owned | ||
```js | ||
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! | ||
} | ||
``` | ||
|
||
#### User-owned | ||
```mermaid | ||
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 | ||
``` | ||
- If you want your user to own their own Space, you'll likely be relying on the `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` methods | ||
- Doing this does take some of the UX out of your control; for instance, when web3.storage fully launches with w3up, your users will have to set up their payment methods with web3.storage | ||
- Note that this alone does not give visibility into which of your end users are uploading what; to track this, you'll probably need them to send you that information separately (e.g., once they've run `upload` and get back a content CID, you can have them send that CID to you for tracking) | ||
|
@@ -784,6 +904,7 @@ Dual-licensed under [MIT + Apache 2.0](https://github.com/web3-storage/w3up-clie | |
[docs]: https://web3-storage.github.io/w3up-client | ||
[docs-Client]: https://web3-storage.github.io/w3up-client/classes/client.Client.html | ||
[docs-Client#agent]: https://web3-storage.github.io/w3up-client/classes/client.Client.html#agent | ||
[docs-Client#createDelegation]: https://web3-storage.github.io/w3up-client/classes/client.Client.html#createDelegation | ||
[docs-Client#createSpace]: https://web3-storage.github.io/w3up-client/classes/client.Client.html#createSpace | ||
[docs-Client#setCurrentSpace]: https://web3-storage.github.io/w3up-client/classes/client.Client.html#setCurrentSpace | ||
[docs-Client#registerSpace]: https://web3-storage.github.io/w3up-client/classes/client.Client.html#registerSpace | ||
|