From 5c02c6dc64f6b1d19ab9af5cb53aded9b541cf3d Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Fri, 26 Jan 2024 00:19:26 -0800 Subject: [PATCH 1/6] feat: provider spec revamp --- w3-provider.md | 557 ++++++++++++++++++++----------------------------- 1 file changed, 223 insertions(+), 334 deletions(-) diff --git a/w3-provider.md b/w3-provider.md index 5d11ff4..fc30cfb 100644 --- a/w3-provider.md +++ b/w3-provider.md @@ -1,7 +1,6 @@ # Provider Protocol -![status:wip](https://img.shields.io/badge/status-wip-orange.svg?style=flat-square) -[![hackmd-github-sync-badge](https://hackmd.io/Zb7gjpLsQn2w3a3JUvnFcw/badge)](https://hackmd.io/Zb7gjpLsQn2w3a3JUvnFcw) +![stable](https://img.shields.io/badge/status-stable-brightgreen.svg?style=flat-square) ## Editors @@ -13,463 +12,353 @@ # Abstract -Thinking about users in web2 terms introduces unfortunate limitations and seems to be a poor fit for User Controlled Authorization Network ([UCAN][]). +The W3 protocol governs user interactions within self-certified Public Key Infrastructure (PKI)-based namespaces. Access control to these namespaces, for simplicity referred to as spaces, is managed through delegated capabilities in [UCAN] format. -## Capabilities +Users can create spaces and delegate capabilities on it to various user agents without anyone's permission or intervention. -In web2, a user _(which could be an individual or an organization)_ directly correlates to a (name) space _(usually behind a walled garden)_ they're given access to. In this model, a user authenticates using credentials or a server issued (secret) authorization token to gain an access to set of capabilities with-in a bound (name) space. +Here we introduce notion of the capability provider, or [provider] for short. [Provider] is protocol compliant capability implementation that performs a task for the invoked capability. + +## Language + +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119](https://datatracker.ietf.org/doc/html/rfc2119). + +# Introduction + +In web2, a user _(which could be an individual or an organization)_ directly correlates to a (name) space _(usually behind a walled garden)_ they're given access to. In this model, a user authenticates using credentials or a server issued (secret) authorization token to gain access to a set of capabilities with-in a bound (name) space. > If there is a notion of sharing capabilities it's usually limited & very domain specific. Sharing across applications is extremely rare and usually involves large cross-organizational efforts. -With a [UCAN][] based authorization model, things are different. User creates a (name)space _(addressed by [did:key][] URI)_ locally and can delegate set of capabilities to an agent _(also addressed by [did:key][] URI)_ that acts on their behalf. This allows an agent to invoke any of the delegated capabilities or to (re)delegate them to _another_ user, so they could invoke them. This model enables a wide range of possibilities that are difficult to impossible in the web2 model. Capabilities are the protocol, therefore sharing and interop is built into every layer of the stack. Inevitably this breaks 1 to 1 correlation between users and spaces. Instead each user may have access to a multitude of spaces (that they either own or were delegated capabilities to) and a multitude of users may have access to the same (shared) space. +With a [UCAN][] based authorization model, things are different. User creates a (name)space _(addressed by [did:key][] URI)_ locally and can delegate set of capabilities to an agent _(also addressed by [did:key][] URI)_ that acts on user behalf. Agent can invoke any of the delegated capabilities or (re)delegate them. This model enables a wide range of possibilities that are difficult, to impossible, in the web2 model. Capabilities form the protocol, making sharing and interop implicit in every layer of the stack. Inevitably this breaks 1 to 1 correlation between users and spaces. Each user may have access to a multitude of spaces _(that they either own or were delegated access to)_ and multiple users may have access to the same (shared) space. -> The implications of this are tremendous, we are no longer building apps behind walled gardens, but rather tap into the rich network of information with self describing protocols +> The implications of this are tremendous, we are no longer building apps behind walled gardens, but rather tap into the rich network of information with self describing protocols. -## Providers +## Concepts -As we have above established, users create, own, and manage access to their space through the capabilities that can be delegated. However, owning a `store/add` capability to some `did:key:zAlice` space does not imply it can be invoked, something needs to provide that capability. A user can contract a "storage provider" which they can add it to their (or anyone else's) space, in turn making it possible for a anyone with `store/add` capability to a space with a store provider to store data. +### Roles -Providers are services which user can add to a space so they can handle provided capabilities when they are invoked. +There are several distinct roles that [principal]s may assume in described specification: -## Funding +| Name | Description | +| ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| Principal | The general class of entities that interact with a UCAN. Listed in the `iss` or `aud` field | +| Account | [Principal] identified by memorable identifier like [`did:mailto`]. | +| Agent | [Principal] identified by [`did:key`] identifier, representing a user in some application installation | +| Issuer | Principal sharing access. It is the signer of the [UCAN]. Listed in the `iss` field | +| Audience | Principal access is shared with. Listed in the `aud` field | -Direct correlation between a user and a space in the Web 2 model leads to a system in which users fund their space (by money or their privacy). +### Provider -Decoupling users from spaces enables all kinds of funding strategies. User can hire a storage provider and add it to their space. User can also hire a provider and add it to some common goods space they would like to financially support. Just like every capability can be shared, just the same, every space can be crowd funded, because space is decoupled from the capability provider(s). +Provider is service implementing protocol complaint capability handlers. When [space] is provisioned with a provider, capabilities invoked on that space are routed to a corresponding capability provider. -## Language +As we have above established in the introduction, users create spaces and manage access through delegated capabilities. However, when capability `store/add` of `did:key:zAlice` [space] is invoked, some program needs to perform a task corresponding to that capability. -The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119). +Programs that perform can perform such tasks in protocol compliant manner are offered by capability providers, or providers for short, through a [subscription]. Users can get a subscription from service by agreeing to their terms of service. [Subscription] can be used to provision [space] with capabilities, offered under the terms of subscription. -# Space +Capability invoked on the space is routed to the capability provider that space is provisioned with. If [space] is not provisioned with invoked capability, invocation SHOULD fail. -## Representation +### Subscription -Any valid [did:key][] identifier SHOULD represent a valid space that has no capability providers, therefore attempt to store data into such space (or invoking any other capability) SHOULD fail. +Subscription is an agreement to terms of service. Broadly speaking [customer] agree to pay metered service fee for the provided service and in return [provider] service agrees to provide set of protocol compliant capabilities. -## Ownership +Every [UCAN] invocation gets a signed receipt from the [provider] and can be used by the [customer] to hold service accountable if they violate terms of service. -Space is a resource that MUST be addressed by the [did:key][] URI. It is owned by the (corresponding) private key holder. +Subscription MAY be suspended or terminated by the [provider] if [customer] violates terms of service. [Customer] MAY terminate subscription when they no longer wish to receive the service. -Any [UCAN][] capability for the space resource MUST be issued by the owner _([UCAN][] `iss` MUST be equal to `with` of the capability)_ or its delegate _([UCAN][] MUST have a proof chain leading to delegation from the owner)_. +When subscription is established, [provider] MUST delegate `subscription/*` capabilities to the subscribed [customer]. Capabilities under `subscription/*` namespace MUST follow described protocol, allowing [customer] to manage their subscription and provision spaces with capabilities offered. -This implies that [UCAN][] invocations on a space resource CAN be validated by verifying: +### Provision -1. Signatures, time bounds and principal alignment of the delegation chain. -2. Root `issuer` is the same DID as a resource (`with` field) of the invoked capability. +[Customer] with an active [subscription] MAY provision [space] with set of capabilities offered by the [subscription]. [Customer] MAY set limits on the space provision to manage costs, which makes provisioning [space] controlled by third party financially viable. -## Creation +[Subscription] can be conceptualized as a leasing agreement between [provider] and [customer], in which case [provision] can be viewed as subleasing agreement between [customer] and a [space]. -User MAY create a new space by generating a [keypair][public key cryptography] and deriving a valid [did:key][] identifier from it. +### Customer -> It is RECOMMENDED that user facing applications create a _space_ for a new user with a [ED25519][] keypair & delegate capabilities to it to a local agent whose DID is derived from another [non-extractable keypair](https://developer.mozilla.org/en-US/docs/Web/API/CryptoKey#cryptokey.extractable). -> -> This grants an agent access to a space without reusing its key or a risk of it been compromised. +Customer is a user [account] that has a [subscription] with a service [provider]. -```json -// illustration of the space to agent delegation -{ - "iss": "did:key:zSpace", - "aud": "did:key:zAgent", - "exp": null, // delegation never expires - // allows did:key:zAgent to do anything with did:key:zSpace - "att": [ - { - "with": "did:key:zSpace", - "can": "*" - } - ] -} -``` +ℹ️ Customer does not need to own or even have access to a [space], e.g. employer MAY setup an [account], setup [subscription] and use it to [provision] employee [space]s. -## Setup +### Space -As we have established, space creator is an owner and has a full authority to delegate whichever capabilities to others. However, unless a space has an active provider of the capabilities, no invocation of them could succeed. +Namespace, or space for short, is an owned resource that can be shared. It corresponds to the asymmetric keypair and is identified by the [`did:key`] URI. Space can be provisioned with capabilities provided by the [provider] through a [subscription]. -To make capabilities invocable, one needs to obtain a provider and add it to the desired space. For example, a user could get the "free" provider from web3.storage, which provides `store/*` and `upload/*` capabilities allowing them to store up to 5GiB of data. +# Capabilities -```mermaid -flowchart TB - Space((did:key:zAliceSpace)) - W3{{did:web:free.web3.storage}} - NFT{{did:web:nft.storage}} +Here we describe protocol for arranging subscriptions and using it to provision [space]s. - Name([name/*]) - Upload([upload/*]) - Store([store/*]) +## Provider Capabilities - style W3 fill:grey,color:black,stroke:grey - style NFT fill:grey,color:black,stroke:grey +### Provider Add +User MAY invoke `provider/add` capability on the [account] subject (`with` field) to start a subscription with [provider] (`aud` field). - Space-->Store - Store-->W3 - Store-->NFT - Space-->Upload - Upload-->W3 - Upload-->NFT - Space-->Name - Name-->W3 -``` +#### Subscription Provider -# Provider protocol +The audience of the invocation (`aud` field) MUST be a [provider] [DID] of the desired [subscription]. -The "free" provider setup describes a more general framework for unlocking various capabilities. +#### Subscription Customer -It is RECOMMENDED to follow the outlined `provider/*` protocol even if some domain specific details may vary. +The subject of the invocation (`with` field) MUST be a [customer] [DID] of the desired [subscription]. -## `provider/get` +#### Subscription Plan -A user MAY get a "free" storage provider (from web3.storage) by invoking a self-issued `provider/get` capability. +Provider MAY offer multiple subscription plans. Invocation MAY specify desired plan using `nb.product` field. -> Free provider requires that resource of invocation MUST be an [`did:mailto`] principal and that `consumer` is provided in order to uphold 1 free provider per account limitation. +#### Provider Add Example ```json { - "iss": "did:mailto:web.mail:alice", + "v": "0.9.1", + "iss": "did:key:z6Mkk...xALi", "aud": "did:web:web3.storage", "att": [ { - "can": "provider/get", "with": "did:mailto:web.mail:alice", + "can": "provider/add", "nb": { - // did of the provider, - "provider": "did:web:free.web3.storage", - // did of the consumer space - "consumer": "did:key:zSpace" + "product": "did:web:lite.web3.storage" } } ], - // proof that agent is authorized to represent account - "prf": [ - { - "iss": "did:web:web3.storage", - "aud": "did:mailto:web.mail:alice", - "att": [ - { - "can": "./update", - "with": "did:web:web3.storage", - "nb": { - "key": "did:key:zAgent" - } - } - ] + "prf": [{ "/": "bafy...prf1" }], + "exp": 1685602800, + "s": { + "/": { + "bytes": "7aEDQJbJqmyMxTxcK05XQKWfvxG+Tv+LWCJeE18RSMnciCZ/RQ21U75LA0uFSvIjdqnF5RaauZTE8mh2ZYMBBejdJQ4" } - ] + } } ``` -### get `with` - -Providers MAY impose certain requirements that resource (`with`) must meet. +#### Provider Add IPLD Schema -> Free provider (from web3.storage) requires that resource MUST be a [`did:mailto`] identifier. Lite and Expert providers (from web3.storage) additionally require that the invocation resource have a payment provider (for billing purposes). - -### get `nb.provider` - -Capability MUST have `nb.provider` field specifying a DID of the provider requested. - -### get `nb.consumer` - -Providers can be requested for multiple consumers (spaces) by omitting this field or to a specific consumer by setting this field to a DID. +```ipldsch +type ProviderAddCapability struct { + with AccountDID + nb ProviderAdd +} -```json -{ - "can": "provider/get", - "with": "did:mailto:web.mail:alice", - "nb": { - // did of the provider, - "provider": "did:web:lite.web3.storage" - // without the consumer field the request is for multiple consumers - } +type ProviderAdd struct { + product optional String } ``` -> ⚠️ Some providers (e.g. Free provider from web3.storage) MAY deny request when `nb.consumer` is not defined, because they limit number of providers issued per user account. +#### Subscription State -### get `nb...` +[Provider] MUST return a variant of the `SubscriptionState` on a successful invocation of the `provider/add`. -Some providers MAY specify additional `nb` fields. +#### Subscription State IPLD Schema -## `consumer/add` +```ipldsch +type SubscriptionState union { + PendingSubscription "pending" + ActiveSubscription "active" +} representation keyed +``` -An agent MUST be delegated `consumer/add` capability, on successful [`provider/get`] invocation. +#### Pending Subscription -> Please note that provider MAY also delegate `consumer/add` capability for no reason at all e.g. as free giveaway campaign. +[Provider] MUST return `PendingSubscription` variant when it requires an out-of-bound interaction to start a subscription. For example provider MAY require payment setup for the billing. -```json -{ - "iss": "did:web:web3.storage", - "aud": "did:key:zAgent", - "att": [ - { - "can": "consumer/add", - "with": "did:web:web3.storage:plan:free", - "nb": { - // link to a "provider/get" invocation - "request": { "/": "bafy...get" }, - // did of the consumer space - "consumer": "did:key:zSpace" - } - } - ], - "prf": [ - { - "iss": "did:web:web3.storage:plan:free", - "aud": "did:web:web3.storage", - "att": [ - { - "can": "consumer/add", - "with": "did:web:web3.storage:plan:free" - } - ] - } - ] +##### Pending Subscription Schema + +```ipldsch +type PendingSubscription struct { + provider ProviderDID + order String + url URLString } -``` -### add `aud` +type URLString = string +type ProviderDID = DID +``` -Capability MUST be delegated back to the `iss` of the [`provider/get`] request. +##### Pending Subscription URL -> This allows authorized agent to delegate `provider/get` capability to an agent or another user, which can then complete the loop using [`consumer/add`]. If capability was delegated back to `with` identifier instead, only account or delegate (with `consumer/add` capability) would be able to complete the loop. +The `url` field MUST be set to the location user is expected to navigate, to complete subscription process. -### add `with` +##### Pending Subscription Provider -Capability resource MUST be a provider DID. It MUST be the same as [`nb.provider`](#get-nbprovider) of the corresponding [`provider/get`] invocation. +The `provider` field MUST be set to the [DID] of the [subscription] [provider]. -### add `nb.consumer` +#### Active Subscription -The `nb.consumer` MUST be set to the same DID as [`nb.consumer`](#get-nbconsumer) of the [`provider/get`] request. +[Provider] MUST return `ActiveSubscription` variant when out-of-bound interaction is not required. For example provider may offer a free plan to any [account]. Provider could return `ActiveSubscription` if it already has billing info for the [account]. -> ⚠️ Omitting `nb.consumer` allows delegate to add arbitrary number of consumers to the provider +##### Active Subscription Schema -### add `nb.request` +```ipldsch +type ActiveSubscription struct { + provider ProviderDID + product ProductDID + order String + proof Link +} +``` -Issuers MUST set the `nb.request` field to the corresponding link (CID) of the [`provider/get`] invocation. +##### Active Subscription Provider -> This also represents a signed proof that user agreed to the terms and conditions of the service. +The `provider` field MUST be set to the [DID] of the [subscription] [provider]. -### add `nb...` +##### Active Subscription Plan -Providers MAY impose various other constraints using `nb` fields of the `consumer/add` capability. Usually they would mirror [`nb`](#get-nb) fields of the corresponding [`provider/get`] request. +The `product` field MUST be set to the chosen subscription plan. -### `consumer/add` invocation +##### Subscription Proof -Invoking delegated [`consumer/add`] capability adds a consumer (space) to the provider. It also automatically adds the provider to the consumer space, making provided capabilities invocable by authorized agents. +The `proof` field MUST be a link to a valid delegation for `subscription/*` capabilities. The audience (`aud` field) of the `proof` MUST be set to the [customer] of the subscription. Subject (`with` field) of the `proof` MUST be set to the [provider] of the subscription. The `nb.customer` field of the `proof` MUST be set to the [customer] of the subscription. The `nb.order` field of the `proof` MUST be set to the unique identifier of the subscription. -> Please note that while providers may add consumers without their consent, that will not affect consumers in any way. Unless a provider is used it has no effect on space. Consumer is also not who gets billed for the service it is an account that submitted a request, which is to say that unsolicited providers are sponsored by a third party. +###### Subscription Proof Example ```json { - "iss": "did:mailto:web.mail:alice", - "aud:" "did:web:web3.storage", + "v": "0.9.1", + "iss": "did:web:web3.storage", + "aud": "did:mailto:web.mail:alice", "att": [ { - "with": "did:web:web3.storage:plan:free", - "can": "consumer/add", + "can": "subscription/*", + "with": "did:web:web3.storage", "nb": { - // link to a "provider/get" invocation - "request": { "/": "bafy...get" }, - "consumer": "did:key:zSpace" + "customer": "did:mailto:web.mail:alice", + "order": "bafy...prf1" } } ], - "prf": [ - // proof that agent is authorized to represent account - { - "iss": "did:web:web3.storage", - "aud": "did:mailto:web.mail:alice", - "att": [{ - "can": "./update", - "with": "did:web:web3.storage", - "nb": { - "key": "did:key:zAgent" - } - }] - }, - { - "iss": "did:web:web3.storage", - "aud": "did:mailto:web.mail:alice", - "att": [ - { - "can": "consumer/add", - "with": "did:web:web3.storage:plan:free", - "nb": { - // link to "provider/signup" invocation - "request": { "/": "bafy...get" }, - "consumer": "did:key:zSpace" - } - } - ], - "prf": [ - { - "iss": "did:web:web3.storage:plan:free", - "aud": "did:web:web3.storage", - "att": [ - { - "can": "consumer/add", - "with": "did:web:free.web3.storage" - } - ] - } - ] + "prf": [], + "exp": 1685602800, + "s": { + "/": { + "bytes": "7aEDQ..dJQ4" } - ] + } } ``` -## `provider/publish` +### Provider List + +User MAY invoke `provider/list` capability on the [account] subject (`with` field) to list all subscriptions it has with a [provider] (`aud` field). -In the future we plan to define a set of `provider` capabilities that will allow an author to specify the capabilities it provides, terms of service and various other details. +#### Provider List Schema -In the meantime, publishing providers is not supported. However, existing providers do impose specific terms and limitations. For example, the following terms are imposed by `did:web:web3.storage:plan:free` provider: +```ipldsch +type ProviderListCapability struct { + with AccountDID + nb ProviderList +} -1. Provided `store/*` capabilities are limited to 5GiB of storage. -2. Agent MUST specify `nb.consumer` in [`provider/get`] invocation to enforce single consumer space per user limitation. -3. Invocation MUST be issued by a [`did:mailto`] identifier +type struct ProviderList struct {} +``` -## `provider/add` +### Provider Remove -In a typical flow, a user requests [`provider/get`](#providerget) to get [`consumer/add`](#consumeradd) capability delegated, which it then invokes to add a desired [`nb.consumer`](#add-nbconsumer) (space). +User MAY invoke `provider/remove` capability on the [account] subject (`with` field) to cancel a subscription with [provider] (`aud` field). -```mermaid -sequenceDiagram - participant Agent as 💻

did:key:zAgent #32; - participant Provider as 🤖

did:web:free.web3.storage #32; - participant W3 as 🌐

did:web:web3.storage #32; +#### Provider Remove Schema +```ipldsch +type ProviderRemoveCapability struct { + with AccountDID + nb ProviderRemove +} - Agent ->> Provider: provider/get - activate Provider - Provider->>Agent: consumer/add - deactivate Provider - Agent->>W3: consumer/add +type ProviderRemove struct { + order optional String +} ``` -A more simplified flow exists for cases when provider is needed for a specific (space) consumer. In those cases a `provider/add` capability can be invoked, which is equivalent of [`provider/get`](#providerget), except [`nb.consumer`](#get-nbconsumer) MUST be a concrete DID. On successful invocation, the provider takes care of invoking [`consumer/add`] instead of delegating it back to an agent, which removes the need for an extra roundtrip. +## Subscription Capabilities -```mermaid -sequenceDiagram - participant Agent as 💻

did:key:zAgent #32; - participant Provider as 🤖

did:web:free.web3.storage #32; - participant W3 as 🌐

did:web:web3.storage #32; +### Subscription Capabilities Schema - Agent ->> Provider: provider/add - Provider ->> W3: consumer/add +```ipldsch +type Subscription union { + SubscriptionAddCapability "subscription/add" + SubscriptionRemoveCapability "subscription/remove" + SubscriptionListCapability "subscription/list" +} representation inline { + discriminantKey "can" +} ``` -## Payment protocol +### Subscription Add -### Add payment provider +User MAY invoke `subscription/add` capability to provision desired [space] with the capabilities provided through subscription. -A user agent MAY add a payment provider using credit card information. +#### Subscription Add Schema -```json -{ - "iss": "did:mailto:web.mail:alice", - "aud": "did:web:web3.storage", - "att": [ - { - "can": "provider/add", - "with": "did:mailto:web.mail:alice", - "nb": { - "provider": "did:web:web3.storage:pay", - "consumer": "did:mailto:web.mail:alice", - /* data is the linked CBOR block that has - been encrypted with a symmetric key - inside the `cypher`. We inline here for - simplicity - */ - "credential": { - "type": "card", - "card": { - "number": "4242424242424242", - "exp_month": 9, - "exp_year": 2023, - "cvc": "314" - } - }, - /* symmetric key encrypted with a public - key of the `aud` so only private key - holder is able to decrypt */ - "cypher": "....." - } - } - ], - // proof that agent is authorized to represent account - "prf": [ - { - "iss": "did:web:web3.storage", - "aud": "did:mailto:web.mail:alice", - "att": [ - { - "can": "./update", - "with": "did:web:web3.storage", - "nb": { - "key": "did:key:zAgent" - } - } - ] - } - ] +```ipldsch +type SubscriptionAddCapability struct { + with ProviderDID + nb SubscriptionAdd } + +type SubscriptionAdd struct { + customer AccountDID + order String + consumer SpaceDID + budget Budget +} + +type Budget { String: Int } ``` -On success, the payment provider is added to the consumer, allowing an owner or a delegate to invoke and delegate `payment/*` capabilities. +#### Subscription Consumer -> A service MAY instead, or in addition to, create an out of bound payment method setup flow to avoid capturing sensitive data like card info. +The `nb.consumer` MUST be set to the [space] DID to be provisioned. -### Get payment provider +#### Subscription Budget -Just like with other providers user can invoke [`provider/get`] capability which may incur out-of-band interaction e.g. user may be directed to type in credit card information before response is completed. +The `nb.budget` MUST be set to the map where keys are names of the capacities and values are the allowed utilization per billing cycle. -Also note that [`provider/get`](#providerget) / [`provider/add`](#provideradd) capabilities let user start a provider acquisition process, however services MAY also define alternative ways to issue `consumer/add` capabilities the users. +> ℹ️ Adding [space] to the subscription twice only causes previous budget to be merged with the new one. -### Add paid provider +### Subscription Remove -When a space has a payment provider, its owner or delegate can invoke [`provider/add`](#provideradd) and [`provider/get`](#providerget) capabilities to add providers that require payments. +User MAY invoke `subscription/remove` capability to remove [space] from this subscription. -> Example below illustrates Alice adding a "Lite plan" to Bob's space on her expense. +#### Subscription Remove Schema -```json -{ - "iss": "did:mailto:web.mail:alice", - "aud": "did:web:web3.storage", - "att": [ - { - "can": "provider/add", - "with": "did:mailto:web.mail:alice", - "nb": { - // 30GiB storage plan - "provider": "did:web:lite.web3.storage", - // Space to add storage provider to - "consumer": "did:key:zBob" - } - } - ], - // proof that agent is authorized to represent account - "prf": [ - { - "iss": "did:web:web3.storage", - "aud": "did:mailto:web.mail:alice", - "att": [ - { - "can": "./update", - "with": "did:web:web3.storage", - "nb": { - "key": "did:key:zAgent" - } - } - ] - } - ] +```ipldsch +type SubscriptionRemoveCapability struct { + with ProviderDID + nb SubscriptionRemove +} + +type SubscriptionRemove struct { + customer AccountDID + order String + consumer SpaceDID +} +``` + +### Subscription List + +Agent MAY invoke `subscription/list` capability to list provisions under this subscription. + +#### Subscription List Schema + +```ipldsch +type SubscriptionListCapability struct { + with ProviderDID + nb SubscriptionList +} + +type SubscriptionList struct { + customer AccountDID + order String } ``` [did:key]: https://w3c-ccg.github.io/did-method-key/ -[ucan]: https://github.com/ucan-wg/spec/#57-revocation -[public key cryptography]: https://en.wikipedia.org/wiki/Public-key_cryptography -[`provider/get`]: #providerget -[`consumer/add`]: #consumeradd -[ed25519]: https://en.wikipedia.org/wiki/EdDSA#Ed25519 +[UCAN]:https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md +[principal]:https://github.com/ucan-wg/spec/blob/ +[provider]:#provider +[`did:mailto`]:./did-mailto.md +[`did:key`]:https://w3c-ccg.github.io/did-method-key/ +[customer]:#customer +[account]:#account +[space]:#space +[subscription]:#subscription +[provision]:#provision +[DID]:https://www.w3.org/TR/did-core/ From 6ed251649dbb07747f28c4defde54fce851e3ac8 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 31 Jan 2024 08:58:35 -0800 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Travis Vachon --- w3-provider.md | 74 ++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/w3-provider.md b/w3-provider.md index fc30cfb..dc2b868 100644 --- a/w3-provider.md +++ b/w3-provider.md @@ -28,7 +28,7 @@ In web2, a user _(which could be an individual or an organization)_ directly cor > If there is a notion of sharing capabilities it's usually limited & very domain specific. Sharing across applications is extremely rare and usually involves large cross-organizational efforts. -With a [UCAN][] based authorization model, things are different. User creates a (name)space _(addressed by [did:key][] URI)_ locally and can delegate set of capabilities to an agent _(also addressed by [did:key][] URI)_ that acts on user behalf. Agent can invoke any of the delegated capabilities or (re)delegate them. This model enables a wide range of possibilities that are difficult, to impossible, in the web2 model. Capabilities form the protocol, making sharing and interop implicit in every layer of the stack. Inevitably this breaks 1 to 1 correlation between users and spaces. Each user may have access to a multitude of spaces _(that they either own or were delegated access to)_ and multiple users may have access to the same (shared) space. +With a [UCAN][] based authorization model, things are different. A user creates a (name)space _(addressed by [did:key][] URI)_ locally and can delegate a set of capabilities to an agent _(also addressed by [did:key][] URI)_ that acts on the user's behalf. The user's software "agent" can invoke any of the delegated capabilities or (re)delegate them. This model enables a wide range of possibilities that are difficult or impossible in the web2 model. Capabilities can be assembled into a protocol, making sharing and interop implicit in every layer of the stack. Inevitably this breaks the 1 to 1 correlation between users and spaces. Each user may have access to a multitude of spaces _(that they either own or were delegated access to)_ and multiple users may have access to the same (shared) space. > The implications of this are tremendous, we are no longer building apps behind walled gardens, but rather tap into the rich network of information with self describing protocols. @@ -36,73 +36,73 @@ With a [UCAN][] based authorization model, things are different. User creates a ### Roles -There are several distinct roles that [principal]s may assume in described specification: +There are several distinct roles that [principal]s may assume in this specification: | Name | Description | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| Principal | The general class of entities that interact with a UCAN. Listed in the `iss` or `aud` field | -| Account | [Principal] identified by memorable identifier like [`did:mailto`]. | -| Agent | [Principal] identified by [`did:key`] identifier, representing a user in some application installation | -| Issuer | Principal sharing access. It is the signer of the [UCAN]. Listed in the `iss` field | -| Audience | Principal access is shared with. Listed in the `aud` field | +| Principal | The general class of entities that interact with a UCAN. Identified by a DID that can be used in the `iss` or `aud` field of a UCAN. | +| Account | A [Principal] identified by memorable identifier like [`did:mailto`]. | +| Agent | A [Principal] identified by [`did:key`] identifier, representing a user in an application. | +| Issuer | A [principal] delegating capabilities to another [principal]. It is the signer of the [UCAN]. Specified in the `iss` field of a UCAN. | +| Audience | Principal access is shared with. Specified in the `aud` field of a UCAN. | ### Provider -Provider is service implementing protocol complaint capability handlers. When [space] is provisioned with a provider, capabilities invoked on that space are routed to a corresponding capability provider. +A provider is service implementing protocol complaint capability handlers. When a [space] is provisioned with a provider, capabilities invoked on that space are routed to a corresponding capability provider. -As we have above established in the introduction, users create spaces and manage access through delegated capabilities. However, when capability `store/add` of `did:key:zAlice` [space] is invoked, some program needs to perform a task corresponding to that capability. +As established in the introduction, users create spaces and manage access through delegated capabilities. However, when a capability (say, `store/add`) is invoked on a [space] (say, `did:key:zAlicesSpace`) is invoked, some program needs to perform a task corresponding to that capability. -Programs that perform can perform such tasks in protocol compliant manner are offered by capability providers, or providers for short, through a [subscription]. Users can get a subscription from service by agreeing to their terms of service. [Subscription] can be used to provision [space] with capabilities, offered under the terms of subscription. +Programs that perform can perform such tasks in a protocol compliant manner are offered by capability providers, or providers for short, through a [subscription]. Users can get a subscription from a service by agreeing to their terms of service. A [subscription] can be used to provision a [space] with capabilities, contingent on the terms of subscription. -Capability invoked on the space is routed to the capability provider that space is provisioned with. If [space] is not provisioned with invoked capability, invocation SHOULD fail. +A capability invoked on the space is routed to the capability provider that space is provisioned with. If a [space] is not provisioned with the invoked capability, invocation SHOULD fail. ### Subscription -Subscription is an agreement to terms of service. Broadly speaking [customer] agree to pay metered service fee for the provided service and in return [provider] service agrees to provide set of protocol compliant capabilities. +A subscription is an agreement to some terms of service. Broadly speaking a [customer] agrees to pay a metered service fee for the provided service and in return the [provider] service agrees to provide a set of protocol compliant capabilities. Every [UCAN] invocation gets a signed receipt from the [provider] and can be used by the [customer] to hold service accountable if they violate terms of service. -Subscription MAY be suspended or terminated by the [provider] if [customer] violates terms of service. [Customer] MAY terminate subscription when they no longer wish to receive the service. +A subscription MAY be suspended or terminated by the [provider] if the [customer] violates the terms of service. A [customer] MAY terminate a subscription when they no longer wish to receive the service. -When subscription is established, [provider] MUST delegate `subscription/*` capabilities to the subscribed [customer]. Capabilities under `subscription/*` namespace MUST follow described protocol, allowing [customer] to manage their subscription and provision spaces with capabilities offered. +When a subscription is established, the [provider] MUST delegate `subscription/*` capabilities to the subscribed [customer]. Capabilities under `subscription/*` namespace MUST follow the described protocol, allowing the [customer] to manage their subscription and provision spaces with capabilities offered. ### Provision -[Customer] with an active [subscription] MAY provision [space] with set of capabilities offered by the [subscription]. [Customer] MAY set limits on the space provision to manage costs, which makes provisioning [space] controlled by third party financially viable. +A [customer] with an active [subscription] MAY provision a [space] with set of capabilities offered by the [subscription]. A [customer] MAY set limits on the space provision to manage costs, which makes provisioning a [space] controlled by third party financially viable. -[Subscription] can be conceptualized as a leasing agreement between [provider] and [customer], in which case [provision] can be viewed as subleasing agreement between [customer] and a [space]. +A [subscription] can be conceptualized as a leasing agreement between a [provider] and a [customer], in which case a [provision] can be viewed as a subleasing agreement between a [customer] and a [space]. ### Customer -Customer is a user [account] that has a [subscription] with a service [provider]. +A customer is a user [account] that has a [subscription] with a service [provider]. -ℹ️ Customer does not need to own or even have access to a [space], e.g. employer MAY setup an [account], setup [subscription] and use it to [provision] employee [space]s. +ℹ️ A customer does not need to own or even have access to a [space], e.g. an employer MAY setup an [account], setup a [subscription] and use it to [provision] employee [space]s. ### Space -Namespace, or space for short, is an owned resource that can be shared. It corresponds to the asymmetric keypair and is identified by the [`did:key`] URI. Space can be provisioned with capabilities provided by the [provider] through a [subscription]. +A namespace, often referred as a "space", is an owned resource that can be shared. It corresponds to a unique asymmetric cryptographic keypair and is identified by a [`did:key`] URI. A space can be provisioned with capabilities provided by a [provider] through a [subscription]. # Capabilities -Here we describe protocol for arranging subscriptions and using it to provision [space]s. +Here we describe a protocol for arranging subscriptions and using them to provision [space]s. ## Provider Capabilities ### Provider Add -User MAY invoke `provider/add` capability on the [account] subject (`with` field) to start a subscription with [provider] (`aud` field). +A user MAY invoke the `provider/add` capability on the [account] subject (`with` field) to start a subscription with a [provider] (`aud` field). #### Subscription Provider -The audience of the invocation (`aud` field) MUST be a [provider] [DID] of the desired [subscription]. +The audience of the invocation (`aud` field) MUST be the [provider] [DID] of the desired [subscription]. #### Subscription Customer -The subject of the invocation (`with` field) MUST be a [customer] [DID] of the desired [subscription]. +The subject of the invocation (`with` field) MUST be the [customer] [DID] of the desired [subscription]. #### Subscription Plan -Provider MAY offer multiple subscription plans. Invocation MAY specify desired plan using `nb.product` field. +A provider MAY offer multiple subscription plans. An invocation MAY specify the user's desired plan using `nb.product` field. #### Provider Add Example @@ -145,7 +145,7 @@ type ProviderAdd struct { #### Subscription State -[Provider] MUST return a variant of the `SubscriptionState` on a successful invocation of the `provider/add`. +A [Provider] MUST return a variant of the `SubscriptionState` on a successful invocation of the `provider/add`. #### Subscription State IPLD Schema @@ -158,7 +158,7 @@ type SubscriptionState union { #### Pending Subscription -[Provider] MUST return `PendingSubscription` variant when it requires an out-of-bound interaction to start a subscription. For example provider MAY require payment setup for the billing. +A [Provider] MUST return a `PendingSubscription` variant when it requires an out-of-bound interaction to start a subscription. For example, a provider MAY require payment setup for the billing. ##### Pending Subscription Schema @@ -175,7 +175,7 @@ type ProviderDID = DID ##### Pending Subscription URL -The `url` field MUST be set to the location user is expected to navigate, to complete subscription process. +The `url` field MUST be set to the location to which the user is expected to navigate to complete subscription process. ##### Pending Subscription Provider @@ -183,14 +183,14 @@ The `provider` field MUST be set to the [DID] of the [subscription] [provider]. #### Active Subscription -[Provider] MUST return `ActiveSubscription` variant when out-of-bound interaction is not required. For example provider may offer a free plan to any [account]. Provider could return `ActiveSubscription` if it already has billing info for the [account]. +A [Provider] MUST return an `ActiveSubscription` variant when out-of-bound interaction is not required. For example, a provider may offer a free plan to any [account]. The provider could return `ActiveSubscription` if it already has billing info for the [account]. ##### Active Subscription Schema ```ipldsch type ActiveSubscription struct { provider ProviderDID - product ProductDID + product URL order String proof Link } @@ -206,7 +206,9 @@ The `product` field MUST be set to the chosen subscription plan. ##### Subscription Proof -The `proof` field MUST be a link to a valid delegation for `subscription/*` capabilities. The audience (`aud` field) of the `proof` MUST be set to the [customer] of the subscription. Subject (`with` field) of the `proof` MUST be set to the [provider] of the subscription. The `nb.customer` field of the `proof` MUST be set to the [customer] of the subscription. The `nb.order` field of the `proof` MUST be set to the unique identifier of the subscription. +The `proof` field of the `provider/add` invocation MUST be a link to a valid delegation of `subscription/*` capabilities. + +The audience (`aud` field) of the delegation (the `proof`) MUST be set to the [customer] of the subscription. The subject (`with` field) of the `proof` MUST be set to the [provider] of the subscription. The `nb.customer` field of the `proof` MUST be set to the [customer] of the subscription. The `nb.order` field of the `proof` MUST be set to the unique identifier of the subscription. ###### Subscription Proof Example @@ -237,7 +239,7 @@ The `proof` field MUST be a link to a valid delegation for `subscription/*` capa ### Provider List -User MAY invoke `provider/list` capability on the [account] subject (`with` field) to list all subscriptions it has with a [provider] (`aud` field). +A user MAY invoke the `provider/list` capability on the [account] subject (`with` field) to list all subscriptions it has with a [provider] (`aud` field). #### Provider List Schema @@ -252,7 +254,7 @@ type struct ProviderList struct {} ### Provider Remove -User MAY invoke `provider/remove` capability on the [account] subject (`with` field) to cancel a subscription with [provider] (`aud` field). +A user MAY invoke the `provider/remove` capability on the [account] subject (`with` field) to cancel a subscription with [provider] (`aud` field). #### Provider Remove Schema @@ -283,7 +285,7 @@ type Subscription union { ### Subscription Add -User MAY invoke `subscription/add` capability to provision desired [space] with the capabilities provided through subscription. +A user MAY invoke the `subscription/add` capability to provision desired [space] with the capabilities provided through the subscription. #### Subscription Add Schema @@ -311,11 +313,11 @@ The `nb.consumer` MUST be set to the [space] DID to be provisioned. The `nb.budget` MUST be set to the map where keys are names of the capacities and values are the allowed utilization per billing cycle. -> ℹ️ Adding [space] to the subscription twice only causes previous budget to be merged with the new one. +> ℹ️ Adding a [space] to a subscription twice only causes previous budget to be merged with the new one. ### Subscription Remove -User MAY invoke `subscription/remove` capability to remove [space] from this subscription. +A user MAY invoke the `subscription/remove` capability to remove a [space] from this subscription. #### Subscription Remove Schema @@ -334,7 +336,7 @@ type SubscriptionRemove struct { ### Subscription List -Agent MAY invoke `subscription/list` capability to list provisions under this subscription. +An agent MAY invoke the `subscription/list` capability to list provisions under this subscription. #### Subscription List Schema From 1c7e6c17c19b3db4da51a631ca07b8976b579b1f Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 31 Jan 2024 09:00:56 -0800 Subject: [PATCH 3/6] fix: ignore pipe delimited words that tool is confused by --- .github/workflows/words-to-ignore.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/words-to-ignore.txt b/.github/workflows/words-to-ignore.txt index 7a8c8f3..f6cfd91 100644 --- a/.github/workflows/words-to-ignore.txt +++ b/.github/workflows/words-to-ignore.txt @@ -152,3 +152,7 @@ fieldAccountPrincipal installationIssuerPrincipal iteratively base32 +AccountA +AgentA +IssuerA +AudiencePrincipal From 17b4144cbc1b659a1003356e8d25f35b726d9b4a Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 31 Jan 2024 09:04:19 -0800 Subject: [PATCH 4/6] Apply suggestions from code review --- w3-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3-provider.md b/w3-provider.md index dc2b868..442c8dc 100644 --- a/w3-provider.md +++ b/w3-provider.md @@ -354,7 +354,7 @@ type SubscriptionList struct { [did:key]: https://w3c-ccg.github.io/did-method-key/ [UCAN]:https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md -[principal]:https://github.com/ucan-wg/spec/blob/ +[principal]:https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md#321-principals [provider]:#provider [`did:mailto`]:./did-mailto.md [`did:key`]:https://w3c-ccg.github.io/did-method-key/ From 425a03d270e64c8508e8414d84937d4571cba93d Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 31 Jan 2024 09:07:17 -0800 Subject: [PATCH 5/6] Apply suggestions from code review --- w3-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3-provider.md b/w3-provider.md index 442c8dc..a7b89fa 100644 --- a/w3-provider.md +++ b/w3-provider.md @@ -359,7 +359,7 @@ type SubscriptionList struct { [`did:mailto`]:./did-mailto.md [`did:key`]:https://w3c-ccg.github.io/did-method-key/ [customer]:#customer -[account]:#account +[account]:./w3-account.md#account [space]:#space [subscription]:#subscription [provision]:#provision From 9e2bb3f5f550f67bf8f4a9e91333dc37b899c8af Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Wed, 31 Jan 2024 09:09:55 -0800 Subject: [PATCH 6/6] Update w3-provider.md --- w3-provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/w3-provider.md b/w3-provider.md index a7b89fa..ab8e861 100644 --- a/w3-provider.md +++ b/w3-provider.md @@ -206,7 +206,7 @@ The `product` field MUST be set to the chosen subscription plan. ##### Subscription Proof -The `proof` field of the `provider/add` invocation MUST be a link to a valid delegation of `subscription/*` capabilities. +The `proof` field of the `provider/add` invocation MUST be a link to a valid delegation of `subscription/*` capabilities. The audience (`aud` field) of the delegation (the `proof`) MUST be set to the [customer] of the subscription. The subject (`with` field) of the `proof` MUST be set to the [provider] of the subscription. The `nb.customer` field of the `proof` MUST be set to the [customer] of the subscription. The `nb.order` field of the `proof` MUST be set to the unique identifier of the subscription.