Skip to content

Commit 0875d71

Browse files
authored
Merge pull request #64 from storacha/rfc/new-usage-capability
rfc: new usage capability proposal (account/usage/get)
2 parents 488144d + a94de1a commit 0875d71

File tree

1 file changed

+242
-0
lines changed

1 file changed

+242
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
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

Comments
 (0)