@storacha/capabilities
v1.2.0
Published
UCAN Capabilities provided by storacha.network
Downloads
462
Readme
⁂ @storacha/capabilities
Capabilities for interacting with storacha.network
About
The w3up platform by storacha.network is implemented as a set of capabilities that can be invoked using the ucanto RPC framework.
The @storacha/capabilities
package contains capability definitions, which are used by clients to create invocations and by services to validate and parse invocations and route requests to the correct capability handler.
See the capabilities spec for more information about each capability included in this package.
Install
Install the package:
npm install @storacha/capabilities
Usage
import * as Space from '@storacha/capabilities/space'
import * as Store from '@storacha/capabilities/store'
import * as Top from '@storacha/capabilities/top'
import * as Types from '@storacha/capabilities/types'
import * as Upload from '@storacha/capabilities/upload'
import * as Utils from '@storacha/capabilities/utils'
import * as Plan from '@storacha/capabilities/plan'
import * as Filecoin from '@storacha/capabilities/filecoin'
import * as Aggregator from '@storacha/capabilities/filecoin/aggregator'
import * as DealTracker from '@storacha/capabilities/filecoin/deal-tracker'
import * as Dealer from '@storacha/capabilities/filecoin/dealer'
import * as Index from '@storacha/capabilities/space/index'
// This package has a "main" entrypoint but we recommend the usage of the specific imports above
Capability types
The capability objects exported by this package are defined using ucanto's type-inference based capability parser. This results in concrete types that capture the details of each capability, allowing type-safe invocation and validation.
When inspecting the concrete types of a capability object (e.g. in your IDE), you may see something similar to the following:
const add: TheCapabilityParser<DerivedMatch<{
can: "store/add";
with: URI<"did:">;
nb: InferCaveats<{
link: typeof Store.Schema.Link;
size: Store.Schema.NumberSchema<number & Phantom<{
typeof: "integer";
}>, unknown>;
origin: Store.Schema.Schema<...>;
}>;
}, CapabilityMatch<...> | DerivedMatch<...>>>
While this is a fairly complex type signature, most of the types exist to support the mechanics of the capability parser and can generally be ignored when using the capabilities. The most interesting part as a user is the definition in the DerivedMatch
type constraint, which shows the inferred ability and the constraints upon the resource URI and the caveats. In the example above, the can
field shows that this capability's ability is "store/add"
, its resource URI (the with
field) must have the "did:"
scheme, and there are three caveats defined in the nb
field: link
, size
, and origin
, each of which have constraints on their allowed values.
Using the exported capabilities
The capability object exposes three methods via the TheCapabilityParser
interface: create
, invoke
, and delegate
.
create
The create
method returns a "materialized" capability object, which is to say, a JS object containing the can
, with
, and nb
fields needed to fully specify a UCAN capability.
You must provide an input object containing a with
resource URI that matches the constraints in the capability definition, as well as an nb
object containing any caveats you want to include. If a capability has no caveats defined, or if all the caveats are optional, you may omit the nb
field from the input.
const cap = Store.add.create({
with: 'did:key:z6MkwFPNubhwM66HNKeJYtBu1Rv9n1LZdJhbyhLFg97Qr6FG',
nb: {
link: 'bagbaieraspawtgooy5lptr7loyd3fxjsrgkamre3y6au3ga4df5bkhrxdkmq',
size: 20,
}
})
The above would result in an object similar to the following:
{
can: 'store/add',
with: 'did:key:z6MkwFPNubhwM66HNKeJYtBu1Rv9n1LZdJhbyhLFg97Qr6FG',
nb: {
link: 'bagbaieraspawtgooy5lptr7loyd3fxjsrgkamre3y6au3ga4df5bkhrxdkmq',
size: 20,
}
}
invoke
The invoke
method returns an invocation of the capability, which can be executed against a ucanto service.
Like create
, invoke
accepts with
and nb
fields, and the inputs must match the constraints in the capability definition.
Because invocations are a type of UCAN, you also need to supply some UCAN-related options. At minimum, you need to include the issuer
, which is a Signer
capable of signing a UCAN, and audience
, which identifies the recipient by DID. You can also include any of the optional fields in the interface definition below:
interface UCANOptions {
audience: Principal
lifetimeInSeconds?: number
expiration?: UCAN.UTCUnixTimestamp
notBefore?: UCAN.UTCUnixTimestamp
nonce?: UCAN.Nonce
facts?: Fact[]
proofs?: Proof[]
}
In the example below, we're generating a new Signer
to act as the issuer of the invocation using the @ucanto/principal/ed25519
package. Note that in a real application, the service would likely reject an invocation from this signer, as it does not have any delegated permissions. See the access client package for more about key management and delegation in practice.
import * as DID from '@ipld/dag-ucan/did'
import * as ed25519 from '@ucanto/principal/ed25519'
const issuer = await ed25519.generate()
const audience = DID.parse('did:web:storacha.network')
const invocation = Store.add.invoke({
issuer,
audience,
with: 'did:key:z6MkwFPNubhwM66HNKeJYtBu1Rv9n1LZdJhbyhLFg97Qr6FG',
nb: {
link: 'bagbaieraspawtgooy5lptr7loyd3fxjsrgkamre3y6au3ga4df5bkhrxdkmq',
size: 20,
}
})
Note that creating an invocation does not automatically execute it. To send the invocation to a service, you need a ucanto ConnectionView
configured to access the service, which you can pass into the execute
method on the invocation object.
const result = await invocation.execute(serviceConnection)
delegate
The delegate
method allows you to create a ucanto Delegation
, which allows another principal to invoke the capability.
delegate
accepts the same input as invoke
, however the nb
field is optional. If nb
is present, the values provided will act as constraints on the invocations that can be made using the delegation. For example, creating a store/add
delegation with the size
caveat set to 1048576
would limit invocations made using the delegation to uploads of no more than 1MiB.
import * as DID from '@ipld/dag-ucan/did'
import * as ed25519 from '@ucanto/principal/ed25519'
const issuer = await ed25519.generate()
const audience = DID.parse('did:web:storacha.network')
const delegation = await Store.add.delegate({
issuer,
audience,
with: 'did:key:z6MkwFPNubhwM66HNKeJYtBu1Rv9n1LZdJhbyhLFg97Qr6FG',
})