Blob Sharing API Documentation¶
Overview¶
The Blob Sharing API enables secure sharing of blobs between users with granular access control. Each user has a unique Public User ID generated from their device name, ensuring that one party cannot have both keys.
Key Concepts¶
Public User ID¶
- Generated from device name using a hash function
- Format:
user_<hash>(e.g.,user_abc123def456) - Unique per device/user
- Used for sharing instead of exposing API keys
Access Levels¶
read_only: User can only read the blobread_write: User can read and modify the blob
Security Features¶
✅ One party cannot have both keys - Enforced by user_id system
✅ Hash ring consistency - All operations use the same server
✅ Server-side enforcement - Access control managed by Rust server
✅ Cannot share with yourself - Prevented by server
API Reference¶
BlobStorage.get_user_id()¶
Get your Public User ID.
def get_user_id(server: Optional[str] = None) -> Optional[str]
Parameters:
- server (Optional[str]): Specific server to get user_id from, or None for first available
Returns:
- User ID string (e.g., 'user_abc123') or None
Example:
storage = BlobStorage(servers=['http://localhost:3000'])
my_user_id = storage.get_user_id()
print(f"My User ID: {my_user_id}")
BlobStorage.share_blob()¶
Share a blob with another user.
def share_blob(
blob_id: str,
target_user_id: str,
access_level: str = 'read_only'
) -> Dict
Parameters:
- blob_id (str): The blob to share
- target_user_id (str): Public User ID of the target user
- access_level (str): 'read_only' or 'read_write'
Returns:
- Dict with share information:
- blob_id: The shared blob
- user_id: Target user ID
- access_level: Access level granted
- granted_at: Unix timestamp
Raises:
- ValueError: Invalid access_level
- HTTPError: Server error (e.g., cannot share with yourself, target user doesn't exist)
Example:
# Share config file (read-only)
storage.share_blob('app/config.json', 'user_abc123', 'read_only')
# Share data file (read-write)
storage.share_blob('app/data.json', 'user_xyz789', 'read_write')
BlobStorage.revoke_share()¶
Revoke share access for a user.
def revoke_share(blob_id: str, target_user_id: str) -> bool
Parameters:
- blob_id (str): The blob to revoke access from
- target_user_id (str): Public User ID to revoke
Returns:
- True if successful, False otherwise
Example:
storage.revoke_share('app/config.json', 'user_abc123')
BlobStorage.list_shares()¶
List all users who have access to a blob.
def list_shares(blob_id: str) -> List[Dict]
Parameters:
- blob_id (str): The blob to list shares for
Returns:
- List of dicts with:
- user_id: Public User ID
- access_level: 'read_only' or 'read_write'
- granted_by: User ID who granted access
- granted_at: Unix timestamp
Example:
shares = storage.list_shares('app/config.json')
for share in shares:
print(f"{share['user_id']}: {share['access_level']}")
Server API Endpoints¶
POST /keys¶
Create API key with device name.
Request:
{
"device_name": "my-laptop"
}
Response:
{
"key": "uuid-api-key",
"user_id": "user_abc123def456"
}
POST /share/:blob_id¶
Share a blob with a user.
Headers:
- x-api-key: Your API key
Request:
{
"user_id": "user_xyz789",
"access_level": "read_only"
}
Response:
{
"blob_id": "app/config.json",
"user_id": "user_xyz789",
"access_level": "read_only",
"granted_at": 1234567890
}
GET /share/:blob_id¶
List all shares for a blob.
Headers:
- x-api-key: Your API key
Response:
{
"blob_id": "app/config.json",
"shares": [
{
"user_id": "user_xyz789",
"access_level": "read_only",
"granted_by": "user_abc123",
"granted_at": 1234567890
}
]
}
DELETE /share/:blob_id/:user_id¶
Revoke share for a user.
Headers:
- x-api-key: Your API key
Response:
- 204 No Content on success
Usage Examples¶
Example 1: Share Config File (Read-Only)¶
# User A
storage_a = BlobStorage(servers=['http://localhost:3000'])
user_a_id = storage_a.get_user_id()
# Create config
with BlobFile('app/config.json', 'w', storage=storage_a) as f:
f.write_json({'theme': 'dark'})
# User B
storage_b = BlobStorage(servers=['http://localhost:3000'])
user_b_id = storage_b.get_user_id()
# Share with User B
storage_a.share_blob('app/config.json', user_b_id, 'read_only')
# User B can read
with BlobFile('app/config.json', 'r', storage=storage_b) as f:
config = f.read_json() # ✅ Works
# User B cannot write
with BlobFile('app/config.json', 'w', storage=storage_b) as f:
f.write_json({'modified': True}) # ❌ 403 Forbidden
Example 2: Collaborative Editing (Read-Write)¶
# User A creates document
with BlobFile('collab/doc.json', 'w', storage=storage_a) as f:
f.write_json({'content': 'Initial'})
# Share with User B (read-write)
storage_a.share_blob('collab/doc.json', user_b_id, 'read_write')
# User B can edit
with BlobFile('collab/doc.json', 'r', storage=storage_b) as f:
doc = f.read_json()
doc['content'] += '\nEdited by B'
with BlobFile('collab/doc.json', 'w', storage=storage_b) as f:
f.write_json(doc) # ✅ Works
Security Considerations¶
- User ID Generation: Based on device hostname, ensure unique device names
- API Key Storage: Keys are encrypted with device key
- Access Control: Enforced server-side, cannot be bypassed
- Hash Ring: Ensures all operations for a blob use the same server
- One Party Rule: User IDs prevent one party from having both keys
Error Handling¶
| Error | Status | Meaning |
|---|---|---|
401 Unauthorized |
401 | Invalid or missing API key |
403 Forbidden |
403 | Insufficient permissions (e.g., read-only trying to write) |
404 Not Found |
404 | Blob or user doesn't exist |
400 Bad Request |
400 | Invalid request (e.g., sharing with yourself) |