Skip to content

Conversation

@randygrok
Copy link
Contributor

@randygrok randygrok commented Nov 28, 2025

This will be CLOSED and proposed as small PRs.

Please check

Part 1: #2905

Overview

- Introduced a new `fetchBlobs` function to streamline the process of retrieving blobs from the DA client.
- Replaced direct calls to `client.DA.GetIDs` and `client.DA.Get` with the new `fetchBlobs` function to handle errors more gracefully.
- Added custom error types for better clarity when blobs are not found or when a height is in the future.
- Updated blob processing logic to work with the new batch structure, improving efficiency and readability.
- Modified tests to reflect changes in ID creation and splitting, ensuring compatibility with the new blob handling logic.
@github-actions
Copy link
Contributor

github-actions bot commented Nov 28, 2025

The latest Buf updates on your PR. Results from workflow CI / buf-check (pull_request).

BuildFormatLintBreakingUpdated (UTC)
✅ passed⏩ skipped✅ passed✅ passedDec 3, 2025, 4:28 PM

@claude
Copy link
Contributor

claude bot commented Nov 28, 2025

Claude finished @randygrok's task —— View job


Comprehensive Code Review Complete

I've performed a thorough review of the blob API migration PR. This is a significant architectural refactoring that removes the DA interface layer and migrates to a more direct Blob API approach. Below is my detailed analysis:


🏗️ 1. Code Quality & Architecture

Strengths

  1. Clean Package Structure: The new pkg/blob package provides a well-isolated implementation that mirrors celestia-node's API surface
  2. Clear Abstraction: The BlobAPI interface in block/internal/da/client.go:42-47 is minimal and focused
  3. Effective Use of Generics: The submitToDA function (da_submitter.go:353) uses generics well for type-safe batch processing
  4. Good Separation of Concerns: JSON-RPC transport layer properly separated from business logic

⚠️ Concerns

Critical: Interface Proliferation (addresses reviewer comment)

  • Location: sequencers/single/sequencer.go:26-30, da/jsonrpc/server.go:17-23, block/internal/da/client.go:42-47
  • Issue: Multiple similar BlobAPI interfaces scattered across packages violates DRY principle
  • Impact: Maintenance burden, inconsistency risk, harder to mock/test
  • Recommendation: Define a single canonical BlobAPI interface in pkg/blob or core and have all implementations reference it
// Suggested location: pkg/blob/api.go
type API interface {
    Submit(ctx context.Context, blobs []*Blob, opts *SubmitOptions) (uint64, error)
    GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*Blob, error)
    GetProof(ctx context.Context, height uint64, namespace share.Namespace, commitment Commitment) (*Proof, error)
    Included(ctx context.Context, height uint64, namespace share.Namespace, proof *Proof, commitment Commitment) (bool, error)
}

Redundant JSON Marshaling (addresses reviewer comment at pkg/blob/submit_options.go:61)

  • Location: pkg/blob/submit_options.go:27-61
  • Issue: Custom MarshalJSON/UnmarshalJSON implementations are unnecessary when field names/types match exactly
  • Problem: The jsonSubmitOptions type is identical to SubmitOptions - this is 35 lines of redundant code
  • Recommendation: Remove custom marshaling methods and rely on default JSON encoding. If you need to control output, use struct tags only.

Missing Documentation (addresses reviewer comment at pkg/blob/blob.go:1)

  • Location: pkg/blob/blob.go:1
  • Issue: No package-level documentation explaining this is a copy of celestia-node code and why
  • Recommendation: Add comprehensive package documentation:
// Package blob provides a minimal copy of celestia-node's blob types and operations.
//
// This package intentionally duplicates code from celestia-node to:
// 1. Maintain JSON wire compatibility with celestia-node's RPC API
// 2. Avoid dependency on the full celestia-node stack
// 3. Support future divergence if needed
//
// When updating: Track changes from celestia-node's blob package via
// periodic comparison or CI checks to ensure compatibility.
package blob

Namespace Handling Inconsistency

  • Location: pkg/namespace/namespace.go vs github.com/celestiaorg/go-square/v3/share
  • Issue: Custom namespace implementation alongside celestia's share.Namespace creates confusion
  • Impact: Conversion overhead, potential bugs from mismatched implementations
  • Recommendation: Document clearly when to use which, or consolidate to use only celestia's implementation

🔒 2. Error Handling & Resilience

Strengths

  1. Comprehensive Error Classification: block/internal/da/client.go:156-170 properly categorizes errors by status code
  2. Context Cancellation Handling: Proper context.Canceled detection in da_submitter.go:485-489
  3. Retry Logic: Sophisticated retry policy with exponential backoff in da_submitter.go:62-79

⚠️ Concerns

String-Based Error Matching

  • Location: block/internal/da/client.go:160-169, client.go:234-253
  • Issue: Using strings.Contains() for error detection is fragile and error-prone
  • Problem: RPC wrapping, error message changes, localization all break detection
  • Recommendation: Use errors.Is() or typed error sentinels:
// Instead of:
if strings.Contains(err.Error(), datypes.ErrTxTimedOut.Error()) {

// Do:
if errors.Is(err, datypes.ErrTxTimedOut) {

Integer Overflow Check Could Be Simpler

  • Location: block/internal/da/client.go:100-108
  • Issue: Manual overflow detection with math.MaxUint64-blobSize
  • Recommendation: Use blobSize > math.MaxUint64 - uint64(len(b)) for clarity, or just check blobSize + uint64(len(b)) < blobSize (simpler overflow detection)

Silent Failure in SplitID

  • Location: pkg/blob/blob.go:142-149
  • Issue: Returns (0, nil) for malformed IDs without error
  • Problem: Caller can't distinguish between "height 0" and "invalid ID"
  • Recommendation: Return error for malformed input

🔐 3. Security

Strengths

  1. No Credential Logging: Tokens and keys not logged in visible code
  2. Authorization Header: Proper Bearer token usage in da/jsonrpc/client.go:91
  3. Input Validation: Namespace validation in pkg/blob/blob.go:49

⚠️ Concerns

No Rate Limiting on GetAll (addresses reviewer comment at block/internal/da/client.go:231)

  • Location: block/internal/da/client.go:231
  • Issue: No bounds on number of blobs returned from GetAll
  • Problem: With 10k blobs, could cause memory exhaustion or crash
  • Recommendation: Add pagination or max blob count:
func (c *client) GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*blob.Blob, error) {
    blobs, err := c.blobClient.GetAll(ctx, height, namespaces)
    if err != nil {
        return nil, err
    }
    
    const maxBlobs = 1000
    if len(blobs) > maxBlobs {
        c.logger.Warn().Int("count", len(blobs)).Msg("Too many blobs returned, truncating")
        return blobs[:maxBlobs], fmt.Errorf("too many blobs: %d (max %d)", len(blobs), maxBlobs)
    }
    return blobs, nil
}

HTTP Server Security Headers Missing

  • Location: da/jsonrpc/server.go:73-78
  • Issue: ReadHeaderTimeout: 2s is good, but missing other security headers
  • Recommendation: Add WriteTimeout, IdleTimeout, and consider TLS support

4. Performance & Resource Efficiency

Strengths

  1. Semaphore-Based Concurrency Control: da_submitter.go:543 limits to 32 workers
  2. Batch Size Limiting: limitBatchBySize prevents oversized submissions
  3. Memory Bound Visualization: da_visualization.go:86-88 keeps only 100 recent submissions

⚠️ Concerns

Wait Group Pattern Change (addresses reviewer comments at da_submitter.go:549-580)

  • Location: block/internal/submitting/da_submitter.go:549-586
  • Issue: The wait group usage has changed from previous implementation
  • Current Code: Uses defer wg.Wait() at line 580, which waits for all goroutines before collecting all results
  • Problem: The defer wg.Wait() means we wait for ALL goroutines to complete even after early cancellation
  • Recommendation: The early exit on error (line 556-558, 564-571) is good, but the deferred wait means we block unnecessarily. Consider:
// Remove defer wg.Wait() and use:
go func() {
    wg.Wait()
    close(resultCh)
}()

// Then read from resultCh in a loop:
for i := 0; i < len(items); i++ {
    if err := <-resultCh; err != nil {
        cancel() // Signal other goroutines
        // Drain remaining results
        go func() {
            for range resultCh {}
        }()
        return nil, err
    }
}

Docker Build Optimization Issue (addresses reviewer comment at apps/evm/Dockerfile:7)

  • Location: apps/evm/Dockerfile:6-8
  • Issue: Copying core and da before go mod download defeats layer caching
  • Problem: Every change to core/da code invalidates the mod download cache, slowing builds significantly
  • Current:
COPY go.mod go.sum ./
COPY core core      # ❌ Copied before mod download
COPY da da          # ❌ Copied before mod download  
RUN go mod download
COPY . .            # ❌ core and da copied again
  • Recommendation: Remove lines 6-7 since line 10 copies everything anyway, or better yet:
COPY go.mod go.sum ./
RUN go mod download   # This layer only invalidates when go.mod/go.sum change
COPY . .              # Code changes don't invalidate mod download

Concurrent Marshal Goroutine Explosion

  • Location: da_submitter.go:549-577
  • Issue: Creates one goroutine per item even for small batches
  • Problem: For 2-3 items, goroutine overhead exceeds benefit
  • Recommendation: Only parallelize for batches > 10 items

🧪 5. Testing & Reliability

Strengths

  1. Mock Implementation: LocalBlobAPI in local_blob_api.go is clean and testable
  2. Table-Driven Tests: client_test.go:42-69 properly tests error mapping
  3. Round-Trip Testing: blob_test.go:18-34 validates JSON serialization

⚠️ Concerns

Test File Naming Issue (addresses reviewer comment at block/internal/da/local_blob_api.go:1)

  • Location: block/internal/da/local_blob_api.go:1
  • Issue: Test-only code not marked as test file
  • Problem: Included in production builds unnecessarily
  • Recommendation: Rename to local_blob_api_test.go or add build tag //go:build testing

Coverage Drop

  • Issue: Patch coverage is 41.83%, down from 64.81% to 57.88%
  • Files with Poor Coverage:
    • block/internal/da/client.go: 54.47% (50 missing, 6 partials)
    • tools/da-debug/main.go: 3.57% (53 missing)
    • block/internal/da/local_blob_api.go: 0.00% (35 missing)
    • pkg/blob/submit_options.go: 0.00% (26 missing)
  • Recommendation: Add tests for:
    1. Error edge cases in client.go
    2. The redundant marshal functions (or remove them)
    3. LocalBlobAPI methods

Missing Test for Retry Logic

  • Location: da_submitter_retry_test.go was added but coverage still low
  • Recommendation: Add tests for:
    • Partial success scenarios
    • Batch halving on "too big" errors
    • Context cancellation during retry

File Deletion Issues (addresses reviewer comments)

Several files deleted in PR that may need to be reverted:

  • pkg/rpc/server/templates/da_visualization.html:183 - mistaken deletion
  • pkg/rpc/server/da_visualization.go:176 - needs to be reverted
  • pkg/rpc/server/da_visualization_non_aggregator_test.go - deleted, should revert
  • pkg/rpc/server/da_visualization_test.go - deleted, should revert

🔧 6. Documentation & Developer Experience

Strengths

  1. Clear Function Names: RetrieveHeaders vs RetrieveData are self-documenting
  2. Godoc Comments: Most exported types have documentation
  3. CLAUDE.md Updated: Project documentation reflects the new BlobAPI

⚠️ Concerns

Missing Godoc on Functions (addresses reviewer comment at da/jsonrpc/client.go:33)

  • Location: da/jsonrpc/client.go:33
  • Issue: Godoc should start with the function name "Submit"
  • Current: // BlobAPI exposes the methods needed...
  • Should be: // Submit submits blobs to the blob namespace and returns the height...

Subscribe Method Missing (addresses reviewer comments)

  • Locations:
    • block/internal/da/client.go:47
    • da/jsonrpc/client.go:16
  • Issue: Reviewers requested Subscribe method for staying at head of chain
  • Recommendation: Add subscription API to interface:
type BlobAPI interface {
    Submit(ctx context.Context, blobs []*blob.Blob, opts *SubmitOptions) (uint64, error)
    GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*Blob, error)
    GetProof(ctx context.Context, height uint64, namespace share.Namespace, commitment Commitment) (*Proof, error)
    Included(ctx context.Context, height uint64, namespace share.Namespace, proof *Proof, commitment Commitment) (bool, error)
    
    // Subscribe returns a channel of new blobs for the given namespace
    Subscribe(ctx context.Context, namespace share.Namespace) (<-chan *Blob, error)
}

Merge Conflict Artifacts (addresses reviewer comment)

  • Locations: Various test files
  • Issue: test/e2e/evm_test_common.go:2 and others have leftover merge artifacts
  • Recommendation: Clean up commented-out imports and code

📊 Summary & Recommendations

Must Fix Before Merge

  1. Consolidate BlobAPI interfaces - Define once in pkg/blob, reference everywhere
  2. Remove redundant JSON marshaling in submit_options.go
  3. Fix Docker build layer caching - Remove premature core/da copy
  4. Revert mistaken deletions in da_visualization files
  5. Add package documentation to pkg/blob explaining it's a celestia-node copy
  6. Replace string-based error matching with errors.Is()
  7. Mark test-only code properly - Rename local_blob_api.go or add build tag
  8. Clean up merge conflict artifacts in test files

Should Fix

  1. Add rate limiting to GetAll to prevent memory exhaustion
  2. Improve wait group pattern in marshalItems to avoid unnecessary blocking
  3. Add Subscribe method to BlobAPI for future optimization
  4. Improve test coverage - aim for >60% on new code

Nice to Have

  1. Add CI job to track celestia-node blob package changes
  2. Add metrics for blob size distribution
  3. Consider pagination for large blob retrievals
  4. Add integration test for concurrent submitters with multiple signing addresses

🎯 Overall Assessment

This is a solid refactoring that simplifies the architecture by removing an abstraction layer. The core logic is sound, but there are several issues that should be addressed:

  • Architecture: Good direction, but interface proliferation needs cleanup
  • Code Quality: Generally high, with some redundancy to remove
  • Testing: Coverage drop is concerning and needs attention
  • Performance: Good patterns, but Docker build and wait group issues should be fixed
  • Security: Mostly solid, but needs rate limiting

Recommendation: Address the "Must Fix" items before merging. The "Should Fix" items can be follow-up PRs if time is constrained, but they're important for production readiness.


@github-actions
Copy link
Contributor

github-actions bot commented Nov 28, 2025

PR Preview Action v1.6.3

🚀 View preview at
https://evstack.github.io/docs-preview/pr-2893/

Built to branch main at 2025-11-28 17:18 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@randygrok randygrok changed the title reafactor: migrate to blob api and remove da interface (approach 2) refactor: migrate to blob api and remove da interface (approach 2) Nov 28, 2025
Full nodes syncing from DA were not persisting the DAIncludedHeightKey
metadata, causing the E2E test to fail when querying this value.

The submitter (sequencer) already persists this key when verifying DA
inclusion. This change adds the same logic to the syncer so full nodes
also track which blocks have been DA included.
The LocalDA's GetAll method was missing the height-from-future check
that existed in the old GetIDs method. This caused the syncer to
iterate indefinitely instead of backing off when caught up with DA.

Also simplified IsHeightDAIncluded by removing unused variable.

// API defines the jsonrpc service module API
// API exposes the blob RPC methods used by the node.
type API struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we able to use subscribe here?

@codecov
Copy link

codecov bot commented Nov 28, 2025

Codecov Report

❌ Patch coverage is 40.22140% with 324 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.70%. Comparing base (f1e677d) to head (d1e2288).

Files with missing lines Patch % Lines
block/internal/da/client.go 47.51% 65 Missing and 9 partials ⚠️
tools/da-debug/main.go 3.57% 53 Missing and 1 partial ⚠️
block/internal/da/local_blob_api.go 0.00% 35 Missing ⚠️
da/jsonrpc/client.go 0.00% 27 Missing ⚠️
pkg/blob/submit_options.go 0.00% 26 Missing ⚠️
block/public.go 0.00% 22 Missing ⚠️
da/jsonrpc/server.go 0.00% 20 Missing ⚠️
pkg/blob/blob.go 73.52% 11 Missing and 7 partials ⚠️
pkg/rpc/server/da_visualization.go 0.00% 18 Missing ⚠️
block/internal/submitting/da_submitter.go 51.85% 12 Missing and 1 partial ⚠️
... and 7 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2893      +/-   ##
==========================================
- Coverage   64.53%   57.70%   -6.83%     
==========================================
  Files          81       81              
  Lines        7370     7339      -31     
==========================================
- Hits         4756     4235     -521     
- Misses       2072     2583     +511     
+ Partials      542      521      -21     
Flag Coverage Δ
combined 57.70% <40.22%> (-6.83%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Fix broken markdown links by correcting the relative path depth
from ../../../ to ../../ for linking to execution/grpc and
sequencers/single READMEs.
@randygrok randygrok marked this pull request as ready for review December 2, 2025 09:10
Copy link
Contributor

@alpe alpe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work.
I added some comments to the changes in the Docker file and about the wait group. Otherwise LGTM 👍


COPY go.mod go.sum ./
COPY core core
COPY da da
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you copy core and da here? they are copied in line 10.
Go mods are normally copied as a separate step before the code to optimize build time as they usually do not change as often as the code. This improves build time significantly.
To copy core and da before running go mod works against this

defer wg.Done()

select {
case <-ctx.Done():
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good fix to exit early and release resources. This may was blocking before. 🚀

bz, err := marshalFn(itm)
if err != nil {
resultCh <- fmt.Errorf("failed to marshal %s item at index %d: %w", itemType, idx, err)
cancel()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an optimization which should not be needed without the wg.Wait.

}

// Wait for all goroutines to complete and check for errors
defer wg.Wait()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you add the wait group? The code below was waiting for all Go routines to land on the happy path and exited early on failures.

if strings.Contains(err.Error(), context.Canceled.Error()) {
api.Logger.Debug().Str("method", "GetIDs").Msg("RPC call canceled due to context cancellation")
return res, context.Canceled
// BlobAPI exposes the methods needed by block/internal/da.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Godoc should start with Submit

}
*cfg = SubmitOptions(j)
return nil
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you implement Un/MarshalJSON ? This is a lot of code when the field names/types are equal.

Comment on lines 42 to 47
type BlobAPI interface {
Submit(ctx context.Context, blobs []*blob.Blob, opts *blob.SubmitOptions) (uint64, error)
GetAll(ctx context.Context, height uint64, namespaces []share.Namespace) ([]*blob.Blob, error)
GetProof(ctx context.Context, height uint64, namespace share.Namespace, commitment blob.Commitment) (*blob.Proof, error)
Included(ctx context.Context, height uint64, namespace share.Namespace, proof *blob.Proof, commitment blob.Commitment) (bool, error)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add subscribe here? ideally we can use subscribe to stay at the head of the chain instead of doing gets on heights. The integration into syncing can come later, but we should have the implementation here

@@ -0,0 +1,154 @@
package blob
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add docs on this is a copy of the code in node and why. If its a copy it might be better to copy outright so we can track changes in the future through some ci job

defer cancel()
idsResult, err := c.da.GetIDs(getIDsCtx, height, namespace)

blobs, err := c.blobClient.GetAll(getIDsCtx, height, []share.Namespace{ns})
Copy link
Contributor

@tac0turtle tac0turtle Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be rate limited? if we have 10k blobs could it crash something?

@@ -0,0 +1,80 @@
package da
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can this file be marked as _tests? or a comment at the top saying its for tests

Comment on lines +124 to +138
server.SetDAVisualizationServer(
server.NewDAVisualizationServer(
func(ctx context.Context, id []byte, ns []byte) ([]datypes.Blob, error) {
// minimal fetch: derive height from ID and use namespace provided
height, _ := blob.SplitID(id)
res := client.Retrieve(ctx, height, ns)
if res.Code != datypes.StatusSuccess || len(res.Data) == 0 {
return nil, fmt.Errorf("blob not found")
}
return res.Data, nil
},
visualizerLogger,
config.Node.Aggregator,
),
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we modify the server to not need the function wrapper?

@@ -1,32 +1,33 @@
//go:build !ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this needed?

"time"

testutils "github.com/celestiaorg/utils/test"
"github.com/evstack/ev-node/block"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we revert this

<div class="blob-ids">
{{$namespace := .Namespace}}
{{range .BlobIDs}}
<a href="/da/blob?id={{.}}&namespace={{$namespace}}" class="blob-link blob-id" title="Click to view blob details">{{slice . 0 8}}...</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a mistaken deletion, can you revert

ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
defer cancel()

var namespace []byte
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be reverted as well.

@@ -1,3 +1,5 @@
//go:build ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -1,3 +1,5 @@
//go:build ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment on lines +27 to +31
type blobProofVerifier interface {
GetProof(ctx context.Context, height uint64, namespace share.Namespace, commitment blob.Commitment) (*blob.Proof, error)
Included(ctx context.Context, height uint64, namespace share.Namespace, proof *blob.Proof, commitment blob.Commitment) (bool, error)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this pr has a lot of interfaces spread through the code, why this approach instead of defining one interface in core?

@@ -1,4 +1,5 @@
//go:build evm
// +build evm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these can be removed

Copy link
Contributor

@tac0turtle tac0turtle left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still some cleanup needed from merge conflict issues.

can we split this pr into some smaller ones? like adding blob pkg in one pr moving interfaces around in another then the impl in a third, it will make the review and merge faster

@randygrok randygrok marked this pull request as draft December 3, 2025 17:00
@randygrok randygrok changed the title refactor: migrate to blob api and remove da interface (approach 2) refactor: migrate to blob api and remove da interface. Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants