# Capabilities for w3up Metrics/Blocking Before we can bring w3up to a wider audience we need better administrative tools. This document proposes a set of UCAN capabilities that will underlie a minimum-viable set of these tools. A prototype implementation of these tools has been created in the https://github.com/web3-storage/w3admin repository, and is deployed at https://w3admin.vercel.app Mock implementations of these capabilities have been created as part of the [`w3admin`](https://github.com/web3-storage/w3admin) prototype. Capability specifications can be found in https://github.com/web3-storage/w3admin/tree/main/src/capabilities In-memory providers for these capabilities can be found in https://github.com/web3-storage/w3admin/blob/main/src/hooks/service.ts#L155 The remainder of this document will summarize the implementations linked above. ## User Needs The burden of finding and blocking problematic users mostly falls on David Choi so `w3admin` was built to meet the needs he has identified. 1. `w3admin` users need to be able to search for spaces by space DID and search for customers by email address. 2. `w3admin` users need to be able to see a space's total possible storage, allocated storage, block status and attached subscriptions as well as block or unblock a space. 3. `w3admin` users need to be able to see the customer who pays for a subscription and the space a subscription is attached to. 4. `w3admin` users need to be able to see a customer's subscriptions and whether a customer's email address or email address domain is blocked. They need to be able to block a customer's email or domain. These needs are not exhaustive and this list is expected to grow. ## Capabilities | Capability | New? | Resource | Inputs (nb) | | -------- | ------ | -------- | -------- | | customer/get | No | Provider | `customer: DIDMailto` | | consumer/get | Yes | Provider | `consumer: DIDKey` | | subscription/get | Yes | Provider | `subscription: string` | | rate-limit/add | Yes | Provider | `subject: string`, `rate: number` | | rate-limit/remove | Yes | Provider | `id: string` | | rate-limit/list | Yes | Provider | `subject: string` | ### customer/get Given a customer's `did:mailto`, return information about that customer of the given Provider. #### inputs `customer: DIDMailto` #### returns ```typescript { did: DIDMailto } ``` #### errors `CustomerNotFound` #### capability definition ```javascript= export const get = capability({ can: 'customer/get', with: ProviderDID, nb: struct({ customer: AccountDID, }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.customer, parent.nb.customer, 'customer')) || ok({}) ) }, }) ``` ### consumer/get Return information about the consumer (ie, space) identified by `consumer`. #### inputs `consumer: DIDKey` #### returns ```typescript { did: DIDKey allocated: number total: number subscription: string } ``` #### errors `ConsumerNotFound` #### capability definition ```typescript= export const get = capability({ can: 'consumer/get', with: ProviderDID, nb: struct({ consumer: Schema.string() }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.consumer, parent.nb.consumer, 'consumer')) || ok({}) ) }, }) ``` ### subscription/get Given a subscription ID, return information about the identified subscription. #### inputs `subscription: string` #### returns ```typescript= { customer: DIDMailto consumer: DIDKey } ``` #### errors `SubscriptionNotFound` #### capability definition ```javascript= export const get = capability({ can: 'subscription/get', with: ProviderDID, nb: struct({ subscription: Schema.string() }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.subscription, parent.nb.subscription, 'subscription')) || ok({}) ) }, }) ``` ### rate-limit/add Given a subject ID (eg, a`did:mailto`, a URL, a domain name, etc), set a rate limit for the entity represented by that ID. The semantics of both the subject and rate are intentionally abstract, and the service is expected to record them without much processing. Consumers of rate limits are expected to query the underlying datastore where they are stored for the subjects they care about and interpret the `rate` value in a way that makes sense for their usecase. Returns an ID that can be used to remove this limit later. #### inputs `subject: string` `rate: number` #### returns ```typescript { id: string } ``` #### errors #### capability definition ```javascript= export const add = capability({ can: 'rate-limit/add', with: ProviderDID, nb: struct({ subject: Schema.string(), rate: Schema.number() }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.subject, parent.nb.subject, 'subject')) || and(equal(child.nb.rate, parent.nb.rate, 'rate')) || ok({}) ) }, }) ``` ### rate-limit/list Given a subject ID (eg, a`did:mailto`, a URL, a domain name, etc), list all rate limits that apply to the given subject. #### inputs `subject: string` #### returns ```typescript { limits: [ { id: '123', limit: 0 }, { id: '456', limit: 2 } ] } ``` #### errors #### capability definition ```javascript= export const remove = capability({ can: 'rate-limit/list', with: ProviderDID, nb: struct({ subject: Schema.string() }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.subject, parent.nb.subject, 'subject')) || ok({}) ) }, }) ``` ### rate-limit/remove Given a rate limit ID (returned from `rate-limit/add` or `rate-limit/list`), remove the identified rate limit. #### inputs `id: string` #### returns ```typescript {} ``` #### errors `RateLimitsNotFound` #### capability definition ```javascript= export const remove = capability({ can: 'rate-limit/remove', with: ProviderDID, nb: struct({ ids: Schema.string().array() }), derives: (child, parent) => { return ( and(equalWith(child, parent)) || and(equal(child.nb.id, parent.nb.id, 'id')) || ok({}) ) }, }) ```