Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

This repository contains the specs for the `w3up` protocol and associated subsystems.

The implementations of these specs can be found in [storacha/upload-service/packages/capabilities](https://github.com/storacha/upload-service/tree/main/packages/capabilities)
The implementations of these specs can be found in [upload-service/packages/capabilities](https://github.com/storacha/upload-service/tree/main/packages/capabilities) (JavaScript)
and [go-libstoracha/capabilities](https://github.com/storacha/go-libstoracha/tree/main/capabilities) (Go).

## Overview

Expand Down
236 changes: 188 additions & 48 deletions w3-account.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
## Authors

- [Irakli Gozalishvili], [Protocol Labs]
- [Natalie Bravo], [Storacha Network]
- [Vicente Olmedo], [Storacha Network]

# Abstract

Expand Down Expand Up @@ -50,13 +52,13 @@ Agent authorization can use familiar email-based authorization flows providing a

There are several distinct roles that [principals] may assume in described 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 |
| 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 |

### Space

Expand Down Expand Up @@ -222,7 +224,7 @@ type AccountUsageGetNB struct {

type Period struct {
from Int # inclusive
to Int # exclusive
to Int # exclusive
}
```

Expand Down Expand Up @@ -284,52 +286,52 @@ The service MUST verify that the Account DID is authorized to access usage data
#### Receipt

```ipldsch
type AccountUsageGetReceipt = {
ran: Link<AccountUsageGet>
out: Result<AccountUsageGetSuccess, AccountUsageGetFailure>
type AccountUsageGetReceipt struct {
ran Link<AccountUsageGet>
out Result<AccountUsageGetSuccess, AccountUsageGetFailure>
}

type AccountUsageGetFailure {
message: string
type AccountUsageGetFailure struct {
message String
}

type AccountUsageGetSuccess {
type AccountUsageGetSuccess struct {
total Int
spaces Record<SpaceDID, SpaceUsage> # keys MUST be sorted
spaces {SpaceDID: SpaceUsage} # keys MUST be sorted
}

type SpaceUsage {
type SpaceUsage struct {
total Int
providers Record<ProviderDID, UsageData> # keys MUST be sorted
providers {ProviderDID: UsageData} # keys MUST be sorted
}

# UsageData is shared with `usage/report`
type UsageData {
type UsageData struct {
provider ProviderDID
space SpaceDID
period PeriodISO
size SizeDelta
events [UsageEvent]
}

type SizeDelta {
type SizeDelta struct {
initial Int
final Int
}

type UsageEvent {
type UsageEvent struct {
cause Link
delta Int
receiptAt ISO8601Date
}

type PeriodISO {
type PeriodISO struct {
from ISO8601Date
to ISO8601Date
}

type ISO8601Date = string
type ProviderDID = string
type ISO8601Date string
type ProviderDID string
```

In all responses, the keys of the `spaces` field in `AccountUsageGetSuccess` and the `providers` field in `SpaceUsage` MUST be sorted lexicographically by their respective key (`SpaceDID`, `ProviderDID`). This ensures that the same query produces the same output each time.
Expand Down Expand Up @@ -368,33 +370,171 @@ In all responses, the keys of the `spaces` field in `AccountUsageGetSuccess` and
}
```

[Protocol Labs]:https://protocol.ai/
[Irakli Gozalishvili]:https://github.com/Gozala
[PKI]:https://en.wikipedia.org/wiki/Public_key_infrastructure
[UCAN]:https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md
[`did:mailto`]:./did-mailto.md
[`did:key`]:https://w3c-ccg.github.io/did-key-spec/
[principal]:https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md#321-principals
### `account/egress/get`

Capability can be invoked by an agent to retrieve egress data for all, or a specified set, of spaces within an account for a given period.

#### Capability schema

```ipldsch
type AccountEgressGet struct {
with AccountDID
nb optional AccountEgressGetNB
}

type AccountEgressGetNB struct {
# Optional list of spaces to filter. If omitted, provider SHOULD aggregate all spaces the account is authorized to include.
spaces optional [SpaceDID]

# Optional time period
# If omitted, provider MUST return egress data from the first day of the last full month to the day the request is made.
period optional Period
}

# From and to are both expressed as ISO-8601 date-only strings (e.g. 2026-01-20).
# From is inclusive; to is exclusive.
type Period struct {
from ISO8601Date
to ISO8601Date
}

type ISO8601Date string
```

#### Invocation examples

> Example: getting the current total egress

```json
{
"iss": "did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHOOOagent",
"aud": "did:web:etracker.storacha.network",
"att": [
{
"with": "did:mailto:web.mail:alice",
"can": "account/egress/get"
}
],
"prf": [
{ "/": "bafyAccountDelegationCid" },
{ "/": "bafySessionAttestationCid" },
],
"sig": "..."
}
```

> Example: filtering a single space and period

```json
{
"iss": "did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHOOOagent",
"aud": "did:web:etracker.storacha.network",
"att": [
{
"with": "did:mailto:web.mail:alice",
"can": "account/egress/get",
"nb": {
"spaces": ["did:key:z6MkuxVKbEvYzXw89c9ESd3xoZ988MFrCgqT5JF5wtBvuYWe"],
"period": {
"from": "2025-07-01",
"to": "2025-07-03"
}
}
}
],
"prf": [{ "/": "bafyAccountDelegationCid" }, { "/": "bafySessionAttestationCid" }],
"sig": "..."
}
```

#### Authorization requirements

The service MUST verify that the Account DID is authorized to access egress data for all requested spaces. If any of the requested spaces is not authorized, the entire invocation SHOULD fail with an error message indicating which spaces lack authorization.

#### Receipt

```ipldsch
type AccountEgressGetReceipt struct {
ran Link<AccountEgressGet>
out Result<AccountEgressGetSuccess, AccountEgressGetFailure>
}

type AccountEgressGetFailure struct {
name String
message String
}

type AccountEgressGetSuccess struct {
total Int # total egress for the account in the requested period. Unit: bytes.
spaces {SpaceDID: SpaceEgress} # breakout per-space. Keys MUST be sorted.
}

type SpaceEgress struct {
total Int # total egress for the space in the requested period. Unit: bytes.
dailyStats [DailyStat] # sorted by date ascending
}

type DailyStat struct {
date ISO8601Date
egress Int # egress for that date. Unit: bytes.
}
```

In all responses, the keys of the `spaces` field in `AccountEgressGetSuccess` MUST be sorted lexicographically. `DailyStats` within each `SpaceEgress` MUST be sorted by date ascending. This ensures that the same query produces the same output each time.

##### Receipt example (success)

```json
{
"total": 1111111110,
"spaces": {
"did:key:z6MkuxVKbEvYzXw89c9ESd3xoZ988MFrCgqT5JF5wtBvuYWe": {
"total": 1111111110,
"dailyStats": [
{
"date": "2025-07-01",
"egress": 123456789
},
{
"date": "2025-07-02",
"egress": 987654321
}
]
}
}
}
```

[Protocol Labs]: https://protocol.ai/
[Irakli Gozalishvili]: https://github.com/Gozala
[Natalie Bravo]: https://github.com/bravonatalie
[Vicente Olmedo]: https://github.com/volmedo
[Storacha Network]: https://storacha.network/
[PKI]: https://en.wikipedia.org/wiki/Public_key_infrastructure
[UCAN]: https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md
[`did:mailto`]: ./did-mailto.md
[`did:key`]: https://w3c-ccg.github.io/did-key-spec/
[principal]: https://github.com/ucan-wg/spec/blob/692e8aab59b763a783fe1484131c3f40d997b69a/README.md#321-principals
<!-- markdown-link-check-disable -->
<!-- stackexchange 403s this, presumably to prevent bot scraping -->
[non-extractable keys]:https://crypto.stackexchange.com/questions/85587/what-do-people-use-non-extractable-webcrypto-keys-for/102695#102695
[non-extractable keys]: https://crypto.stackexchange.com/questions/85587/what-do-people-use-non-extractable-webcrypto-keys-for/102695#102695
<!-- markdown-link-check-enable-->
[agent]:#agent
[account]:#account
[UCAN-IPLD Schema]:https://github.com/ucan-wg/ucan-ipld/#2-ipld-schema
[link]:https://ipld.io/docs/schemas/features/links/
[authorization payload]:#authorization-payload
[RFC6376]:https://www.rfc-editor.org/rfc/rfc6376#section-3.4
[Nonstandard `VarSig` signature]:https://github.com/ucan-wg/ucan-ipld/#251-nonstandard-signatures
[ABNF]:https://en.wikipedia.org/wiki/Augmented_Backus%E2%80%93Naur_form
[DAG-JSON]:https://ipld.io/specs/codecs/dag-json/spec/
[ucan attestation]:./w3-ucan.md#attestation
[agent]: #agent
[account]: #account
[UCAN-IPLD Schema]: https://github.com/ucan-wg/ucan-ipld/#2-ipld-schema
[link]: https://ipld.io/docs/schemas/features/links/
[authorization payload]: #authorization-payload
[RFC6376]: https://www.rfc-editor.org/rfc/rfc6376#section-3.4
[Nonstandard `VarSig` signature]: https://github.com/ucan-wg/ucan-ipld/#251-nonstandard-signatures
[ABNF]: https://en.wikipedia.org/wiki/Augmented_Backus%E2%80%93Naur_form
[DAG-JSON]: https://ipld.io/specs/codecs/dag-json/spec/
[ucan attestation]: ./w3-ucan.md#attestation
[IPLD]: https://ipld.io/
[DAG-CBOR]: https://ipld.io/specs/codecs/dag-cbor/spec/
[DID methods]:https://www.w3.org/TR/did-core/#methods
[w3up]:https://github.com/web3-storage/w3up
[owner]:#owner
[space]:#space
[DKIM]:https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail
[attestation]:./w3-ucan.md#attestation
[authority]:#authority
[DID methods]: https://www.w3.org/TR/did-core/#methods
[w3up]: https://github.com/web3-storage/w3up
[owner]: #owner
[space]: #space
[DKIM]: https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail
[attestation]: ./w3-ucan.md#attestation
[authority]: #authority