# UCAN.storage Specification v0.0.1
## Authors
- [Hugo Dias](https://github.com/hugomrdias)
- [Irakli Gozalishvili](https://github.com/Gozala)
# 0. Abstract
UCAN.storage defines the [UCAN](https://github.com/ucan-wg/spec/blob/main/README.md) protocol used by [web3.storage](web3.storage) and [nft.storage](nft.storage). It inherits all the UCAN characteristics by just defining a specific set of [capabilities](https://en.wikipedia.org/wiki/Object-capability_model) and [facts](https://github.com/ucan-wg/spec/blob/main/README.md#324-facts).
## 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 [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119).
# 1. Introduction
UCAN.storage describes how our services leverage UCANs to decentralized authorization and access permissions using a specific capability scheme embedded in UCAN tokens.
The rest of this document outlines the `storage` scheme, the capabilities it supports and considerations that implementers might want to be aware of when implementing.
# 2. Terminology
Refer to [UCAN Spec](https://github.com/ucan-wg/spec/blob/main/README.md#2-terminology) for general UCAN terminology. This document defines only term specific for UCAN.storage.
# 2.1 Service
A service is an entity that implements this spec, is able to serve as root issuer of UCAN.storage tokens and validate them. Examples of UCAN.storage complaint services are [web3.storage](https://web3.storage) and [nft.storage](https://nft.storage).
# 2.2 DID
DIDs in this document use the [`did:key` method](https://w3c-ccg.github.io/did-method-key/) and may be referred to as `did:user1`, `did:service` for simplicity in examples and descriptions.
The actual representation is always as described in the `did:key` [specification](https://w3c-ccg.github.io/did-method-key/#format).
```abnf
did-key-format := did:key:<mb-value>
mb-value := z[a-km-zA-HJ-NP-Z1-9]+
```
# 3. JWT Structure
The JWT header and payload structure in this document serves just a visual reference, the overall container remains the same as in the [UCAN spec](https://github.com/ucan-wg/spec/blob/main/README.md#3-jwt-structure). For UCAN.storage this document defines just the structure of the `att` claims.
## 3.1 Header
The header MUST include all of the following fields:
| Field | Type | Description | Required |
|-------|----------|--------------------------------|----------|
| `alg` | `String` | Signature algorithm | Yes |
| `typ` | `String` | Type (MUST be `"JWT"`) | Yes |
| `ucv` | `String` | UCAN Semantic Version (v2.0.0) | Yes |
## 3.2 Payload
The payload MUST describe the authorization claims being made, who is involved, and its validity period.
| Field | Type | Description | Required |
| ----- | ---------- | ------------------------------------------------ | -------- |
| `iss` | `String` | Issuer DID (sender) | Yes |
| `aud` | `String` | Audience DID (receiver) | Yes |
| `nbf` | `Number` | Not Before UTC Unix Timestamp (valid from) | No |
| `exp` | `Number` | Expiration UTC Unix Timestamp (valid until) | Yes |
| `nnc` | `String` | Nonce | No |
| `fct` | `Json[]` | Facts (asserted, signed data) | No |
| `prf` | `String[]` | Proof of delegation (witnesses are nested UCANs) | Yes |
| `att` | `Json[]` | Attenuations | Yes |
### 3.2.2 Attenuation
The REQUIRED `att` field contains a set of capabilities as defined in the [UCAN Attenuation Scope](https://github.com/ucan-wg/spec/blob/main/README.md#325-attenuation-scope).
For UCAN.storage `att` MUST contain at least one capability using the `storage` scheme.
#### Examples
```json
"att": [
{
"with": "storage://<did_aud>/<did_aud>",
"can": "upload/IMPORT",
}
]
```
# 4. `storage` scheme
The `storage` scheme represents the ownership and access permissions over storage resources in a [Service](#21-Service).
## 4.1 Resource Pointer
The resource pointer MUST match the current proof scope. Meaning it will follow the UCAN chain of audiences.
When a [Service](#21-Service) issues a UCAN for `did:user1` the resource will be `storage://did:user-1`, if `user-1` issues a delegated UCAN to `did:user-2` it MAY further restrict the scope of it by setting the resource to `storage://did:user-1/did:user-2/`.
When restricting the issuer MUST add another path segment to the resource URI. Using audience DID will guarantee uniqueness, although it is not REQUIRED to be unique and could be anything i.e. `storage://did:user-1/public`.
<!--
> We avoid name collisions simply by treating `/` terminated paths as directories and non `/` terminated as files.
-->
#### Examples
```json
// Ucan from service to user1
{
"iss": "did:service"
"aud": "did:user-1"
"att": {
"with": "storage://did:user-1"
}
}
// Ucan from user1 to user2
{
"iss": "did:user-1"
"aud": "did:user-2"
"att": {
"with": "storage://did:user-1/did:user-2"
}
}
// Ucan from user2 to service
{
"iss": "did:user2"
"aud": "did:service-did"
"att": {
"with": "storage://did:user-1/did:user-2"
}
}
```
## 4.2 Capabilities
### 4.2.1 `upload/IMPORT` Importing CARs
The `upload/IMPORT` capability allows access to importing a CAR under the specified resource in the `with` field.
```json
"att": [
{
"with": "storage://did:user",
"can": "upload/IMPORT",
}
]
```
#### 4.2.1.1 Constraints
The `upload/IMPORT` ability MUST support a OPTIONAL field `mh` to constrain an import by [multihash](https://github.com/multiformats/multihash).
> A Service MAY use this multihash to perform integrity check.
```json
"att": [
{
"with": "storage://did:user",
"can": "upload/IMPORT",
"mh": "CIQJZPAHYP4ZC4SYG2R2UKSYDSRAFEMYVJBAXHMZXQHBGHM7HYWL4RY"
}
]
```
# 5. User Stories
### Marketplace intermediary
Marketplace issues delegated restricted tokens to their users.
#### Access control
To limit to single upload session market can issue UCAN for DID for the session with short lifespan. Client app may perform multiple CAR uploads for that specific DID and throw the key once complete.
If UCAN expires user can request another UCAN to extend the session can be issued by marketplace.
This way maketplace can track specific uploads by DIDs.
further can limit by requesting upload specific UCANs and throw them away once upload is done.
```mermaid
sequenceDiagram
Note over Service,Marketplace: Service needs to know Marketplace
Marketplace->>Service: Request UCAN for did:market
Service->>Service: Signs UCAN for did:market
Service->>Marketplace: UCAN for did:market <br>with full capabilities
loop Every time UCAN expires
Alice->>Marketplace: Request UCAN for did:alice
Marketplace->>Marketplace: Signs UCAN for did:alice
Marketplace->>Alice: Short lived UCAN for did:alice <br> with upload/IMPORT
end
Alice->>Alice: Signs UCAN for did:service
Alice->>Service: UCAN+Car
```
```json
// UCAN for did:market
{
"iss": "did:service",
"aud": "did:marketplace",
"exp": 1643905307, // 2 years into the future
"att": {
{
"with": "storage://did:marketplace",
"can": "upload_v1/*"
}
}
}
// UCAN for did:alice
{
"iss": "did:marketplace",
"aud": "did:alice",
"exp": 1643905307, // 15 mins into the future
"att": {
{
"with": "storage://did:marketplace/did:alice",
"can": "upload_v1/IMPORT"
}
}
}
// UCAN for did:service
{
"iss": "did:alice",
"aud": "did:service",
"exp": 1643905306, // =< 15 mins
// call we skip this att since it can't be anything other than the previous ????
"att": {
{
"with": "storage://did:marketplace/did:user", // "with": "prf:*"
"can": "upload_v1/IMPORT" // "can": "ucan/DELEGATE"
},
},
"prf": [
ucan_0
]
}
```
### Marketplace intermidiates every upload by multihash
```mermaid
sequenceDiagram
Note over Service,Marketplace: Service needs to know Marketplace
Marketplace->>Service: Request UCAN for did:market
Service->>Service: Signs UCAN for did:market
Service->>Marketplace: UCAN for did:market <br>with full capabilities
Alice->>Marketplace: Request UCAN for did:alice <br> and a multihash
Marketplace->>Marketplace: Signs UCAN for did:alice
Marketplace->>Alice: UCAN for did:alice with upload/IMPORT <br>constrained to a multihash
Alice->>Alice: Signs UCAN for did:service
Alice->>Service: UCAN+Car
```
```json
// UCAN for did:market
{
"iss": "did:service",
"aud": "did:marketplace",
"exp": 1643905307, // 2 years into the future
"att": {
{
"with": "storage://did:marketplace",
"can": "upload_v1/*"
}
}
}
// UCAN for did:alice
{
"iss": "did:marketplace",
"aud": "did:alice",
"att": {
{
"with": "storage://did:marketplace/did:user",
"can": "upload_v1/IMPORT",
"mh": "CIQJZPAHYP4ZC4SYG2R2UKSYDSRAFEMYVJBAXHMZXQHBGHM7HYWL4RY"
},
}
}
// UCAN for did:service
{
"iss": "did:user",
"aud": "did:service",
// call we skip this att since it can't be anything other than the previous ????
"att": {
{
"with": "storage://did:marketplace/did:user",
"can": "upload_v1/IMPORT"
},
}
}
```