Skip to content

feat(consumer): build TLS and SASL from config - #166

Merged
fredcamaral merged 2 commits into
developfrom
feat/consumer-tls-sasl-from-config
Sep 26, 2026
Merged

fredcamaral merged 2 commits into
developfrom
feat/consumer-tls-sasl-from-config

Conversation

@fredcamaral

Copy link
Copy Markdown
Member

Problem

The producer Builder applies the STREAMING_TLS_* / STREAMING_SASL_* surface with TLSFromConfig(cfg) / SASLFromConfig(cfg). The ConsumerBuilder only takes a built *tls.Config (TLS) and a built sasl.Mechanism (SASL), so every consuming service rebuilds the mechanism switch, the credential check and the plaintext opt-in by hand. Three consumers in br-sfn already do this (services/siloc/internal/silocstream/consumer_security.go, services/spb/internal/streamingcompat/consumer_security.go, services/spi/internal/spistream/consumer.go), each a copy of the lib's own producer logic.

API added

  • ConsumerBuilder.TLSFromConfig(cfg Config) *ConsumerBuilder
  • ConsumerBuilder.SASLFromConfig(cfg Config) *ConsumerBuilder

Both call the construction the producer already calls (Config.BuildTLSConfig, kafkasec.BuildSASLMechanism), so nothing was extracted or copied. Semantics match the producer:

  • TLS disabled is a no-op; an empty mechanism is a no-op and never opens the plaintext gate.
  • A malformed CA or invalid mechanism / missing credentials is kept as a deferred error that Build returns (ErrInvalidTLSConfig / ErrInvalidSASLMechanism); the first one wins.
  • SASL without TLS still fails at Build with ErrPlaintextSASLNotAllowed unless SASLAllowPlaintext is set.
  • The deferred error is returned only by an enabled Build: Enabled(false) stays a pure no-op, matching how the disabled consumer already skips Validate.

Order note, unchanged: FromConfig(consumerCfg) replaces the whole consumer config, so call TLSFromConfig / SASLFromConfig after it (same as TLS / SASL today).

Tests

api_consumer_tls_sasl_from_config_test.go (mirrors builder_tls_sasl_from_config_test.go):

  • empty mechanism is a no-op
  • invalid mechanism fails at Build
  • SASL without TLS is refused
  • SASL without TLS builds with SASLAllowPlaintext
  • SASL over config TLS builds (proves TLSFromConfig wires TLS)
  • empty mechanism keeps the plaintext gate closed (separately chained SASL(...) still refused)
  • disabled TLS is a no-op
  • invalid CA fails at Build
  • first deferred error wins
  • disabled consumer stays a no-op

TestConsumerBuilder_NilReceiverGuards covers both new setters on a nil receiver.

A mutation check made each of these five broken versions fail at least one case: TLS not wired, deferred error not surfaced, plaintext gate opened on an empty mechanism, last error wins, error surfaced on a disabled consumer.

Gates (local, at 35238da)

  • golangci-lint run ./... (v2.12.2, CI pin): 0 issues.
  • make test-unit: DONE 1988 tests, [ok] Unit tests passed
  • make check-tests: [ok] Tagged test package verification completed
  • make sec (gosec): Issues : 0
  • go vet ./..., go vet -tags=unit ./..., go vet -tags=integration ./...: clean; gofmt -l .: empty
  • Coverage: TLSFromConfig 100%, SASLFromConfig 100%

Line delta

  • prod: +63 / -3 (api_consumer.go)
  • test: +95 / -0
  • docs: +7 / -4 (AGENTS.md, doc.go, docs/design/consumer.md)

Once released, each br-sfn consumer deletes its hand-built SASL/TLS helper and calls .TLSFromConfig(cfg).SASLFromConfig(cfg) on the builder.

🤖 Generated with Claude Code

The producer Builder applies the STREAMING_TLS_* / STREAMING_SASL_* surface
through TLSFromConfig / SASLFromConfig; the ConsumerBuilder only took a built
*tls.Config and sasl.Mechanism, so every consuming service re-implemented the
mechanism switch and CA decoding to reach the same broker.

ConsumerBuilder.TLSFromConfig and .SASLFromConfig now call the same
Config.BuildTLSConfig and kafkasec.BuildSASLMechanism the producer calls, with
the producer's semantics: disabled TLS and an empty mechanism are no-ops, an
empty mechanism never opens the plaintext gate, and a malformed CA or invalid
mechanism is deferred to Build (first error wins) as ErrInvalidTLSConfig /
ErrInvalidSASLMechanism. The deferred error surfaces only on an enabled Build,
so the Enabled(false) kill switch stays a pure no-op.
@coderabbitai

coderabbitai Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Essentials

Run ID: 2eb99aa6-4cc0-4bdb-a11e-cdcba7eead6d

📥 Commits

Reviewing files that changed from the base of the PR and between 35238da and 21055a3.

📒 Files selected for processing (2)
  • api_consumer_tls_sasl_from_config_test.go
  • doc.go

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

ConsumerBuilder can now apply TLS and SASL settings from shared configuration. It defers configuration errors until an enabled build and skips this check for disabled builds. Tests and documentation cover the new methods and behavior.

Changes

Consumer Configuration

Layer / File(s) Summary
Apply configuration and validate builds
api_consumer.go, api_consumer_builder_whitebox_test.go, api_consumer_tls_sasl_from_config_test.go, doc.go, docs/design/consumer.md, AGENTS.md
Adds TLSFromConfig and SASLFromConfig to ConsumerBuilder. Enabled builds return the first deferred configuration error before resolving the receiver; disabled builds skip this check. Tests cover configuration outcomes and nil receivers. Documentation describes the shared configuration settings. The supplied AGENTS.md change shows no observable difference.

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 21055

The consumer TLS and SASL configuration behavior matches its documented contract, with no identified issue that should block merging.

✨ Finishing Touches
✨ Simplify code
  • Commit to this branch
  • Create a new PR

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

@github-actions github-actions Bot added size/S PR changes 50–199 lines docs Documentation and markdown content tests Unit, integration, fuzz and property tests consumer Consumer surface — at-least-once group runtime, retry/seek-back, DLQ, tenant scoping labels Sep 25, 2026
@lerian-studio

lerian-studio commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

🔒 Security Scan Results — lib-streaming

✅ PR Mergeable — no blocking findings

Stage Status Blocking?
Filesystem Scan ✅ Clean —
Docker Image Scan ➖ Skipped —
Docker Hub Health Score ➖ Skipped —
Pre-release Version Check ✅ Clean —

Trivy

Filesystem Scan

✅ No vulnerabilities or secrets found.


Pre-release Version Check

✅ No unstable version pins found.


🔍 View full scan logs

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

🔍 PR Validation Summary

✅ PR Mergeable — no blocking failures

Check Status Blocking
Source Branch ✅ success yes
PR Title ✅ success yes
PR Description ✅ success yes
Breaking Change Guard ✅ success yes
Commit Signatures ✅ success yes
PR Size ✅ success no
Auto Labels ✅ success no
PR Metadata ✅ success no

🔍 View workflow run

@lerian-studio

Copy link
Copy Markdown
Contributor

📊 Unit Test Coverage Report: app

Metric Value
Overall Coverage 86.9% ✅ PASS
Threshold 80%

Coverage by Package

Package Coverage
github.com/LerianStudio/lib-streaming/v4/billing 97.3%
github.com/LerianStudio/lib-streaming/v4/internal/buildmeta 100.0%
github.com/LerianStudio/lib-streaming/v4/internal/cloudevents 93.7%
github.com/LerianStudio/lib-streaming/v4/internal/config 86.8%
github.com/LerianStudio/lib-streaming/v4/internal/consumer 92.3%
github.com/LerianStudio/lib-streaming/v4/internal/contract 85.0%
github.com/LerianStudio/lib-streaming/v4/internal/dlqheader 33.3%
github.com/LerianStudio/lib-streaming/v4/internal/emitter 100.0%
github.com/LerianStudio/lib-streaming/v4/internal/kafkasec 84.9%
github.com/LerianStudio/lib-streaming/v4/internal/manifest 84.0%
github.com/LerianStudio/lib-streaming/v4/internal/producer 89.2%
github.com/LerianStudio/lib-streaming/v4/internal/transport/eventbridge 87.3%
github.com/LerianStudio/lib-streaming/v4/internal/transport/kafka 56.4%
github.com/LerianStudio/lib-streaming/v4/internal/transport/rabbitmq 93.2%
github.com/LerianStudio/lib-streaming/v4/internal/transport/sqs 84.4%
github.com/LerianStudio/lib-streaming/v4/internal/transport 96.7%
github.com/LerianStudio/lib-streaming/v4/streamingtest 90.7%
github.com/LerianStudio/lib-streaming/v4 86.4%

Generated by Go PR Analysis workflow

@fredcamaral

Copy link
Copy Markdown
Member Author

@coderabbitai approve

@coderabbitai

coderabbitai Bot commented Sep 26, 2026

Copy link
Copy Markdown
✅ Action performed

Comments resolved and changes approved.

@fredcamaral
fredcamaral merged commit b3319af into develop Sep 26, 2026
27 checks passed
@fredcamaral
fredcamaral deleted the feat/consumer-tls-sasl-from-config branch September 26, 2026 03:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

consumer Consumer surface — at-least-once group runtime, retry/seek-back, DLQ, tenant scoping docs Documentation and markdown content size/S PR changes 50–199 lines tests Unit, integration, fuzz and property tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants