-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Ok all much better now --------- Co-authored-by: David Choi <[email protected]>
- Loading branch information
Showing
18 changed files
with
403 additions
and
360 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
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 |
---|---|---|
@@ -0,0 +1,173 @@ | ||
import { Callout } from 'nextra/components' | ||
|
||
# Possible architectures using web3.storage to upload | ||
|
||
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: | ||
|
||
- **Client-server**: You (the developer) own the space, your users upload data to your application server, and you upload it to web3.storage from there. | ||
- **Delegated**: You own the Space, but you provide your user with a delegation that allows them to upload directly to the service. This avoids proxying the upload through your backend (no egress from your infrastructure). | ||
- **User-owned**: Your user owns the Space and registers it and they use it to upload directly with the service; if you want to instrument visibility into what they're uploading, you'll have to write separate code in your app for it. | ||
|
||
In the How-tos section of the docs, we focused on the first two options, as they are the most typical today. However, you can implement each of these in a number of ways, but we talk through some considerations when implementing a given option. | ||
|
||
## Client-server | ||
|
||
In this set up, you (the developer) own the space, your users upload data to your application server, and you upload it to web3.storage from there. | ||
|
||
```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 | ||
``` | ||
|
||
You'll need a registered Space, and your client in the backend to have a delegation to use the Space. | ||
|
||
<Callout> | ||
**Recommended**: It's easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli), then create an identity for your server and delegate it the ability to upload to your Space. [This example](https://github.com/web3-storage/w3up/tree/main/packages/w3up-client#bringing-your-own-agent-and-delegation) shows how to load an identity and a delegation into the client on the server. | ||
</Callout> | ||
|
||
After this, once your user uploads data to your backend, you can run any of the upload methods. | ||
|
||
For your backend to be scalable, you might consider using serverless workers or a queue in front of a server. | ||
|
||
## Delegated | ||
|
||
In this set up, you own the Space, but you provide your user with a delegation that allows them to upload directly to the service. This avoids proxying the upload through your backend (no egress from your infrastructure). | ||
|
||
```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 | ||
``` | ||
|
||
Typically `w3up-client` will be 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). | ||
|
||
You'll need a registered Space, and your client in the backend to have a delegation to use the Space. | ||
|
||
<Callout> | ||
**Recommended**: It's easiest to create and register your Space using [w3cli](https://github.com/web3-storage/w3cli), then create an identity for your server and delegate it the ability to upload to your Space. [This example](https://github.com/web3-storage/w3up/tree/main/packages/w3up-client#bringing-your-own-agent-and-delegation) shows how to load an identity and a delegation into the client on the server. | ||
</Callout> | ||
|
||
Your backend code can then re-delegate it's capabilities to your users. Your user does not need a registered Space - just an Agent with a delegation from your Space. | ||
|
||
When your user is ready to upload, they should request a delegation from your backend to upload to your Space. They must send their Agent DID so that the delegation is restricted to be used by only them. | ||
|
||
In your backend, you can call [`client.createDelegation(...)`](https://github.com/web3-storage/w3up/blob/main/packages/w3up-client/docs-Client#createDelegation) passing in the Agent object from `client.agent()` in your end user's instance, and passing params to limit the scope of the delegation (e.g., `store/add`, `upload/add`, expiration time) | ||
|
||
You can serialize the delegation using `delegation.archive()` and send it to your user. | ||
|
||
The client the user is using does not need to call `client.login(email)`, as it is not claiming any delegations via email address (but instead getting a delegation from your backend). | ||
|
||
|
||
When your user receives the delegation, they can deserialize it using [`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. | ||
|
||
<Callout type="info"> | ||
This does not provide visibility over which users are uploading what; to track this, you'll need to share 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). | ||
</Callout> | ||
|
||
A code example that does this can be found below: | ||
|
||
**Backend** | ||
|
||
```js | ||
import { CarReader } from '@ipld/car' | ||
import * as DID from '@ipld/dag-ucan/did' | ||
import * as Delegation from '@ucanto/core/delegation' | ||
import * as Signer from '@ucanto/principal/ed25519' | ||
import * as Client from '@web3-storage/w3up-client' | ||
|
||
async function backend(did) { | ||
// 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 Delegation.importDAG(blocks) | ||
} | ||
``` | ||
|
||
**Frontend** | ||
|
||
```js | ||
import * as Delegation from '@ucanto/core/delegation' | ||
import * as Client from '@web3-storage/w3up-client' | ||
|
||
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', { cause: delegation.error }) | ||
} | ||
|
||
// 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 | ||
|
||
In this set up your user owns the Space and registers it and they use it to upload directly with the service; if you want to instrument visibility into what they're uploading, you'll have to write separate code in your app for it. | ||
|
||
```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 typically be relying on the `w3up-client` methods to create a Space, authorize the Space, and authorize the Agent on the user-side; afterwards they can run any of the upload methods. | ||
|
||
<Callout type="info"> | ||
This does not provide visibility over which users are uploading what; to track this, you'll need to share 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). | ||
</Callout> | ||
|
||
There is a world of possibilities when users "bring their own" Space; you could explore how crypto wallet private keys, Apple Passkey, and more might map to Space DIDs and have the client use those. Your users could delegate capabilities for their space to your backend application or share spaces between friends by delegating to each other. | ||
|
||
--- | ||
|
||
If you have code snippet(s) for any of the above architectural options, please share them in a PR or [Github issue](https://github.com/web3-storage/www/issues) and we'll link them here! |
Oops, something went wrong.