# Storacha Privacy Layer - Discovery & Proposal
## Discovery
## 1. Research and document the current state of Encryption & Access Control across Storacha systems
**What it is**
Understand and map how encryption currently works (or doesn’t) across all components.
**What we want to answer**
- Where and how is encryption applied today?
- Are private files actually private?
- How to handle private content and metadata exposure to Filecoin, IPFS, IPNI, etc?
- How do we handle Identity & Authority?
## 1.1. Upload & Download Encryption Flow
Our infra is currently integrated with the Lit Protocol into Storacha's upload service repository (https://github.com/storacha/upload-service/pull/192/files). This is an overview of how encrypted uploads are handled:
##### Step 1. Initialization
* The client initializes the `EncryptClient` by providing a `storachaClient` and a `cryptoAdapter`.
* The `cryptoAdapter` defines methods for encrypting and decrypting data streams. By default it use Node.js crypto API (`crypto.webcrypto.subtle`) with `AES-256-CBC` encryption algorithm.
Notes:
- :warning: `AES-256-CBC`: does not support authentication. It means the data is encrypted, but anyone can change the ciphertext, and the receiver will decrypt garbage without noticing. This opens the door to bit-flipping and padding oracle attacks. Recommendation: `AES-GCM` - the encryption produces not only ciphertext but also an authentication tag (aka MAC), when decrypting, the system verifies this tag. If the ciphertext or associated data was modified, decryption fails with an error.
- :warning: The encryption algorithm needs to support streamming, otherwise large files will need to be loaded to the memory, which is not good. `CBC` supports that, but `GCM` doesn't. Another alternative is using `CTR` - which is compatible with Browsers as well. The current implementation only supports Node environments.
- :warning: The encryption library is only compatible with Node environments and does not support usage in browsers.
- :warning: The Lit Action currently validates only `did:mailto` and `did:key` identifiers, without performing any DID resolution.
##### Step 2. Encryption
* When a file is uploaded, the client encrypts the file using the `encryptStream` method from the `cryptoAdapter`. This method returns the encryption key, initialization vector (IV), and the encrypted data stream.
* An Access Control Condition (ACC) is generated based on the current space setup in the Storacha client.
* The encryption key is then encrypted using the Lit Protocol, associating it with the ACC.
* **Encryption Flow**
```mermaid
sequenceDiagram
participant User
participant StorachaClient
participant CryptoAdapter
participant LitProtocol
participant Storacha
User->>StorachaClient: Request to upload file
StorachaClient->>CryptoAdapter: encryptStream(file)
CryptoAdapter->>CryptoAdapter: Generate Symmetric Key
CryptoAdapter-->>StorachaClient: key, iv, encryptedStream
StorachaClient->>LitProtocol: Encrypt Symmetric Key with Lit ACC
LitProtocol-->>StorachaClient: Encrypted Symmetric key
StorachaClient->>Storacha: Upload encryptedStream and encryption metadata
Storacha-->>StorachaClient: Encryption Metadata CID
StorachaClient-->>User: Return CID
```
##### Step 3. Upload
* The encrypted file, along with the encrypted metadata (including the encrypted key and ACC), is uploaded to Storacha.
##### Step 4. Decryption
* To decrypt a file, the user must have:
* The `CID` of the encrypted file metadata.
* A `UCAN delegation` in CAR format with the `space/content/decrypt` capability.
* `Capacity Credits` on the Lit Network (it will be covered by Storacha).
* The client retrieves the encrypted file and metadata, decrypts the encryption key using Lit Protocol (verifying the ACC), and then decrypts the file using the key and IV.
* **Decryption Flow**
```mermaid
sequenceDiagram
participant User
participant StorachaClient
participant Gateway
participant LitProtocol
participant CryptoAdapter
User->>StorachaClient: Request to retrieve & decrypt file by CID
StorachaClient->>Gateway: Get encryption metadata by CID
Gateway-->>StorachaClient: Return encryption metadata
StorachaClient-->>StorachaClient: Extract: encrypted data CID, Encrypted Symmetric Key, ACC
StorachaClient->>LitProtocol: Request decryption of Symmetric Key with ACC and authSig
LitProtocol-->>StorachaClient: Return Decrypted Symmetric Key
StorachaClient->>CryptoAdapter: Decrypt file using Symmetric Key and IV
CryptoAdapter-->>StorachaClient: Return Decrypted File
StorachaClient-->>User: Return Decrypted File
```
**Notes**
- **Funding**: Users are **NOT** required to control an Externally Owned Account (EOAs) / Ethereum Wallet.
- The decryption of the simmetric key happens in the Lit Action which validates the `space/content/decrypt` capability, regardless of the Ethereum Wallet.
- The wallet is configure just to cover the fees of the Lit Network, and the intent is to get an agreement with Lit team that will cover the capacity credits for our users for a given period of time.
- Once the simmetric key is decrypted in the Lit Action, the user can perform the **decryption of the file on the client side**.
- **Access Control Condition**: The ACC defines who can decrypt the file.
- It's enforced by the Lit Protocol Action during the decryption process.
- The Lit Action validates if the issuer of the decryption operation is the same as the audience provided in the delegation proof. Otherwise the issuer is not authorized to decrypt the symmetric key. It also checks if the delegation proof matches the allowed SpaceDID action parameter.
- There is no verification if the delegation proof was revoked or not.
- **UCAN Delegation**: The `space/content/decrypt` capability ensures that only authorized users can decrypt the content stored in a given Space.
- **Recovery**: Decryption requests are signed by the Storacha Agent, which is tied to the authenticated Storacha user account. When a user creates a Space, they can optionally:
- 1) Add a recovery account,
- 2) Download a mnemonic, which can later be used to restore access to the Space.
- This recovery mechanism allows users to regain control of encrypted content and delegate decryption permissions to other user accounts if needed.
## 1.2. Investigate Filecoin/IPFS/IPNI handling
```mermaid
flowchart TD
U[User Uploads File] --> HS[Hot Storage]
HS --> Decision{Is File Private?}
Decision -->|No| FC[Filecoin Pipeline]
FC --> IPFS[IPFS: CID Indexing]
IPFS --> IPNI[IPNI: CID + Location Info]
IPFS --> BS[Bitswap Announcement]
Decision -->|Yes| XFC[Skip Filecoin]
XFC --> XIPFS[Do Not Publish to IPFS]
XIPFS --> XIPNI[Do Not Publish to IPNI]
XIPNI --> R2[Backup Copy to R2]
R2 --> GW[Serve via Gateway - Access Controlled]
```
**Notes**
- :warning: By default, an encrypted file is published to Filecoin, and the metadata and CID are published to IPNI/IPFS. The flow for private files (`private = yes`) is not yet implemented but represents a **potential path forward**. It outlines how encrypted files could be handled **without publishing CIDs and Location metadata to public discovery networks** like IPFS and/or IPNI, and **without being stored on Filecoin for redundancy**. This ensures encrypted/private content is **never pushed to long-term archival storage** where retrieval can't be restricted.
- :warning: There is no file content Removal/Deletion operation. When you delete a file, we just remove a record from our DynamoDB, but we don't delete the existing file content. Even if the file is not published to the Filecoin, and other discovery networks.
- To ensure durability and availability, we could store a **redundant encrypted copy in R2 or S3**, serving as a fallback in case the Storacha network becomes temporarily unavailable.
- In this model, **access is enforced at the gateway level** using **access tokens and delegation proofs** that grant `space/content/serve` capabilities. This ensures that only authorized users can retrieve and decrypt private content.
- For **Bitswap** - still need to understand how to prevent unauthorized content delivery.
- **Access tokens** also allow us to **identify the space owner**, enabling proper egress accounting and billing.
- **Access control for retrievals via storage nodes** (outside the gateway) still needs to be defined.
- **Client-side encryption** currently occurs after the file is fetched from the gateway. However, we could extend the model to allow **delegations that permit the gateway to decrypt on the user's behalf**, if desired.
- **There is currently no deletion operation in place**. Supporting deletion of private content will be necessary to allow users to revoke access and remove sensitive data permanently.
## 1.3. Document Access Control & Revocation
**Summary**
* Access to files is enforced by verifying **UCAN delegations** with the capability `space/content/serve/*`. These files are not encrypted, they are all public at the moment. There is no access token validation at the delegation level, although the content handler supports the token in the request headers.
* The **space identity** is resolved and checked against the delegation
* Delegation proofs are loaded fromt he storage and validated during the retrieval operation to authorize access
* **Unauthorized requests** fail early in middleware before reaching business logic
**Access Control**
**Content Serve Delegation Flow**
```mermaid
sequenceDiagram
box Client Side
participant User
participant StorachaClient
end
box Gateway
participant AccessDelegateHandler
end
box Cloudflare KV
participant DelegationsKV as Delegations Store
end
User->>StorachaClient: Create space and delegate access
StorachaClient->>AccessDelegateHandler: POST /access/delegate (UCAN proofs)
AccessDelegateHandler->>AccessDelegateHandler: Validate capability and UCAN proof chain
AccessDelegateHandler->>DelegationsKV: Store delegation
```
**Explanation**
1. **User Interaction**
The user interacts with the `Storacha Client` to create a space and delegate `space/content/serve` to the gateway. By default, the Freeway gateway is authorized, but alternative gateways can also be specified.
2. **UCAN Invocation**
The Storacha Client sends a `POST /access/delegate` request to the Gateway `Access/Delegate` endpoint during the space creation, it includes `{ space, proofs }`. This endpoint validates and stores the delegation for later use.
3. **Validation Steps**
* The UCAN handler **does not currently verify** that the space has been provisioned.
* It checks that if there are any `space/content/serve/*` delegations for a given space, and validates the UCAN proof chain.
4. **Storing Delegation**
Valid delegations are stored in the **Delegations KV Store**, where the Freeway Worker can access them during content retrieval.
The space DID Key + the Delegation CID are combined to form the Key in the Delegations store, and the Value is the `space/content/serve/*` delegation in array buffer format. It means you can have multiple delegations for the same space.
**Content Serve Authorization Flow**
```mermaid
sequenceDiagram
box Client Side
participant HTTPClient as User or App
end
box Gateway
participant FreewayWorker
end
box Cloudflare KV
participant DelegationsKV as Delegations Store
end
HTTPClient->>FreewayWorker: GET /ipfs/:cid
FreewayWorker->>DelegationsKV: Load delegation proofs (did:key:space:*)
FreewayWorker->>FreewayWorker: Validate proofs
FreewayWorker-->>HTTPClient: Serve content (if authorized)
```
**Explanation**
1. **Content Retrieval**
When a user or application requests a file via CID (e.g. `GET /ipfs/:cid`), the **Freeway Worker**:
* Resolves the associated space.
* Loads the delegation proofs from the KV store by listing all delegations by prefix: `did:key:[space]:*` - `*` indicates all delegation CIDs.
* Verifies that the requester has permission to retrieve content from that space.
**Notes**
* **Mitigating DoS Attacks**
:warning: To prevent abuse, the `Access/Delegate` endpoint should verify that the referenced space has actually been provisioned. This could be done by querying the space registry or relevant metadata store (TODO).
* **Access Token Support**
Adding short-lived or scoped **access tokens** on top of delegations would simplify content sharing. Ideally, users could generate these tokens during **space creation** and/or **file upload** (TODO).
* **Egress Billing**
Determining the **space ID** is critical for identifying the account responsible for egress costs. This billing flow is in progress but not yet fully live in production because it depends on the `upload-service` and `w3up` repo unification (TODO). It also relies on the user `did:mailto` to link the Stripe account.
* **Existing Spaces**
:warning: There is an custom logic to handle existing spaces as legacy spaces - spaces that were created before the Content Serve Auth flow was implemented.
For the legacy spaces, the Content Locator (indexer/content-claim service) returns Site results which have no Space attached, for these, the gateway will always serve the content, but only if the request has no authorization.
* **Code Locations**
Relevant files in
[freeway/src](https://github.com/storacha/freeway/tree/main/src)
* `middleware/withAuthToken.js`
* `middleware/withAuthorizedSpace.js`
* `middleware/withDelegationsStorage.js`
* `middleware/withLocator.js`
* `middleware/withUcanInvocationHandler.js`
* `server/service.js`
[upload-service/packages/w3up-client/src](https://github.com/storacha/upload-service/blob/main/packages/w3up-client/src/client.js#L305)
* `client.js`
[upload-service/packages/cli](
https://github.com/storacha/upload-service/blob/main/packages/cli/space.js#L39)
* `space.js`
**Delegation Revocation**
**Summary**
The existing implementation of the revocation operation allows users to revoke delegation via CLI or SDK. This operation sends a UCAN invocation to the Upload Service which will be handled by the [Revoke Handler](https://github.com/storacha/upload-service/blob/d494a823253060e791b4d80d30bec50d6a7a4b95/packages/upload-api/src/ucan/revoke.js#L10). However, there is no mechanism to check if a delegation was revoked or not.
```mermaid
sequenceDiagram
participant User
participant CLI as Storacha CLI or SDK
participant UploadService
participant RevokeHandler
participant DelegationStore
User->>CLI: storacha delegation revoke <delegation-cid>
CLI->>UploadService: UCAN invocation to revoke delegation
UploadService->>RevokeHandler: Pass revoke request
RevokeHandler->>DelegationStore: Add revocations
RevokeHandler-->>UploadService: Revocation confirmed
UploadService-->>CLI: Response to user revoked
```
**Explanation**
1. **User initiates revocation**
The user runs a CLI command like `storacha delegation revoke <delegation-cid>`, or uses the equivalent method in the JS SDK. This generates a **UCAN invocation** specifying the delegation to be revoked.
2. **Upload Service receives the invocation**
The request is sent to the **Upload Service**, which routes it to the appropriate handler.
3. **Revoke Handler processes the request**
The `RevokeHandler` (at `upload-api/src/ucan/revoke.js`) handles the UCAN invocation.
4. **Delegation Store interaction**
* It **stores the revocation entry** in the Delegation Store (it is a DynamoDB table).
5. **Response to the user**
Once the revocation is stored, the Upload Service returns a response indicating the delegation has been successfully revoked.
**Notes**
* :warning: There is currently **no mechanism to check whether a delegation has been revoked**. This problem is being tracked in [upload-service#177](https://github.com/storacha/upload-service/issues/177).
* :warning: The **gateway** does **not validate revocation status** during content access. Still need to be implemented. Related issues:
* [project-tracking#213](https://github.com/storacha/project-tracking/issues/213)
* [project-tracking#214](https://github.com/storacha/project-tracking/issues/214)
* [project-tracking#215](https://github.com/storacha/project-tracking/issues/215)
* :warning: The **Lit Action** does **not verify whether delegations have been revoked** when checking access conditions. This will be addressed in [upload-service#195](https://github.com/storacha/upload-service/issues/195).
* :warning: Every time a Lit Action gets updated and deployed, the existing encrypted data won't be decryted by the new action anymore. So we need a solution to ensure client upgrades won't break decryption when the action changes.
* Can we use UCN to sync the latest deployed Lit Action?
* Can we build a Lit Action that acts as a Proxy to route the decryption request to other Lit Actions based on client version?
* :warning: We still need to define how the transaction sponsoring will work.
## 1.4. Identity & Authority Chain
**Current Implementation**
In the current Storacha Identity & Authority Chain model, the root authority is the private key of the space DID. All capability delegations descend from this key. The flow is:
- A wildcard delegation is signed from the space DID to the local Storacha agent DID
- A wildcard delegation is also signed from the space DID to the user's account DID (currently using `did:email`, future: `did:plc` ?)
- After delegations are created, the space private key is discarded
:warning: This model works for initial bootstrapping, but it depends on identity representations like `did:email` which are **non-standard and cannot be resolved to a DID document**.
:warning: Lit actions currently validate access using these direct delegations and do not rely on DID resolution.
**Proposed Improvement for Privacy Support**
To support encrypted/decrypted content with recoverable and rotating keys, instead of using `did:email` it would be great to adopt a standard method such as `did:plc` for user identity.
The reason for that is, we need a stable/static user DID using the [`did:plc`](https://github.com/did-method-plc/did-method-plc ) method to have a stable identity model which allows PKP rotation and recovery based on a canonical user DID. This approach allows **DID document resolution**, which enables PKP rotation, recovery, updates and better integration with decentralized identity standards. For more details on the `did:plc` adoption see: https://hackmd.io/@fforbeck/rymUnIoWll
For example, when a user signs up:
- A `did:plc` identifier is created on the client side, and stored as the user's canonical DID
- The space DID issues a UCAN delegation to the user's `did:plc`
- The `did:plc` document contains multiple PKPs:
- One PKP listed under `verificationMethod` for regular use
- Optional recovery PKPs listed separately
The Lit action will resolve the `did:plc` to validate that the signing PKP is listed in the DID document. This supports:
- A stable identity tied to encrypted content
- Key rotation and recovery
- Separation of space authority and user-level access
The **space DID continues to be the root authority**. It issues UCAN delegations to the user's `did:plc`. All decryption authority flows from the space DID to the user's stable DID, which in turn allows the associated PKPs to act on behalf of the user.
Using `did:plc` is key because it is a standard backed by an active companies in the ecosystem such as Bluesky.
:warning: This is the ideal scenario, but currently we need the `did:mailto` to link the user identity to the Stripe customer account, so in a first iteration we would probably need to preserve the `did:mailto` in the authority chain.
For additional details on how we can implement and integrate `did:plc` see: https://hackmd.io/@fforbeck/rymUnIoWll
## 2. Map out product vision and key requirements
**What it is**
Gather expectations and vision from the stakeholders.
**What we want to answer**
- What use cases require privacy?
- What features are essential (recovery, sharing, performance)?
- How does this fit across all existing and new integrations?
## 2.1. Sync with Internal Stakeholders
I met with **Alex** and **Hannah** to gather product vision and expectations around privacy support. Alex provided the high-level product direction, while Hannah focused on technical possibilities for decentralized key management and Lit integration.
**Encryption**
• Ensures file contents are protected from unauthorized access.
• Files are encrypted client-side before upload, so only authorized users with the correct key or delegation can decrypt and read the data.
**File Naming**
• Allows users to assign human-readable names to encrypted files.
• File names remain visible even for private files to support usability in the Console and CLI.
**File Tagging**
• Enables users to attach metadata (such as labels, categories, or keywords) to files.
• Tags improve file organization and make filtering and searching easier in large buckets or spaces.
**Sharing at the File Level**
• Users can delegate access to a specific file using UCANs or access tokens.
• Enables selective sharing without exposing unrelated files or the entire bucket.
**Sharing at the Bucket/Space Level**
• Grants access to all files within a bucket or space.
• Useful for team collaboration or applications that need broader access.
**Revocation**
• Allows users to cancel previously granted access to a file or space.
• Once revoked, the delegation or access token is no longer valid and should be rejected by the system.
**Deletion**
• Permanently removes private files and their metadata from storage.
• Ensures the content is no longer accessible or discoverable through the system.
##### Alex’s Vision (Product-focused)
**Console App**
* Users should be able to:
* Upload private files with **clear file names**
* Use **tags** to organize files
* **Share files with friends** via delegated access
* **Choose privacy mode** (public or private) at **bucket/space creation** (default is private)
* **Revoke** access after sharing
* **Delete** files if needed
**CLI**
* Must expose all available options:
* Encryption
* Recovery
* Revocation
* Deletion
**Warm Storage**
* **Encryption is default** for private content
**Recovery**
* Support showing a **mnemonic** or allowing **encrypted JSON download**
* Default identity: `did:key` or `did:plc` (not tied to email)
* Allow **adding recovery accounts**
* A **semi-centralized recovery solution** is acceptable
* Could use cloud KMS for initial rollout (depends on the cost)
* Self-hosting or self-sovereign recovery can be layered later
* Cost trade-offs need to be considered
**Design Priorities (in order)**
1. Access & Recovery
2. Performance
3. Self-sovereignty
**Proposal Requirements**
* **User-first UX**, high **performance**, no-risk of losing access
* Support for **file sharing via delegation**
* Clear **key management boundaries** (server or client-side encryption, with UX in mind)
* Comparison of **centralized vs decentralized cost, resilience, and developer experience**
##### Hannah’s Vision (Architecture-focused)
Interested in exploring a **decentralized, UI-friendly key management architecture using Lit PKPs** (Programmable Key Pairs).
**Proposed Flow**
* Mint a **PKP** on Lit (decentralized key generated via DKG)
* Solve the “no Eth address” challenge by exploring **Claimable Keys** or minting on behalf of the user
* Fund the PKP using **capacity credits** from a Storacha wallet
* Bind **Web2 auth** (e.g., OAuth, Stytch, WebAuthn) directly to the PKP
* Use the PKP to **pay for decryption**
* Optionally:
* Convert the PKP into a **DID** for identity purposes
* Use the PKP as a **recovery key** for a `did:plc` identity
**Vision Outcome**
* A **decentralized identity and encryption flow** abstracted behind familiar login
* No need for users to manage private keys directly
* Aligns well with Bluesky backup needs and future UI-facing integrations
**Notes**
- [Proof Of Concept](https://github.com/storacha/project-tracking/issues/423)
## 2.2. Document product-level expectations
##### Essential Features
**1. Recovery**
* Users must be able to **recover their private files**
* Recovery strategies may include:
* Displaying a **mnemonic** or downloadable **encrypted JSON**
* A hybrid of **centralized recovery** and **self-sovereign options**
* Optional **cloud key management** for scalability
**2. File Sharing**
* Users should be able to **share files** selectively (e.g., with friends, apps)
* Based on **UCAN delegation**, not static ACCs
* Must support **revocation of access**
**3. Performance**
* Encryption and delegation flows should have **minimal UX impact**
* Lit usage should be optional or async to avoid blocking uploads
**4. Usability**
* Prioritize **clear, intuitive flows** for:
* Creating private buckets
* Uploading encrypted files
* Delegating and revoking access
* Recovering lost access
* Make privacy **transparent**, not overwhelming
**5. Sovereignty**
* Offer **self-sovereign options** (Lit, DID keys, UCANs) for advanced users
* Provide **fallbacks** with centralized key custody for ease of use
## 2.3. Identify shared and conflicting priorities
| Priority | Shared Across Teams | Conflicts or Tensions |
| --------------- | ------------------------------------------------ | --------------------------------------------------------- |
| **Recovery** | Important across all systems | Self-sovereign recovery vs centralized cloud KMS |
| **Performance** | Important across all systems | Lit adds latency; centralized KMS is faster but less open and probably expensive |
| **Costs** | Important across all systems | Centralized KMS is expensive; Still need to evaluate Lit Capacity Credits |
## Proposal
## 3. Draft architecture and design plan for the Privacy Layer
**What it is**
Create a technical blueprint for private uploads, access control, revocation, and encryption.
**What we want to answer**
- How does a private file move through our stack?
- Who handles encryption and access enforcement?
- What trade-offs are we making?
## 3.1. How does a private file move through our stack?
1. **Upload Flow**
- Files are encrypted client-side using `AES-256-CBC` (with a planned upgrade to `AES-GCM` if we can me it work with streams)
- The encryption key is encrypted using Lit Protocol with Access Control Conditions
- Encrypted file and metadata are uploaded to Storacha
- Private files skip Filecoin/IPFS/IPNI publication to maintain privacy
- A redundant encrypted copy is stored in R2/S3 for durability
2. **Storage Flow**
- Private files are not published to public discovery networks
- Access is enforced at the gateway level using access tokens and delegation proofs (TODO)
- Files are served via an access-controlled gateway
- Egress accounting and billing is tied to space ownership
## 3.2. Who handles encryption and access enforcement?
1. **Encryption**
- Client-side encryption using `cryptoAdapter` (Node.js crypto API)
- Lit Protocol handles key encryption of the simmetric and access control
- Storacha handles encrypted file storage and metadata
2. **Access Enforcement**
- Gateway enforces access using UCAN delegations with `space/content/serve/*` capability
- Lit Actions validate access conditions and decrypt symmetric keys
- Space identity is resolved and checked against delegations
- Access tokens provide short-lived or scoped access (TODO)
## 3.3. What trade-offs are we making?
1. **Performance vs Security**
- Client-side encryption ensures data privacy but is up to the users preserve their keys
- Lit Protocol integration adds complexity (PKP setup, Capacity Credits, etc) but provides decentralized key management with great user experience
- Centralized KMS would be safer but potentially more expensive depending on the number of keys
2. **Recovery Options**
- Self-sovereign recovery (PKPs, DIDs) vs centralized cloud KMS
- Multiple verification methods increase security but add complexity (up to 5 rotation keys in `did:plc`)
- Recovery PKPs provide backup access but require additional setup
3. **Storage Trade-offs**
- Private files skip Filecoin/IPFS for privacy but lose redundancy
- We will need R2/S3 backup provides durability but adds cost
- Gateway-level access control provides security but adds complexity (delegations + access tokens + billing)
4. **Identity Management**
- `did:plc` adoption provides stable identity but requires additional changes to make our systems compatible with the new method
- Removal of `did:email` requires a large refactor to link accounts with Stripe customers, but simplifies identity and uses a standard method
- Multiple verification methods vs single key simplicity (we need to identify if we want a key per user / per space / per file)
## 4. Define user experience expectations
**What it is**
Describe what users should see/do when interacting with privacy features in Console and CLI.
**What we want to answer**
- How do users create, access, share, and recover private files?
- What UX should be default vs optional?
- What’s the minimum viable experience?
## 4.1. How do users create, access, share, and recover private files?
**Console App Experience**
1. **Space/Bucket Creation**
- Users can choose privacy mode (public/private) during bucket/space creation
- Private is the default setting (but depends on the PKP setup)
- Clear UI indicators for private vs public spaces
2. **File Management**
- Upload private files with clear, human-readable file names
- How we can easily show and preserve the file name? (always upload the file wrapped in a folder?)
- Use tags to organize and categorize files
- TDB: not sure how we can preserve that info, maybe if we always wrap the file in a folder, we can put a metadata dag-json file in the same folder, and it will contain tags, categories, name, etc.
- View file names even for private files for better usability
- Related to the previous item
- Delete files when needed
3. **Sharing**
- Share files with friends via delegated access
- Revoke access after sharing if needed
- Clear indicators of who has access to files
- TBD: where and how we track who has shared what
## 4.2. What UX should be default vs optional?
**Default**
1. **Privacy**
- Private mode as default for new spaces (users need to have their PKP's configured)
- Client-side encryption enabled by default
- Clear file names visible by default (how we can make it happen?)
2. **Recovery**
- Default identity using `did:plc`
- Option to add recovery accounts (up to 5 rotation keys per user `did:plc`)
3. **Delete**
- Users should be able to delete a file
4. **Sharing & Revocation**
- Users should be able to share and revoke access to files and spaces
**Optional**
1. **Advanced Sharing**
- Custom access tokens
- Granular delegation and revocation options
## 4.3. What's the minimum viable experience?
**Requirements**
1. **Basic Commands**
- Encryption enabled by default (option to disable it)
- If enabled, content is not published to public distribution networks
- Recovery options
- User can setup a recovery key using social credential, authn, stytch, etc
- Revocation commands
- User can revoke a delegation
- Deletion capabilities
- User can delete an encrypted content
**Core User Experience**
1. **Must Have**
- User-first UX
- High performance
- No risk of losing access
- File/Space sharing via delegation
- Clear key management boundaries (creation, recovery, rotation, updates, delegation)
2. **Priority Order**
1. Access & Recovery
2. Performance
3. Self-sovereignty
## 5. Define performance, cost, and resilience implications
**What it is**
Evaluate how the existing/proposed designs affect speed, cost, and system reliability.
**What we want to answer**
- What is the cost of a centralized solution vs Lit?
- How does each model handle failures?
- Does encryption or validation degrade performance?
## 5.1. What is the cost of a centralized solution vs Lit?
**Lit Protocol Costs**
- Users are NOT required to control an EOA/Ethereum Wallet
- Decryption happens in Lit Action which validates the `space/content/decrypt` capability
- The wallet is configured just to cover Lit Network fees
- Intent is to get an agreement with Lit team to cover capacity credits for users for a given period
- Once the symmetric key is decrypted in the Lit Action, decryption happens client-side
- :warning: TODO: can we estimate how much we will spend with one end to end operation? e.g: Signing with PKP, Execute the Lit Action and Decrypt (these 3 ops are paid with Capacity Credits).
- :warning: TODO: How many PKPs we can mint per user?
- What ff we decide to create 2 PKPs per space (1 primary and 1 for recovery) is it possible? The `did:plc` has a limitation of 5 rotation keys per did document, so we probably would need to create did docs per space!?
**Centralized KMS Costs**
- Would require cloud KMS service (e.g., AWS KMS, Google Cloud KMS)
- Costs typically include:
- Per-key storage
- API calls for encryption/decryption
- Key rotation operations
- Additional infrastructure for high availability
**Access Pattern Assumptions**
- Each user has 1 key
- Each user decrypts files twice per week
- Monthly calculations: 8-9 decryption operations per user per month
- Additional operations: key creation, rotation, etc.
**AWS KMS Per Key Costs**
- Key storage: $1/month per key (:warning: charges per stored key)
- Free tier: 20,000 requests/month
- After free tier: $0.03 per 10,000 requests
- [Source: AWS KMS Pricing](https://aws.amazon.com/kms/pricing/)
**Monthly Cost Breakdown**
| Users | Keys | Decryption Ops/Month | Cost Calculation | Total Cost |
|-------|------|---------------------|------------------|------------|
| 1 | 1 | 8 | $1 (key) + $0 (free tier) | $1.00 |
| 1,000 | 1,000 | 8,000 | $1,000 (keys) + $0 (free tier) | $1,000.00 |
| 10,000 | 10,000 | 80,000 | $10,000 (keys) + $0.24 (ops) | $10,000.24 |
| 100,000 | 100,000 | 800,000 | $100,000 (keys) + $2.34 (ops) | $100,002.34 |
| 500,000 | 500,000 | 4,000,000 | $500,000 (keys) + $11.94 (ops) | $500,011.94 |
| 1,000,000 | 1,000,000 | 8,000,000 | $1,000,000 (keys) + $23.94 (ops) | $1,000,023.94 |
**Google KMS Per Key Costs**
- Key version: $0.06 per version
- Operations: $0.03 per 10,000 operations
- Assumes 1 version per key
- [Source: Google KMS Pricing](https://cloud.google.com/kms/pricing)
**Monthly Cost Breakdown**
| Users | Keys | Decryption Ops/Month | Cost Calculation | Total Cost |
|-------|------|---------------------|------------------|------------|
| 1 | 1 | 8 | $0.06 (key) + $0 (ops) | $0.06 |
| 1,000 | 1,000 | 8,000 | $60 (keys) + $0.024 (ops) | $60.02 |
| 10,000 | 10,000 | 80,000 | $600 (keys) + $0.24 (ops) | $600.24 |
| 100,000 | 100,000 | 800,000 | $6,000 (keys) + $2.40 (ops) | $6,002.40 |
| 500,000 | 500,000 | 4,000,000 | $30,000 (keys) + $12.00 (ops) | $30,012.00 |
| 1,000,000 | 1,000,000 | 8,000,000 | $60,000 (keys) + $24.00 (ops) | $60,024.00 |
## 5.2. How does each model handle failures?
TODO
## 5.3. Does encryption or validation degrade performance?
TODO
## 6. Map out recovery and key management strategies
**What it is**
Analyze key lifecycle: creation, storage, recovery, and rotation.
**What we want to answer**
- Who generates and stores keys?
- What recovery options do we offer?
- Can we support multiple strategies?
## 6.1. Who generates and stores keys?
1. **Client-Side Generation**
- Symmetric keys for file encryption generated client-side
- PKPs (Programmable Key Pairs) minted through Lit Protocol
- `did:plc` identities imported or created during user registration on the client side
- Public Keys stored in `did:plc` document under `verificationMethods`
2. **Key Storage**
- Primary Public PKP stored in `did:plc` document as rotation key
- Recovery Public PKP stored in `did:plc` document as rotation key
- Symmetric keys encrypted and stored with file metadata in Storacha
- Space DID remains as root authority
## 6.2. What recovery options do we offer?
**Lit Recovery Options**
1. **Primary PKP Recovery**
- Social authentication for primary PKP
- Direct access to encrypted files
- No additional setup needed
2. **Recovery PKP**
- Separate social authentication
- Minted during initial setup
- Stored in `did:plc` document
- Can be used if primary PKP is lost
3. **Key Rotation**
- Through `did:plc` rotation keys
- Can update verification methods
- Maintains access to existing files
**Cloud KMS Recovery Options**
1. **Key Backup**
- Encrypted key backup
- Stored in secure cloud storage
- Password-protected access
2. **Admin Recovery**
- Through KMS admin console
- Audit trail of recovery actions
- Role-based access control
3. **Key Rotation**
- Automated key rotation
- Version management
- Backup of previous versions
## 6.3. Can we support multiple strategies?
**Hybrid Approach**
- Decentralized: Lit PKPs for primary access
- Key generated on the client side, encrypted and stored in Storacha as file metadata
- Semi-centralized: Cloud KMS for fallback
- store the recovery simmetric key (it needs to be done on the server side)
- Self-sovereign: Mnemonic/JSON backup
- generate the recovery keys on the client side, and then the user stores the copy of the mnemonic/json backup data (same as Bsky app?)
**Strategy Comparison**
| Strategy | Pros | Cons |
|----------|------|------|
| **Lit PKPs** | - Decentralized<br>- Social auth integration<br>- Key rotation support | - Lit network dependency<br>- Higher latency (decrypt) <br>- More complex UX (PKP) |
| **Cloud KMS** | - Faster performance<br>- Simpler implementation<br>- Reliable backup | - Centralized control<br>- Higher costs at scale<br>- Less self-sovereign |
| **Hybrid** | - Best of both worlds<br>- Multiple recovery paths<br>- Flexible for users? | - More complex to implement<br>- Higher maintenance<br>- Multiple systems to manage |
## 7. Security: Perform a lightweight threat model
**What it is**
Identify the most likely attack scenarios and how to mitigate them.
**What we want to answer**
- Where are the vulnerabilities?
- What could go wrong with access or revocation?
- What minimal security guarantees must we offer?
**Subtasks**
- List threats (e.g. replay, key loss, revocation failure)
- Rate each by likelihood and impact
- Propose simple mitigations, if any
- Document assumptions and unresolved risks (important)
## 8. Review & Align: Internal review of the proposal across teams
**What it is**
Get alignment and feedback from engineering and product teams. (Should we sync with a security specialist?)
**What we want to answer**
- Does the plan cover key use cases and risks?
- Are we aligned on implementation scope and phasing?
- What concerns or objections remain?
## Additional Resources
- [GitHub Parent Ticket](https://github.com/storacha/project-tracking/issues/407)
- [ACLs for large downloads (UCAN RFC)](https://github.com/storacha/RFC/blob/rfc/resourceful-ucan/rfc/resourceful-ucan.md)
## TODO
- Lit max PKPs we can mint per user?
- Lit capacity credit cost estimative
- KMS: why not?
- ACLs + token access