# 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({})
)
},
})
```