Skip to content

NAUT-1310: add SBOM RPCs to StorageService#50

Merged
jnathangreeg merged 6 commits into
mainfrom
feat/naut-1310-sbom-rpcs
May 12, 2026
Merged

NAUT-1310: add SBOM RPCs to StorageService#50
jnathangreeg merged 6 commits into
mainfrom
feat/naut-1310-sbom-rpcs

Conversation

@jnathangreeg
Copy link
Copy Markdown
Contributor

@jnathangreeg jnathangreeg commented May 12, 2026

Adds four SBOM RPCs to support node-agent (workload) and kubevuln (registry) writing SBOMs directly to the backend, per design naut-1310-storage-deprecation.md §5.2 / §6 S1:

  • PutSBOM(unary) — upload SBOM ≤ 4 MiB
  • PutSBOMStream(client) — upload SBOM > 4 MiB, chunked
  • GetSBOM(unary) — probe (metadata_only=true) or fetch ≤ 4 MiB
  • GetSBOMStream(server) — fetch > 4 MiB, chunked

Primary key on the wire is (customer_guid, image_digest, syft_version), where customer_guid is supplied via the existing gRPC metadata header pattern used by SendContainerProfile. SBOMSource enum distinguishes workload / registry / host origin so a single Vulnerability Scanner pipeline downstream can route per-kind.

Hand-written StorageClient wrappers added in storageclient.go. Unary methods mirror the existing pattern; streaming methods marshal the SBOMSyft proto, chunk at 1 MiB, and reassemble server-side.

Makefile updated to also invoke protoc-gen-go-grpc — previously it ran only protoc-gen-gogo, leaving storage_service_grpc.pb.go stale on proto changes.

Summary by CodeRabbit

  • New Features

    • Streaming upload/download for SBOMs and container profiles with metadata-first responses and optional metadata-only retrieval.
    • Automatic chunking (~1 MiB) for large transfers; unary profile endpoints deprecated in favor of streaming.
  • Tests

    • Expanded end-to-end tests for streaming uploads/downloads, multi-chunk reassembly, metadata-only/miss cases, and validation edge cases.
  • Chores

    • Build tooling updated to install and use both message and gRPC code generators; help output refreshed.

Review Change Stack

Adds four SBOM RPCs to support node-agent (workload) and kubevuln
(registry) writing SBOMs directly to the backend, per design
`naut-1310-storage-deprecation.md` §5.2 / §6 S1:

- PutSBOM(unary)        — upload SBOM ≤ 4 MiB
- PutSBOMStream(client) — upload SBOM > 4 MiB, chunked
- GetSBOM(unary)        — probe (metadata_only=true) or fetch ≤ 4 MiB
- GetSBOMStream(server) — fetch > 4 MiB, chunked

Primary key on the wire is `(customer_guid, image_digest, syft_version)`,
where `customer_guid` is supplied via the existing gRPC metadata header
pattern used by `SendContainerProfile`. SBOMSource enum distinguishes
workload / registry / host origin so a single Vulnerability Scanner
pipeline downstream can route per-kind.

Hand-written StorageClient wrappers added in `storageclient.go`. Unary
methods mirror the existing pattern; streaming methods marshal the
SBOMSyft proto, chunk at 1 MiB, and reassemble server-side.

Makefile updated to also invoke `protoc-gen-go-grpc` — previously it ran
only `protoc-gen-gogo`, leaving `storage_service_grpc.pb.go` stale on
proto changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 12, 2026

Warning

Rate limit exceeded

@jnathangreeg has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 53 minutes and 39 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 19196c8a-6278-4ee5-8eb6-deb1dec421ab

📥 Commits

Reviewing files that changed from the base of the PR and between 05b1507 and 1e17d4c.

📒 Files selected for processing (2)
  • pkg/client/v1/storageclient.go
  • pkg/client/v1/storageclient_test.go
📝 Walkthrough

Walkthrough

Adds metadata-first streaming RPCs for SBOM and container-profile transfers, updates proto Makefile to generate both gogo message bindings and go-grpc service stubs, implements chunked client upload/download helpers with a streaming ReadCloser, and adds bufconn-based tests plus mock streaming hooks.

Changes

SBOM gRPC client support

Layer / File(s) Summary
Proto build configuration for gRPC generation
pkg/client/v1/proto/Makefile
Makefile updated to vendor dependencies during proto generation, run both protoc-gen-gogo and protoc-gen-go-grpc, install protoc-gen-go-grpc via make install-deps, and update help text.
StorageService SBOM RPC contracts and message types
pkg/client/v1/proto/storage_service.proto
StorageService adds metadata-first streaming RPCs for SBOM and container-profile transfers; ErrorCode gains SBOM errors and a new SBOMSource enum; new chunk/metadata/SBOMMetadata message types support probing, multi-chunk transfer, and indexed metadata.
StorageClient streaming methods and helpers
pkg/client/v1/storageclient.go
Implements SendContainerProfileStream, GetContainerProfileStream, PutSBOMStream, and GetSBOMStream with ~1MiB chunking, an sbomStreamReader io.ReadCloser for lazy reassembly, and MarshalSBOM/UnmarshalSBOM helpers; marks unary SendContainerProfile deprecated.
StorageClient tests and mock hooks
pkg/client/v1/storageclient_test.go
Adds bufconn-based round-trip server and tests for SBOM/container-profile streaming (multi-chunk reassembly, metadata-only/miss cases), extends mockStorageServiceClient with streaming hooks and explicit not-implemented errors, and adds nil-input validation tests.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant StorageClient
  participant gRPC
  Client->>StorageClient: PutSBOMStream(imageDigest, syftVersion, source, io.Reader)
  StorageClient->>gRPC: PutSBOMChunk(metadata + first blob slice)
  StorageClient->>gRPC: PutSBOMChunk(blob chunks...)
  gRPC-->>StorageClient: PutSBOMResponse
  Client->>StorageClient: GetSBOMStream(imageDigest, syftVersion)
  StorageClient->>gRPC: GetSBOMRequest
  gRPC-->>StorageClient: GetSBOMChunk(metadata)
  gRPC-->>StorageClient: GetSBOMChunk(blob chunks...)
  StorageClient-->>Client: io.ReadCloser (streamed bytes)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • kubescape/backend#44: Earlier PR modified pkg/client/v1/proto/Makefile to change proto generation; this PR further updates the Makefile to vendor deps and generate go-grpc service stubs.

Suggested reviewers

  • matthyx

Poem

🐰 I split the SBOM into tasty bits,

streamed the crumbs in careful fits,
Makefile hums and plugins sing,
gogo and go-grpc in the ring,
bytes reassembled — hop, joy brings.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 61.54% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: adding SBOM RPCs to the StorageService, which is the primary focus of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/naut-1310-sbom-rpcs

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@matthyx matthyx left a comment

Choose a reason for hiding this comment

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

I don't like the dual API depending on the size, can we make it simpler for the caller?

Comment thread pkg/client/v1/storageclient.go Outdated
Comment thread pkg/client/v1/storageclient.go Outdated
Comment thread pkg/client/v1/storageclient.go Outdated
Comment thread pkg/client/v1/storageclient_test.go Outdated
jnathangreeg and others added 3 commits May 12, 2026 11:05
Addresses matthyx review on #50:

- Drop unary PutSBOM and GetSBOM RPCs. There is now ONE client-streaming
  PutSBOM and ONE server-streaming GetSBOM — callers never need to know
  payload size in advance. Single-chunk streams handle the small-payload
  case naturally.

- Client wrappers take/return io.Reader instead of *v1beta1.SBOMSyft, so
  callers can stream from any source (a file, a syft pipe, etc.) without
  holding the full marshaled blob in memory. Convenience helpers
  MarshalSBOM and UnmarshalSBOM cover the typed-object case.

- Add a bufconn-based round-trip test (TestStorageClient_SBOMRoundTrip)
  that stands up a real gRPC server in-process and exercises the full
  marshal → chunk → wire → unchunk → unmarshal path with a non-trivial
  SBOMSyft. Covers: probe hit, probe miss, full fetch single-chunk, full
  fetch with the payload split across many small chunks (stresses the
  io.ReadCloser buffering logic). This is the kind of end-to-end test
  matthyx asked for — would have caught any wire-shape regression that
  the prior mock-based smoke tests missed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per discussion on PR #50:

- Rename PutSBOM/GetSBOM to PutSBOMStream/GetSBOMStream (proto + Go
  client + bufconn test server). Suffix makes the streaming nature
  explicit and leaves the naming room for unary helpers later if needed.

- Add SendContainerProfileStream (client-streaming) and
  GetContainerProfileStream (server-streaming) RPCs as the proper fix
  for the latent 4 MiB cliff in SendContainerProfile / GetProfile. The
  entry-count cap on ContainerProfile only loosely correlates with
  serialized byte size — a profile with high MaxContainerProfileSize or
  unusually large individual entries can still fail silently on the
  wire.

- Mark `rpc SendContainerProfile` as `option deprecated = true` and add
  a Deprecated: doc comment on the Go wrapper. GetProfile is left alone
  because it serves ApplicationProfile and NetworkNeighborhood too —
  those need their own streaming variants if they have the same risk.
  (Out of scope for this PR; separate ticket.)

- Add a bufconn round-trip test for CP streaming mirroring the existing
  SBOM round-trip test: send → verify server received intact bytes →
  fetch with payload split across small chunks → verify reassembly.
  Renamed sbomRoundTripServer to storageRoundTripServer since it now
  hosts both SBOM and CP handlers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
It now has a streaming replacement (GetContainerProfileStream) and
delegates to the unary GetProfile RPC under the hood, which carries the
same 4 MiB cliff exposure.

GetApplicationProfile and GetNetworkNeighborhood remain undecorated —
they don't have streaming variants yet. The proto-level GetProfile RPC
also stays as-is because it still serves AP/NN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
pkg/client/v1/proto/storage_service.proto (1)

386-392: 💤 Low value

Consider google.protobuf.Timestamp instead of RFC3339 strings.

created_at and last_referenced_at as strings push timestamp parsing/formatting onto every consumer and lose type checking at the wire. google.protobuf.Timestamp would give you well-known semantics, native Go time.Time conversion via AsTime(), and cleaner JSON encoding.

Since these fields are new in this PR, there's no migration cost.

♻️ Proposed change
+import "google/protobuf/timestamp.proto";
+
 // SBOMMetadata is the indexed view of an SBOM row, returned by GetSBOM
 // with or without metadata_only=true.
 message SBOMMetadata {
   ...
-  // created_at is the RFC3339 timestamp when the row was first inserted.
-  string created_at = 5;
+  // created_at is when the row was first inserted.
+  google.protobuf.Timestamp created_at = 5;
 
-  // last_referenced_at is the RFC3339 timestamp when the orchestrator most
-  // recently resolved an inventory entry to this SBOM. Drives eviction.
-  string last_referenced_at = 6;
+  // last_referenced_at is when the orchestrator most recently resolved an
+  // inventory entry to this SBOM. Drives eviction.
+  google.protobuf.Timestamp last_referenced_at = 6;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/client/v1/proto/storage_service.proto` around lines 386 - 392, Replace
the RFC3339 string fields created_at and last_referenced_at in the
storage_service.proto message with google.protobuf.Timestamp types: add an
import for google/protobuf/timestamp.proto, change the field types from string
to google.protobuf.Timestamp for the symbols created_at and last_referenced_at,
then regenerate the protobuf stubs (and update any consumers) so they use the
generated Timestamp types (e.g., in Go call AsTime()/AsTimePtr() or FromTime
when constructing) instead of manual string parsing/formatting.
pkg/client/v1/storageclient.go (2)

22-25: 💤 Low value

Rename sbomStreamChunkSize — it's also used by container-profile streams.

Both SendContainerProfileStream (line 322) and PutSBOMStream use this constant. The SBOM-prefixed name is misleading now that it governs all four streaming RPCs.

♻️ Proposed rename
-// sbomStreamChunkSize is the per-chunk byte budget used by PutSBOMStream
-// and GetSBOMStream. Set well below the default 4 MiB gRPC message limit
-// to leave headroom for framing overhead.
-const sbomStreamChunkSize = 1 << 20 // 1 MiB
+// streamChunkSize is the per-chunk byte budget used by all streaming
+// upload/download RPCs. Set well below the default 4 MiB gRPC message
+// limit to leave headroom for framing overhead.
+const streamChunkSize = 1 << 20 // 1 MiB
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/client/v1/storageclient.go` around lines 22 - 25, The constant
sbomStreamChunkSize is misleading because it is used by multiple streaming RPCs
(PutSBOMStream, GetSBOMStream and SendContainerProfileStream); rename it to a
neutral, descriptive identifier (e.g., streamChunkSize or grpcStreamChunkSize)
and update all references in storageclient.go (including PutSBOMStream,
GetSBOMStream, SendContainerProfileStream) to use the new name so callers and
readers see a consistent, accurate symbol across all four streaming RPC
implementations.

660-693: ⚡ Quick win

Recover real server status when stream.Send returns an error.

In gRPC-Go client streams, when the server closes the stream early (e.g., rejects the metadata header), Send returns io.EOF — the actual status (InvalidArgument, Unauthenticated, ResourceExhausted for over-large SBOMs, etc.) is only retrievable by calling CloseAndRecv. Currently, both functions return immediately on Send error without ever reaching CloseAndRecv, losing the server's diagnostic status.

This applies to:

  • PutSBOMStream: lines 673 and 683
  • SendContainerProfileStream: lines 318 and 327
♻️ Suggested pattern
 if err := stream.Send(first); err != nil {
-    return nil, fmt.Errorf("failed to send first chunk: %w", err)
+    if err == io.EOF {
+        // Server closed the stream early; retrieve the real status.
+        if _, recvErr := stream.CloseAndRecv(); recvErr != nil {
+            return nil, fmt.Errorf("server rejected upload: %w", recvErr)
+        }
+    }
+    return nil, fmt.Errorf("failed to send first chunk: %w", err)
 }

A small helper applied at both Send sites in each function keeps it tidy.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/client/v1/storageclient.go` around lines 660 - 693, Both PutSBOMStream
and SendContainerProfileStream currently return immediately when stream.Send
fails, losing the server's final gRPC status; change both to call
stream.CloseAndRecv() when Send returns an error and include that returned error
(if any) in the final error you return so callers see the server status.
Implement a small helper (e.g., handleSendError(stream grpc.ClientStream,
sendErr error) error or a function specific to the typed stream) that: if
sendErr == nil returns nil; otherwise calls stream.CloseAndRecv(), and returns a
wrapped error that contains both the original sendErr and the CloseAndRecv error
(if present). Replace the two direct early returns in PutSBOMStream and
SendContainerProfileStream with calls to this helper so CloseAndRecv is always
invoked on Send failures and its diagnostic is propagated.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/client/v1/storageclient.go`:
- Around line 744-749: GetSBOMStream currently returns (md, nil, nil) when
md.Success is false which hides server failures; change it to mirror
GetContainerProfileStream by returning a non-nil error for the !md.Success case
while still treating !md.Exists and metadataOnly as "no reader" (nil reader)
cases. Concretely, in GetSBOMStream replace the branch that does "if !md.Success
|| !md.Exists || metadataOnly { cancel(); return md, nil, nil }" with logic
that: cancels in all three cases, if !md.Success construct and return an error
that includes md.ErrorCode and md.ErrorMessage (or a formatted string with those
fields) along with md, and only return (md, nil, nil) for the !md.Exists or
metadataOnly paths; follow the error-wrapping style used in
GetContainerProfileStream.

---

Nitpick comments:
In `@pkg/client/v1/proto/storage_service.proto`:
- Around line 386-392: Replace the RFC3339 string fields created_at and
last_referenced_at in the storage_service.proto message with
google.protobuf.Timestamp types: add an import for
google/protobuf/timestamp.proto, change the field types from string to
google.protobuf.Timestamp for the symbols created_at and last_referenced_at,
then regenerate the protobuf stubs (and update any consumers) so they use the
generated Timestamp types (e.g., in Go call AsTime()/AsTimePtr() or FromTime
when constructing) instead of manual string parsing/formatting.

In `@pkg/client/v1/storageclient.go`:
- Around line 22-25: The constant sbomStreamChunkSize is misleading because it
is used by multiple streaming RPCs (PutSBOMStream, GetSBOMStream and
SendContainerProfileStream); rename it to a neutral, descriptive identifier
(e.g., streamChunkSize or grpcStreamChunkSize) and update all references in
storageclient.go (including PutSBOMStream, GetSBOMStream,
SendContainerProfileStream) to use the new name so callers and readers see a
consistent, accurate symbol across all four streaming RPC implementations.
- Around line 660-693: Both PutSBOMStream and SendContainerProfileStream
currently return immediately when stream.Send fails, losing the server's final
gRPC status; change both to call stream.CloseAndRecv() when Send returns an
error and include that returned error (if any) in the final error you return so
callers see the server status. Implement a small helper (e.g.,
handleSendError(stream grpc.ClientStream, sendErr error) error or a function
specific to the typed stream) that: if sendErr == nil returns nil; otherwise
calls stream.CloseAndRecv(), and returns a wrapped error that contains both the
original sendErr and the CloseAndRecv error (if present). Replace the two direct
early returns in PutSBOMStream and SendContainerProfileStream with calls to this
helper so CloseAndRecv is always invoked on Send failures and its diagnostic is
propagated.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f7c6a87-afa9-4dbc-8f5c-d2ec9726f7b2

📥 Commits

Reviewing files that changed from the base of the PR and between 3f62a2b and c1abadb.

⛔ Files ignored due to path filters (2)
  • pkg/client/v1/proto/storage_service.pb.go is excluded by !**/*.pb.go
  • pkg/client/v1/proto/storage_service_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (3)
  • pkg/client/v1/proto/storage_service.proto
  • pkg/client/v1/storageclient.go
  • pkg/client/v1/storageclient_test.go

Comment thread pkg/client/v1/storageclient.go Outdated
Comment thread pkg/client/v1/storageclient.go Outdated
Comment thread pkg/client/v1/storageclient_test.go
jnathangreeg and others added 2 commits May 12, 2026 04:28
Co-authored-by: Matthias Bertschy <matthias.bertschy@gmail.com>
Signed-off-by: Jonathan Green <jonathang@armosec.io>
- CodeRabbit: GetSBOMStream silently masked server-side failures.
  When the server reported !md.Success the helper returned
  (md, nil, nil), so a caller checking `if err != nil` couldn't tell a
  real RPC failure apart from a clean miss. Now returns a non-nil error
  on !md.Success, wrapping md.ErrorMessage / md.ErrorCode. !md.Exists
  and metadataOnly remain the (md, nil, nil) "no reader, not an error"
  paths. Mirrors GetContainerProfileStream's existing contract; doc
  updated to lead with the contract.

- matthyx (line 656): PutSBOMStream was wrapping the stream context in
  callTimeout — defeats the purpose of streaming for large payloads.
  Dropped on PutSBOMStream, SendContainerProfileStream, and
  GetContainerProfileStream (the latter two had the same bug).
  GetSBOMStream already avoided it explicitly. All four streaming
  wrappers now share the same comment explaining why.

- matthyx (line 874): added TestStorageClient_GetSBOMStream_NotBounded
  ByCallTimeout. Configures the client with WithCallTimeout(20ms),
  has the server delay between chunks by 50ms × 5 chunks = 250ms of
  server-side stream time (12× the configured timeout). If the stream
  were wrapped in context.WithTimeout(callTimeout), the call would
  fail with deadline-exceeded in the first 20ms. Stays under 500ms
  wall time so it's CI-safe. Extended startBufconnStorageServer to
  forward StorageClientOption variadics and added a serveChunkDelay
  knob to the round-trip server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread pkg/client/v1/storageclient.go Outdated
Comment on lines +653 to +656
// Note: we deliberately do NOT apply callTimeout here, because stream
// duration depends on payload size / network speed. Callers wanting a
// timeout should pass a ctx with their own deadline.
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This stray } closes PutSBOMStream early, so stream, err := c.protoClient.PutSBOMStream(ctx) is parsed outside the function body and the package no longer builds.

The current CI failure is:
pkg/client/v1/storageclient.go:658:2: syntax error: non-declaration statement outside function body

Suggested change
// Note: we deliberately do NOT apply callTimeout here, because stream
// duration depends on payload size / network speed. Callers wanting a
// timeout should pass a ctx with their own deadline.
}
// Note: we deliberately do NOT apply callTimeout here, because stream
// duration depends on payload size / network speed. Callers wanting a
// timeout should pass a ctx with their own deadline.

@jnathangreeg jnathangreeg merged commit 0b223ab into main May 12, 2026
4 checks passed
@matthyx matthyx moved this to To Archive in KS PRs tracking May 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants