-
Notifications
You must be signed in to change notification settings - Fork 167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: added script to generate ucan for w3up space and docs for how to access the space using console.web3.storage #2554
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -136,3 +136,55 @@ We use [pickup](https://github.com/web3-storage/pickup) to fetch DAGs from IPFS | |
- `PICKUP_BASIC_AUTH_TOKEN` must be set as a secret in the env. | ||
|
||
For local dev, we use a local ipfs-cluster container for the same service. | ||
|
||
## w3up | ||
|
||
Some uploads sent to nft.storage/api will be sent to up.web3.storage (aka 'w3up') for storage, serving on IPFS, and persistence to filecoin. | ||
|
||
All uploads sent to w3up will be stored in the same web3.storage space configured by env var `W3_NFTSTORAGE_SPACE`. | ||
|
||
### using console.web3.storage to browse uploads to w3up | ||
|
||
You can use console.web3.storage to browse uploads in the W3_NFTSTORAGE_SPACE. | ||
|
||
The DID used by your console.web3.storage session will need to be authorized to access the space. | ||
|
||
The credentials used in staging/production are in the usual vault of secrets under 'w3up credentials'. | ||
|
||
Run the cli command `w3up console ucan generate` | ||
|
||
```shell | ||
( | ||
cd packages/api | ||
node scripts/cli.js w3up console ucan generate | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh lol but I just noticed this - also seems fine. Since this README is already in the |
||
) | ||
``` | ||
|
||
If you see a prompt like "ID of subject that should be authorized": | ||
|
||
- you need to enter the ID of your console.web3.storage session. To get this, visit https://console.web3.storage/space/import. Look for "Send your DID to your friend". After that is a URI starting with `did:`. Copy that and enter it in the prompt. | ||
|
||
If you see a prompt like "space recovery key mnemonic": | ||
|
||
- look in the secrets vault for the mnemonic phrase labeled "Space Recovery Key". Copy that value into the prompt. | ||
|
||
If you see a prompt like "What name do you want to appear in console.web3.storage when this space is | ||
imported?": | ||
|
||
- enter whatever name you want that will help you distinguish this space from other spaces listed in console.web3.storage. | ||
|
||
If you see a prompt like "output ucan car to file /tmp/nftstorage-w3up-1712101390513.ucan.car?" | ||
|
||
- hit enter or type 'Y' to confirm | ||
|
||
Now a UCAN delegation has been written to a CAR file at a path like `/tmp/nftstorage-w3up-1712101390513.ucan.car`. | ||
|
||
To add this delegation to console.web3.storage: | ||
|
||
1. use a web browser to access https://console.web3.storage/space/import | ||
2. Click 'Import UCAN'. This should open a file picker | ||
3. Select the file generated from the last script, e.g. `/tmp/nftstorage-w3up-1712101390513.ucan.car` | ||
|
||
You should see 'Added' and a list containing the space with the name you chose when generating the UCAN CAR. Click 'View' to view the contents of the Space. | ||
|
||
After importing the space, it will also be listed in the space listing at https://console.web3.storage/. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
import { input, confirm } from '@inquirer/prompts' | ||
import { DID } from '@ucanto/core/schema' | ||
import * as Space from '@web3-storage/access/space' | ||
import * as fsp from 'node:fs/promises' | ||
import { CarWriter } from '@ipld/car/writer' | ||
|
||
export async function requestW3upConsoleUcan() { | ||
console.log('requestW3upConsoleUcan') | ||
|
||
/* | ||
We're going to get everything we need to build a UCAN | ||
that authorizes a session (e.g. a console.web3.storage agent) to | ||
do w3up stuff in some space. | ||
*/ | ||
|
||
// subject of the authorization, | ||
// e.g. a DID of a console.web3.storage session | ||
/** @type {`did:${string}:${string}` | undefined} */ | ||
let subjectId | ||
{ | ||
// if env var W3UP_UCAN_SUBJECT is set, | ||
// parse as DID and use as subjectId | ||
let subjectIdFromEnv | ||
if ((subjectIdFromEnv = process.env.W3UP_UCAN_SUBJECT)) { | ||
const did = DID.read(subjectIdFromEnv).ok | ||
if (!did) | ||
throw new Error( | ||
`failed to parse W3UP_UCAN_SUBJECT env var as DID: ${subjectIdFromEnv}` | ||
) | ||
if ( | ||
await confirm({ | ||
message: `use W3UP_UCAN_SUBJECT env var value as authorization subject? ${did}`, | ||
}) | ||
) { | ||
subjectId = did | ||
} else { | ||
console.warn('will not use value of W3UP_UCAN_SUBJECT env var') | ||
} | ||
} else { | ||
const subjectIdFromPrompt = await input({ | ||
message: | ||
'ID of subject that should be authorized. e.g. you can copy the DID of your console session from https://console.web3.storage/space/import', | ||
}) | ||
const subjectIdDid = DID.read(subjectIdFromPrompt).ok | ||
if (subjectIdDid) { | ||
subjectId = subjectIdDid | ||
} else { | ||
throw new Error(`Failed to parse as DID: ${subjectIdFromPrompt}`) | ||
} | ||
} | ||
} | ||
// now we should have a subjectId | ||
const subjectPrincipal = { | ||
did() { | ||
if (!subjectId) throw new Error(`failed to determine subject of ucan`) | ||
return subjectId | ||
}, | ||
} | ||
|
||
/** | ||
* Issuer of the authorization. | ||
* e.g. ultimately the issuer must have authority rooted in the space itself, | ||
* so this issuer may be the space ID. | ||
* @type {Space.OwnedSpace | undefined} | ||
*/ | ||
let space | ||
|
||
// if W3UP_SPACE_RECOVERY is set and user confirms, build issuer of UCANs from it | ||
let spaceRecoveryMnemonic | ||
if (process.env.W3UP_SPACE_RECOVERY) { | ||
if ( | ||
await confirm({ | ||
message: `issue ucan using space from env var W3UP_SPACE_RECOVERY?`, | ||
}) | ||
) { | ||
spaceRecoveryMnemonic = process.env.W3UP_SPACE_RECOVERY | ||
} else { | ||
console.warn(`will not use env var W3UP_SPACE_RECOVERY`) | ||
} | ||
} | ||
// if no spaceRecoveryKey from env var, prompt for a recovery key | ||
if (!spaceRecoveryMnemonic) { | ||
spaceRecoveryMnemonic = await input({ | ||
message: 'space recovery key mnemonic', | ||
}) | ||
} | ||
|
||
/** | ||
* name that should appear for this space in console.web3.storage. | ||
* @type {string} | ||
*/ | ||
let nameForSpaceInConsole = await input({ | ||
message: `What name do you want to appear in console.web3.storage when this space is imported? (e.g. 'staging.nft.storage NFTs')`, | ||
default: `nftstorage-${Date.now()}`, | ||
}) | ||
|
||
space = await Space.fromMnemonic(spaceRecoveryMnemonic, { | ||
name: nameForSpaceInConsole, | ||
}) | ||
|
||
// We now have a space object | ||
if (!space) | ||
throw new Error(`unable to build a space object from inputs: ${space}`) | ||
console.warn('space', space.did()) | ||
|
||
// now let's have the space sign a UCAN that authorizes the subject | ||
// to access the space | ||
const authorizationForSubjectToAccessSpace = await space.createAuthorization( | ||
subjectPrincipal | ||
) | ||
|
||
const exportedUcan = await toCarBlob(authorizationForSubjectToAccessSpace) | ||
|
||
// we want to save to a file | ||
const outputPath = `/tmp/nftstorage-w3up-${Date.now()}.ucan.car` | ||
if (await confirm({ message: `output ucan car to file ${outputPath}?` })) { | ||
await fsp.writeFile(outputPath, exportedUcan.stream()) | ||
console.warn(`wrote`, outputPath) | ||
console.warn( | ||
`When this delegation is imported into console.web3.storage, the space will be shown with the name "${nameForSpaceInConsole}"` | ||
) | ||
} else { | ||
console.warn(`did not output ucan to file because no confirmation`) | ||
} | ||
} | ||
|
||
/** | ||
* given a UCAN delegation, return a Blob of the serialized delegation. | ||
* It's serialized to a CAR file in a way where console.web3.storage import will accept it | ||
* when imported via https://github.com/web3-storage/console/blob/main/src/share.tsx#L138 | ||
* @param {import('@ucanto/interface').Delegation} delegation | ||
* @returns {Promise<Blob>} | ||
*/ | ||
export async function toCarBlob(delegation) { | ||
const { writer, out } = CarWriter.create() | ||
for (const block of delegation.export()) { | ||
// @ts-expect-error slight Block type mismatch | ||
void writer.put(block) | ||
} | ||
void writer.close() | ||
|
||
const carParts = [] | ||
for await (const chunk of out) { | ||
carParts.push(chunk) | ||
} | ||
const car = new Blob(carParts, { | ||
type: 'application/vnd.ipld.car', | ||
}) | ||
return car | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe better to use the yarn command?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
by 'the cli command' I meant 'arguments passed to the ./scripts/cli.js I found'. So I think the way I wrote it was ambiguous and probably should change, but not necessarily to one that mentions yarn.