|
| 1 | +# Account Usage Get |
| 2 | + |
| 3 | +## Editors |
| 4 | + |
| 5 | +- Storacha team |
| 6 | +- Natalie Bravo |
| 7 | + |
| 8 | +## Introduction |
| 9 | + |
| 10 | +The `usage/report` capability enables an agent to obtain storage usage information on a space. |
| 11 | + |
| 12 | +Currently, whenever a user wants to view their total storage usage, whether via CLI or console, we can only provide this information if the agent being used has permission to access every space. In practice, this often isn’t the case<sup>[1](#note1)</sup>, which leads to a misalignment between the current definition of this capability and how it’s actually used across our system. |
| 13 | + |
| 14 | +With Storacha’s growing adoption through other tools, such as the Telegram Mini App and Bluesky, the total usage view has become even more fragmented. |
| 15 | + |
| 16 | +This RFC proposes a discussion on better approaches. |
| 17 | + |
| 18 | + |
| 19 | +<a id="note1"></a> |
| 20 | +> <sub>**Note 1:** Scenarios where we don't have the total view of the users spaces: |
| 21 | +> - If a user has lost the private key for a space and didn’t delegate control to us. |
| 22 | +> - If a user didn’t delegate control to us and is using a device without the appropriate delegation from a space. |
| 23 | +> - If a user logs in to a new agent (B) but creates a new space (2) on the old agent (A), agent B wouldn’t know about space 2.</sub> |
| 24 | +
|
| 25 | + |
| 26 | +## Proposal |
| 27 | + |
| 28 | +A new capability that can be invoked in the context of an Account DID (aggregating usage across all spaces). |
| 29 | + |
| 30 | +### `account/usage/get` |
| 31 | + |
| 32 | +#### Invocation |
| 33 | + |
| 34 | +```ipldsch |
| 35 | +type AccountUsageGet struct { |
| 36 | + with AccountDID |
| 37 | + nb optional AccountUsageGetNB |
| 38 | +} |
| 39 | +
|
| 40 | +type AccountUsageGetNB struct { |
| 41 | + space optional [SpaceDID] |
| 42 | +
|
| 43 | + # Optional time period (Unix timestamps in seconds). |
| 44 | + # If omitted, provider MUST return a current snapshot. |
| 45 | + period optional Period |
| 46 | +} |
| 47 | +
|
| 48 | +type Period struct { |
| 49 | + from Int # inclusive |
| 50 | + to Int # inclusive |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +> example: getting the current total usage |
| 55 | +
|
| 56 | +```json |
| 57 | +{ |
| 58 | + "iss": "did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHOOOagent", |
| 59 | + "aud": "did:web:storacha.network", |
| 60 | + "att": [ |
| 61 | + { |
| 62 | + "with": "did:mailto:web.mail:alice", |
| 63 | + "can": "account/usage/get" |
| 64 | + } |
| 65 | + ], |
| 66 | + "prf": [ |
| 67 | + { "/": "bafyAccountDelegationCid" }, |
| 68 | + { "/": "bafySessionAttestationCid" } |
| 69 | + ], |
| 70 | + "sig": "..." |
| 71 | +} |
| 72 | +```` |
| 73 | + |
| 74 | +> example: filtering a single space and period |
| 75 | + |
| 76 | +```json |
| 77 | +{ |
| 78 | + "iss": "did:key:z6MktfnQz8Kcz5nsC65oyXWFXhbbAZQavjg6LYuHOOOagent", |
| 79 | + "aud": "did:web:storacha.network", |
| 80 | + "att": [ |
| 81 | + { |
| 82 | + "with": "did:mailto:web.mail:alice", |
| 83 | + "can": "account/usage/get", |
| 84 | + "nb": { |
| 85 | + "spaces": ["did:key:z6MkuxVKbEvYzXw89c9ESd3xoZ988MFrCgqT5JF5wtBvuYWe"], |
| 86 | + "period": { |
| 87 | + "to": 1758111728, |
| 88 | + "from": 1754006400 |
| 89 | + }, |
| 90 | + } |
| 91 | + } |
| 92 | + ], |
| 93 | + "prf": [ |
| 94 | + { "/": "bafyAccountDelegationCid" }, |
| 95 | + { "/": "bafySessionAttestationCid" } |
| 96 | + ], |
| 97 | + "sig": "..." |
| 98 | +} |
| 99 | +``` |
| 100 | + |
| 101 | +#### Usage Calculation |
| 102 | + |
| 103 | +**Total usage** is the aggregate usage (e.g., total bytes stored) across all spaces for the requested Account DID. |
| 104 | + |
| 105 | +If `period` is omitted; for each space, usage is computed as: |
| 106 | + |
| 107 | +- The most recent snapshot available (typically the beginning of the current billing cycle, e.g., `startOfLastMonth(now)`). |
| 108 | + |
| 109 | +- Plus the sum of all space diff events (deltas) up to the current time (`to`) or "now". |
| 110 | + |
| 111 | +> **Note:** |
| 112 | +> To guarantee reproducible results (idempotency), clients SHOULD specify an explicit `period` when querying. |
| 113 | +
|
| 114 | +#### Receipt |
| 115 | + |
| 116 | +The capability returns a receipt containing either success or failure information. |
| 117 | + |
| 118 | +##### Authorization Requirements |
| 119 | + |
| 120 | +The invocation MUST verify that the Account DID has proper authorization to access usage data for all requested spaces. If any space is not authorized, the entire invocation SHOULD fail with an appropriate error message indicating which spaces lack authorization. |
| 121 | + |
| 122 | +##### Receipt Structure |
| 123 | + |
| 124 | +```ipldsch |
| 125 | +
|
| 126 | +type AccountUsageGetReceipt = { |
| 127 | + ran: Link<AccountUsageGet> |
| 128 | + out: Result<AccountUsageGetSuccess, AccountUsageGetFailure> |
| 129 | +} |
| 130 | +
|
| 131 | +type AccountUsageGetFailure { |
| 132 | + message: string |
| 133 | +} |
| 134 | +
|
| 135 | +type AccountUsageGetSuccess { |
| 136 | + total Int |
| 137 | + spaces Record<SpaceDID, SpaceUsage> # keys MUST be sorted |
| 138 | +} |
| 139 | +
|
| 140 | +type SpaceUsage { |
| 141 | + total Int |
| 142 | + providers Record<ProviderDID, UsageData> # keys MUST be sorted |
| 143 | +} |
| 144 | +
|
| 145 | +# UsageData is already defined and used by `usage/report` |
| 146 | +type UsageData { |
| 147 | + provider ProviderDID |
| 148 | + space SpaceDID |
| 149 | + period PeriodISO |
| 150 | + size SizeDelta |
| 151 | + events optional [UsageEvent] |
| 152 | +} |
| 153 | +
|
| 154 | +type SizeDelta { |
| 155 | + initial Int |
| 156 | + final Int |
| 157 | +} |
| 158 | +
|
| 159 | +type UsageEvent { |
| 160 | + cause Link |
| 161 | + delta Int |
| 162 | + receiptAt ISO8601Date |
| 163 | +} |
| 164 | +
|
| 165 | +type PeriodISO { |
| 166 | + from ISO8601Date |
| 167 | + to ISO8601Date |
| 168 | +} |
| 169 | +
|
| 170 | +type ISO8601Date = string |
| 171 | +type ProviderDID = string |
| 172 | +type DID = string |
| 173 | +
|
| 174 | +``` |
| 175 | + |
| 176 | +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. |
| 177 | + |
| 178 | + |
| 179 | +> example: |
| 180 | +
|
| 181 | +```json |
| 182 | +{ |
| 183 | + "total": 5356848797, |
| 184 | + "spaces": { |
| 185 | + "did:key:z6MkuxVKbEvYzXw89c9ESd3xoZ988MFrCgqT5JF5wtBvuYWe": { |
| 186 | + "total": 5356848797, |
| 187 | + "providers": { |
| 188 | + "did:web:web3.storage": { |
| 189 | + "provider": "did:web:web3.storage", |
| 190 | + "space": "did:key:z6MkuxVKbEvYzXw89c9ESd3xoZ988MFrCgqT5JF5wtBvuYWe", |
| 191 | + "period": { |
| 192 | + "to": "2025-08-12T15:55:11.000Z", |
| 193 | + "from": "2025-07-01T00:00:00.000Z" |
| 194 | + }, |
| 195 | + "size": { |
| 196 | + "final": 5356848797, |
| 197 | + "initial": 1035217049 |
| 198 | + }, |
| 199 | + "events": [ |
| 200 | + { |
| 201 | + "cause": { |
| 202 | + "/": "bafyreiafouslzry3vunc4okazrqpwpuanrq4d3ehb2oatn4vmrhnmj6rzy" |
| 203 | + }, |
| 204 | + "delta": 54394, |
| 205 | + "receiptAt": "2025-07-01T14:34:50.947Z" |
| 206 | + } |
| 207 | + ] |
| 208 | + } |
| 209 | + } |
| 210 | + } |
| 211 | + } |
| 212 | +} |
| 213 | + |
| 214 | +``` |
| 215 | + |
| 216 | +Obs.: The current `usage/report` returns the `UsageData` as a receipt. |
| 217 | + |
| 218 | + |
| 219 | +## Implementation |
| 220 | + |
| 221 | +This plan outlines the changes needed to create a new usage capability |
| 222 | + |
| 223 | +### 1. Specification Definition |
| 224 | +Create a formal capability specification in the `specs` repository. (The current `usage/report` capability lacks a formal spec) |
| 225 | + |
| 226 | +### 2. Capability definition |
| 227 | +Add the new capability definition in on `upload-service/packages/capabilities`. Also create new types to support it. |
| 228 | + |
| 229 | +### 3. Client implementation |
| 230 | +Update the `UsageClient` to support the new capability to on `upload-service/packages/w3up-client/src/capability`. |
| 231 | + |
| 232 | +### 4. Service implementation |
| 233 | +This involves two parts: |
| 234 | + |
| 235 | +- **Upload API Handler**: Add a new handler in `upload-service/packages/upload-api` to process the new capability. |
| 236 | +- **Storage Layer**: Update `w3infra/upload-api/stores/usage.js` to handle the new capability. |
| 237 | + |
| 238 | + |
| 239 | +### 5. Integrations |
| 240 | + |
| 241 | +- **CLI**: Add a new command on the CLI to support the new usage capability. |
| 242 | +- **Console**: Replace the current `usage/report` invocations in the console application with calls to the new capability. |
0 commit comments