Skip to content

Commit 98284ef

Browse files
authored
feat: w3 delegation create --base64 & w3 space add <base64> (storacha#158)
**export** a delegation as base64 encoded identity CID with `w3 delegation create --base64` ```shell $ w3 delegation create did:key:z6MkviAsUfBwegmB57byQ7SZTFtX4jNjo315EvgurjWYoTRX --can 'store/add' --can 'upload/add' --base64 mAYIEAO0OEaJlcm9vdHOAZ3ZlcnNpb24BvQUBcRIgL7w/mAWPOV8Wt/B0ygdaOI+20/ZG8pX+5YzZ5X9U4y6oYXNYRO2hA0CbPDJlxyrorHHdNAUnRUDA4xU7KHgHHstkM8tBxq+6KaQP5xLCknOh9TjkR0S0yuK/fiFxKwRDUHfECFEWQn4DYXZlMC45LjFjYXR0hqJjY2FuZ3NwYWNlLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmdzdG9yZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5odXBsb2FkLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmhhY2Nlc3MvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV6JjY2FuamZpbGVjb2luLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2Nhbmd1c2FnZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXY2F1ZFgi7QHob+19JDMUBs+u1e646vN2MLovQUXA7xJeFs2THUcb+mNleHAaZzctGmNmY3SBoWVzcGFjZaFkbmFtZWV0b290c2Npc3NYIu0BL9X+p4Uyz05zSH0ol8TYPXpwU9EljNRo1O18uYbWlvljcHJmgL0FAXESIC+8P5gFjzlfFrfwdMoHWjiPttP2RvKV/uWM2eV/VOMuqGFzWETtoQNAmzwyZccq6Kxx3TQFJ0VAwOMVOyh4Bx7LZDPLQcavuimkD+cSwpJzofU45EdEtMriv34hcSsEQ1B3xAhRFkJ+A2F2ZTAuOS4xY2F0dIaiY2NhbmdzcGFjZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5nc3RvcmUvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV6JjY2FuaHVwbG9hZC8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5oYWNjZXNzLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmpmaWxlY29pbi8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5ndXNhZ2UvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV2NhdWRYIu0B6G/tfSQzFAbPrtXuuOrzdjC6L0FFwO8SXhbNkx1HG/pjZXhwGmc3LRpjZmN0gaFlc3BhY2WhZG5hbWVldG9vdHNjaXNzWCLtAS/V/qeFMs9Oc0h9KJfE2D16cFPRJYzUaNTtfLmG1pb5Y3ByZoDbAwFxEiB9iHpD1ttdKEQCvBZ8jJBD7Wqw1abOtYwNCKAKYALXMqhhc1hE7aEDQJ7U8I+a4Au/eb10r9T89weG/Nl2jccEUXHs8wq+i2tU0Iaik8KaKvovDqqE57JU8ZoY0JAzOBW7cMLGcV6/UwthdmUwLjkuMWNhdHSComNjYW5pc3RvcmUvYWRkZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5qdXBsb2FkL2FkZGR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV2NhdWRYIu0B8YzaLs8NDe7oZt6rlpsW6iMh8XsoXZLvkHPqtftYRXZjZXhw9mNmY3SBoWVzcGFjZaFkbmFtZWV0b290c2Npc3NYIu0B6G/tfSQzFAbPrtXuuOrzdjC6L0FFwO8SXhbNkx1HG/pjcHJmgtgqWCUAAXESIC+8P5gFjzlfFrfwdMoHWjiPttP2RvKV/uWM2eV/VOMu2CpYJQABcRIgL7w/mAWPOV8Wt/B0ygdaOI+20/ZG8pX+5YzZ5X9U4y4 ``` ...yes, what if we put the CAR _in_ the CID! That way we can detect when the input has been truncted, and the screed is self describing... you can paste it into [cid.ipfs.tech](https://cid.ipfs.tech/#mAYIEAO0OEaJlcm9vdHOAZ3ZlcnNpb24BvQUBcRIgL7w/mAWPOV8Wt/B0ygdaOI+20/ZG8pX+5YzZ5X9U4y6oYXNYRO2hA0CbPDJlxyrorHHdNAUnRUDA4xU7KHgHHstkM8tBxq+6KaQP5xLCknOh9TjkR0S0yuK/fiFxKwRDUHfECFEWQn4DYXZlMC45LjFjYXR0hqJjY2FuZ3NwYWNlLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmdzdG9yZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5odXBsb2FkLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmhhY2Nlc3MvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV6JjY2FuamZpbGVjb2luLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2Nhbmd1c2FnZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXY2F1ZFgi7QHob+19JDMUBs+u1e646vN2MLovQUXA7xJeFs2THUcb+mNleHAaZzctGmNmY3SBoWVzcGFjZaFkbmFtZWV0b290c2Npc3NYIu0BL9X+p4Uyz05zSH0ol8TYPXpwU9EljNRo1O18uYbWlvljcHJmgL0FAXESIC+8P5gFjzlfFrfwdMoHWjiPttP2RvKV/uWM2eV/VOMuqGFzWETtoQNAmzwyZccq6Kxx3TQFJ0VAwOMVOyh4Bx7LZDPLQcavuimkD+cSwpJzofU45EdEtMriv34hcSsEQ1B3xAhRFkJ+A2F2ZTAuOS4xY2F0dIaiY2NhbmdzcGFjZS8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5nc3RvcmUvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV6JjY2FuaHVwbG9hZC8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5oYWNjZXNzLypkd2l0aHg4ZGlkOmtleTp6Nk1raGZ6VHdaSjI4YVJvYkNwNzZ1WFJxenNqSDZHTnUxOFdGd011bWtBRjVvaleiY2NhbmpmaWxlY29pbi8qZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5ndXNhZ2UvKmR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV2NhdWRYIu0B6G/tfSQzFAbPrtXuuOrzdjC6L0FFwO8SXhbNkx1HG/pjZXhwGmc3LRpjZmN0gaFlc3BhY2WhZG5hbWVldG9vdHNjaXNzWCLtAS/V/qeFMs9Oc0h9KJfE2D16cFPRJYzUaNTtfLmG1pb5Y3ByZoDbAwFxEiB9iHpD1ttdKEQCvBZ8jJBD7Wqw1abOtYwNCKAKYALXMqhhc1hE7aEDQJ7U8I+a4Au/eb10r9T89weG/Nl2jccEUXHs8wq+i2tU0Iaik8KaKvovDqqE57JU8ZoY0JAzOBW7cMLGcV6/UwthdmUwLjkuMWNhdHSComNjYW5pc3RvcmUvYWRkZHdpdGh4OGRpZDprZXk6ejZNa2hmelR3WkoyOGFSb2JDcDc2dVhScXpzakg2R051MThXRndNdW1rQUY1b2pXomNjYW5qdXBsb2FkL2FkZGR3aXRoeDhkaWQ6a2V5Ono2TWtoZnpUd1pKMjhhUm9iQ3A3NnVYUnF6c2pINkdOdTE4V0Z3TXVta0FGNW9qV2NhdWRYIu0B8YzaLs8NDe7oZt6rlpsW6iMh8XsoXZLvkHPqtftYRXZjZXhw9mNmY3SBoWVzcGFjZaFkbmFtZWV0b290c2Npc3NYIu0B6G/tfSQzFAbPrtXuuOrzdjC6L0FFwO8SXhbNkx1HG/pjcHJmgtgqWCUAAXESIC+8P5gFjzlfFrfwdMoHWjiPttP2RvKV/uWM2eV/VOMu2CpYJQABcRIgL7w/mAWPOV8Wt/B0ygdaOI+20/ZG8pX+5YzZ5X9U4y4) and it'll tell you it's CAR flavour identity hashed bytes! **import** a space from a stringified proof ```shell $ w3 space add $PROOF did:key:z6MkhfzTwZJ28aRobCp76uXRqzsjH6GNu18WFwMumkAF5ojW ``` see: storacha#154 (comment) ## Usage **on your machine** - with `@web3-storage/w3cli` installed. - Set the space you want CI to upload to as the current space ```shell # create a key for ci $ w3 key create --json > ci.json # create a proof for that key. copy paste it to env var in CI $ w3 delegation create $(jq -r .did ci.json) -c 'store/add' -c 'upload/add' --base64 mA... ``` **in CI** - install `@web3-storage/w3cli`. No need to login, we pass it the proof to use. - copy paste the key we generated above `(jq -r .key ci.json)` as a secret in your ci workflow. _KEEP IT SECRET!_ - copy paste the proof we generated above as a variable or secret in your ci workflow. _it's ok to share this, only the holder of the secret key can use it._ ```shell # set key (jq -r .key ci.json) in env so w3cli uses it instead of generating one $ W3_PRINCIPAL=${{ secrets.w3key }} # import the space from the stringified proof $ w3 space add ${{ vars.w3proof }} # upload yer stuff $ w3 upload ./my/cool/stuff ``` License: MIT --------- Signed-off-by: Oli Evans <oli@protocol.ai>
1 parent 55f546b commit 98284ef

File tree

5 files changed

+141
-20
lines changed

5 files changed

+141
-20
lines changed

README.md

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ w3 open bafybeidluj5ub7okodgg5v6l4x3nytpivvcouuxgzuioa6vodg3xt2uqle/olizilla.png
112112

113113
Print information about the current agent.
114114

115-
### `w3 space add <proof.ucan>`
115+
### `w3 space add <proof>`
116116

117-
Add a space to the agent. The proof is a CAR encoded delegation to _this_ agent.
117+
Add a space to the agent. The proof is a CAR encoded UCAN delegating capabilities over a space to _this_ agent.
118+
119+
`proof` is a filesystem path to a CAR encoded UCAN, as generated by `w3 delegation create` _or_ a base64 identity CID string as created by `w3 delegation create --base64`.
118120

119121
### `w3 space create [name]`
120122

@@ -144,17 +146,14 @@ Create a delegation to the passed audience for the given abilities with the _cur
144146
- `--name` Human readable name for the audience receiving the delegation.
145147
- `--type` Type of the audience receiving the delegation, one of: device, app, service.
146148
- `--output` Path of file to write the exported delegation data to.
149+
- `--base64` Format as base64 identity CID string. Useful when saving it as an environment variable.
147150

148151
```bash
149-
# delegate space/info to did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN
150-
w3 delegation create did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN --can space/info
151-
152-
# delegate store/* and upload/* to did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN
153-
w3 delegation create did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN --can 'store/*' --can 'upload/*'
152+
# delegate space/info to did:key:z6M..., output as a CAR
153+
w3 delegation create did:key:z6M... --can space/info --output ./info.ucan
154154

155-
# delegate all capabilities to did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN
156-
# WARNING - this is bad practice and should generally only be done in testing and development
157-
w3 delegation create did:key:z6MkrwtRceSo2bE6vAY4gi8xPNfNszSpvf8MpAHnxVfMYreN --can '*'
155+
# delegate store/* and upload/* to did:key:z6M..., output as a string
156+
w3 delegation create did:key:z6M... --can 'store/*' --can 'upload/*' --base64
158157
```
159158

160159
### `w3 delegation ls`

bin.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ cli
142142
cli
143143
.command('space add <proof>')
144144
.describe(
145-
'Add a space to the agent. The proof is a CAR encoded delegation to _this_ agent.'
145+
'Import a space from a proof: a CAR encoded UCAN delegating capabilities to this agent. proof is a filesystem path, or a base64 encoded cid string.'
146146
)
147147
.action(addSpace)
148148

@@ -181,7 +181,7 @@ cli
181181
cli
182182
.command('delegation create <audience-did>')
183183
.describe(
184-
'Create a delegation to the passed audience for the given abilities with the _current_ space as the resource.'
184+
'Output a CAR encoded UCAN that delegates capabilities to the audience for the current space.'
185185
)
186186
.option('-c, --can', 'One or more abilities to delegate.')
187187
.option(
@@ -201,6 +201,10 @@ cli
201201
'-o, --output',
202202
'Path of file to write the exported delegation data to.'
203203
)
204+
.option(
205+
'--base64',
206+
'Format as base64 identity CID string. Useful when saving it as an environment variable.'
207+
)
204208
.action(createDelegation)
205209

206210
cli

index.js

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import fs from 'fs'
22
import ora, { oraPromise } from 'ora'
3-
import { Readable } from 'stream'
3+
import { pipeline } from 'node:stream/promises'
44
import { CID } from 'multiformats/cid'
5+
import { base64 } from 'multiformats/bases/base64'
6+
import { identity } from 'multiformats/hashes/identity'
57
import * as DID from '@ipld/dag-ucan/did'
68
import * as dagJSON from '@ipld/dag-json'
79
import { CarWriter } from '@ipld/car'
@@ -15,6 +17,7 @@ import {
1517
filesize,
1618
filesizeMB,
1719
readProof,
20+
readProofFromBytes,
1821
uploadListResponseToString,
1922
startOfLastMonth,
2023
} from './lib.js'
@@ -253,11 +256,33 @@ export async function createSpace(name) {
253256
}
254257

255258
/**
256-
* @param {string} proofPath
259+
* @param {string} proofPathOrCid
257260
*/
258-
export async function addSpace(proofPath) {
261+
export async function addSpace(proofPathOrCid) {
259262
const client = await getClient()
260-
const delegation = await readProof(proofPath)
263+
264+
let cid
265+
try {
266+
cid = CID.parse(proofPathOrCid, base64)
267+
} catch (/** @type {any} */ err) {
268+
if (err?.message?.includes('Unexpected end of data')) {
269+
console.error(`Error: failed to read proof. The string has been truncated.`)
270+
process.exit(1)
271+
}
272+
/* otherwise, try as path */
273+
}
274+
275+
let delegation
276+
if (cid) {
277+
if (cid.multihash.code !== identity.code) {
278+
console.error(`Error: failed to read proof. Must be identity CID. Fetching of remote proof CARs not supported by this command yet`)
279+
process.exit(1)
280+
}
281+
delegation = await readProofFromBytes(cid.multihash.digest)
282+
} else {
283+
delegation = await readProof(proofPathOrCid)
284+
}
285+
261286
const space = await client.addSpace(delegation)
262287
console.log(space.did())
263288
}
@@ -339,6 +364,7 @@ Providers: ${providers || chalk.dim('none')}`)
339364
* @param {number} [opts.expiration]
340365
* @param {string} [opts.output]
341366
* @param {string} [opts.with]
367+
* @param {boolean} [opts.base64]
342368
*/
343369
export async function createDelegation(audienceDID, opts) {
344370
const client = await getClient()
@@ -367,7 +393,25 @@ export async function createDelegation(audienceDID, opts) {
367393
const { writer, out } = CarWriter.create()
368394
const dest = opts.output ? fs.createWriteStream(opts.output) : process.stdout
369395

370-
Readable.from(out).pipe(dest)
396+
pipeline(
397+
out,
398+
async function* maybeBaseEncode(src) {
399+
const chunks = []
400+
for await (const chunk of src) {
401+
if (!opts.base64) {
402+
yield chunk
403+
} else {
404+
chunks.push(chunk)
405+
}
406+
}
407+
if (!opts.base64) return
408+
const blob = new Blob(chunks)
409+
const bytes = new Uint8Array(await blob.arrayBuffer())
410+
const idCid = CID.createV1(ucanto.CAR.code, identity.digest(bytes))
411+
yield idCid.toString(base64)
412+
},
413+
dest
414+
)
371415

372416
for (const block of delegation.export()) {
373417
// @ts-expect-error

lib.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,24 +139,31 @@ export function getClient() {
139139
* @param {string} path Path to the proof file.
140140
*/
141141
export async function readProof(path) {
142+
let bytes
142143
try {
143-
await fs.promises.access(path, fs.constants.R_OK)
144+
const buff = await fs.promises.readFile(path)
145+
bytes = new Uint8Array(buff.buffer)
144146
} catch (/** @type {any} */ err) {
145147
console.error(`Error: failed to read proof: ${err.message}`)
146148
process.exit(1)
147149
}
150+
return readProofFromBytes(bytes)
151+
}
148152

153+
/**
154+
* @param {Uint8Array} bytes Path to the proof file.
155+
*/
156+
export async function readProofFromBytes(bytes) {
149157
const blocks = []
150158
try {
151-
const reader = await CarReader.fromIterable(fs.createReadStream(path))
159+
const reader = await CarReader.fromBytes(bytes)
152160
for await (const block of reader.blocks()) {
153161
blocks.push(block)
154162
}
155163
} catch (/** @type {any} */ err) {
156164
console.error(`Error: failed to parse proof: ${err.message}`)
157165
process.exit(1)
158166
}
159-
160167
try {
161168
// @ts-expect-error
162169
return importDAG(blocks)

test/bin.spec.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { UCAN, Provider } from '@web3-storage/capabilities'
1818
import * as ED25519 from '@ucanto/principal/ed25519'
1919
import { sha256, delegate } from '@ucanto/core'
2020
import * as Result from '@web3-storage/w3up-client/result'
21+
import { base64 } from 'multiformats/bases/base64'
2122

2223
const w3 = Command.create('./bin.js')
2324

@@ -349,6 +350,34 @@ export const testSpace = {
349350
assert.ok(listSome.output.includes(spaceDID))
350351
}),
351352

353+
'w3 space add `base64 proof car`': test(async (assert, context) => {
354+
const { env } = context
355+
const spaceDID = await loginAndCreateSpace(context, { env: env.alice })
356+
const whosBob = await w3.args(['whoami']).env(env.bob).join()
357+
const bobDID = SpaceDID.from(whosBob.output.trim())
358+
const res = await w3
359+
.args([
360+
'delegation',
361+
'create',
362+
bobDID,
363+
'-c',
364+
'store/*',
365+
'upload/*',
366+
'--base64'
367+
])
368+
.env(env.alice)
369+
.join()
370+
371+
const listNone = await w3.args(['space', 'ls']).env(env.bob).join()
372+
assert.ok(!listNone.output.includes(spaceDID))
373+
374+
const add = await w3.args(['space', 'add', res.output]).env(env.bob).join()
375+
assert.equal(add.output.trim(), spaceDID)
376+
377+
const listSome = await w3.args(['space', 'ls']).env(env.bob).join()
378+
assert.ok(listSome.output.includes(spaceDID))
379+
}),
380+
352381
'w3 space add invalid/path': test(async (assert, context) => {
353382
const fail = await w3
354383
.args(['space', 'add', 'djcvbii'])
@@ -784,6 +813,44 @@ export const testDelegation = {
784813
assert.equal(delegate.status.success(), true)
785814
}),
786815

816+
'w3 delegation create -c store/add -c upload/add --base64': test(
817+
async (assert, context) => {
818+
const env = context.env.alice
819+
const { bob } = Test
820+
const spaceDID = await loginAndCreateSpace(context)
821+
const res = await w3
822+
.args([
823+
'delegation',
824+
'create',
825+
bob.did(),
826+
'-c',
827+
'store/add',
828+
'-c',
829+
'upload/add',
830+
'--base64'
831+
])
832+
.env(env)
833+
.join()
834+
835+
assert.equal(res.status.success(), true)
836+
837+
const identityCid = parseLink(res.output, base64)
838+
const reader = await CarReader.fromBytes(identityCid.multihash.digest)
839+
const blocks = []
840+
for await (const block of reader.blocks()) {
841+
blocks.push(block)
842+
}
843+
844+
// @ts-expect-error
845+
const delegation = importDAG(blocks)
846+
assert.equal(delegation.audience.did(), bob.did())
847+
assert.equal(delegation.capabilities[0].can, 'store/add')
848+
assert.equal(delegation.capabilities[0].with, spaceDID)
849+
assert.equal(delegation.capabilities[1].can, 'upload/add')
850+
assert.equal(delegation.capabilities[1].with, spaceDID)
851+
}
852+
),
853+
787854
'w3 delegation ls --json': test(async (assert, context) => {
788855
const { mallory } = Test
789856

0 commit comments

Comments
 (0)