[DRAFT] feat: Add blob storage foundation #3824
[DRAFT] feat: Add blob storage foundation #38240vertake wants to merge 1 commit intosuperplanehq:mainfrom
Conversation
0vertake
commented
Mar 30, 2026
- Define BlobStorage interface with in-memory and filesystem backends
- Add blob metadata model and DB migration
- Add StoreBlob, ListBlobs, DescribeBlob, DeleteBlob API endpoints
- Wire blob authorization rules
- Expose BlobContext to component execution context
- Add blob UI panel across org, canvas, node, and execution scopes
- Regenerate proto, OpenAPI spec, and TypeScript/Go SDK clients
- Define BlobStorage interface with in-memory and filesystem backends - Add blob metadata model and DB migration - Add StoreBlob, ListBlobs, DescribeBlob, DeleteBlob API endpoints - Wire blob authorization rules - Expose BlobContext to component execution context - Add blob UI panel across org, canvas, node, and execution scopes - Regenerate proto, OpenAPI spec, and TypeScript/Go SDK clients Signed-off-by: Milos Jovanovic <milosjovanovic519@gmail.com> Made-with: Cursor
|
👋 Commands for maintainers:
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| default: | ||
| return "", status.Errorf(codes.InvalidArgument, "invalid blob scope type: %s", scopeType) | ||
| } | ||
| } |
There was a problem hiding this comment.
Organization scope object key missing org ID causes cross-tenant collision
High Severity
scopeObjectKey generates blobs/organization/{path} for organization scope without including the organization ID. Since object_key has a global unique constraint (idx_blobs_object_key), two different organizations storing a blob at the same path will collide — the second org gets an AlreadyExists error, and worse, the first org's blob content gets silently overwritten in storage. The function doesn't accept an organization ID parameter, and the caller in StoreBlob doesn't pass one. Other scopes avoid this because they embed globally-unique UUIDs (canvas, execution).
Additional Locations (1)
| } | ||
| _ = store.Delete(ctx, objectKey) | ||
| return nil, status.Errorf(codes.Internal, "failed to store blob metadata: %v", err) | ||
| } |
There was a problem hiding this comment.
StoreBlob overwrites existing content before DB duplicate check
High Severity
store.Put at line 95 writes new content to blob storage, then models.CreateBlob at line 126 attempts the DB insert. If the insert fails with a duplicate object_key constraint, the handler returns AlreadyExists at line 128 without cleanup. At that point the original blob's content in storage has already been silently overwritten, while the original DB record still exists pointing to the now-corrupted data. The store.Delete cleanup on line 130 only runs for non-duplicate errors.

