Skip to content

feat(providers): Nebius MysteryBox integration#5868

Merged
Skarlso merged 20 commits intoexternal-secrets:mainfrom
nebius:nebius-mysterybox-integration
Mar 6, 2026
Merged

feat(providers): Nebius MysteryBox integration#5868
Skarlso merged 20 commits intoexternal-secrets:mainfrom
nebius:nebius-mysterybox-integration

Conversation

@greenmapc
Copy link
Copy Markdown
Contributor

@greenmapc greenmapc commented Jan 25, 2026

Problem Statement

Integration with Nebius Mysterybox Secret Service

Related Issue

Fixes #5852

Format

Please ensure that your PR follows the following format for the title:

feat(scope): add new feature
fix(scope): fix bug
docs(scope): update documentation
chore(scope): update build tool or dependencies
ref(scope): refactor code
clean(scope): provider cleanup
test(scope): add tests
perf(scope): improve performance
desig(scope): improve design

Where scope is optionally one of:

  • charts
  • release
  • testing
  • security
  • templating

Checklist

  • I have read the contribution guidelines
  • All commits are signed with git commit --signoff
  • My changes have reasonable test coverage
  • All tests pass with make test
  • I ensured my PR is ready for review with make reviewable

Summary

Adds complete integration of Nebius Mysterybox Secret Service as an External Secrets Operator provider (addresses #5852). Implemented as a direct integration due to authorization constraints with the gRPC plugin bridge approach.

Core Implementation

API & CRD Types:

  • New types: NebiusAuth (service account credentials or token), NebiusCAProvider, NebiusMysteryboxProvider
  • Updated SecretStoreProvider to include NebiusMysterybox field
  • Generated deep copy methods and manifests for ClusterSecretStore/SecretStore

Provider (providers/v1/nebius/mysterybox/):

  • Cached gRPC clients per API domain/CA cert with thread-safe mutex access
  • Token getter with singleflight to deduplicate concurrent token exchange requests
  • SecretsClient implementing read-only operations: GetSecret and GetSecretMap
  • Comprehensive validation: RFC-compliant API domains, enforced authentication (exactly one method required), certificate reference checks

SDK Components (providers/v1/nebius/common/sdk/):

  • Mysterybox gRPC client with payload retrieval and test fake client
  • IAM token exchanger for service account credentials with test fake
  • SDK initialization with TLS 1.3 minimum and optional CA cert support

Documentation & Configuration:

  • Provider guide with authentication examples and YAML templates
  • Provider registration module
  • Updated stability table and API documentation

Testing:

  • Unit tests for token caching, expiration, concurrent access, singleflight deduplication
  • Validation tests for API domains, authentication configs, and error scenarios
  • Comprehensive test coverage (958+ lines in provider_test.go alone)

Key Design Decisions

  • Read-only provider (GetSecret/GetSecretMap); write operations return not-implemented
  • Singleflight prevents redundant token exchanges for identical credentials
  • LRU cache for client instances with eviction cleanup
  • API domain validation enforces RFC compliance with IDNA/ASCII conversion

@github-actions github-actions bot added kind/documentation Categorizes issue or PR as related to documentation. kind/dependency dependabot and upgrades size/l labels Jan 25, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This pull request adds comprehensive support for Nebius Mysterybox as a new secret provider in External Secrets Operator. The implementation includes new CRD types, provider client with IAM token caching, gRPC-based secret retrieval, validation logic, documentation, and tests.

Changes

Cohort / File(s) Summary
API Types & Generated Code
apis/externalsecrets/v1/secretstore_nebius_types.go, apis/externalsecrets/v1/secretstore_types.go, apis/externalsecrets/v1/zz_generated.deepcopy.go
Added three new public structs (NebiusAuth, NebiusCAProvider, NebiusMysteryboxProvider) with authentication and CA bundle configuration. Updated SecretStoreProvider to include optional NebiusMysterybox field. Generated deep copy methods for all new types.
CRD Schemas
config/crds/bases/external-secrets.io_clustersecretstores.yaml, config/crds/bases/external-secrets.io_secretstores.yaml, deploy/crds/bundle.yaml
Extended ClusterSecretStore, SecretStore, and related CRDs with nested nebiusmysterybox provider schema, including apiDomain, auth with validation rules, and caProvider configuration.
Nebius Provider Implementation
providers/v1/nebius/mysterybox/provider.go, providers/v1/nebius/mysterybox/secrets_client.go, providers/v1/nebius/mysterybox/validation.go, providers/v1/nebius/mysterybox/utils.go
Complete provider client with config parsing, cached MysteryBox gRPC client management, IAM token retrieval, and comprehensive validation including API domain RFC compliance and secret reference checks.
Nebius SDK & Client Layer
providers/v1/nebius/common/sdk/mysterybox/client.go, providers/v1/nebius/common/sdk/mysterybox/grpc_client.go, providers/v1/nebius/common/sdk/sdk.go
Low-level gRPC client for Nebius Mysterybox with payload retrieval methods. SDK initialization with TLS 1.3, CA certificate handling, and keepalive configuration.
IAM & Token Management
providers/v1/nebius/common/sdk/iam/token_exchanger.go, providers/v1/nebius/common/sdk/iam/grpc_token_exchanger.go, providers/v1/nebius/mysterybox/token_getter.go
Token exchange interface and implementations: gRPC-based token exchanger for service account credentials, and cached token getter with LRU cache and singleflight deduplication.
Testing Infrastructure
providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go, providers/v1/nebius/common/sdk/iam/fake_token_exchanger.go, providers/v1/nebius/common/sdk/mysterybox/grpc_client_test.go
Fake Mysterybox client, in-memory service for testing, fake token exchanger with configurable responses, and gRPC client tests with mock PayloadService.
Documentation & Examples
docs/api/spec.md, docs/provider/nebius-mysterybox.md, docs/snippets/nebius-mysterybox-*.yaml
API reference documentation for new types, Nebius MysteryBox provider guide with authentication examples, and YAML snippets for SecretStore and ExternalSecret configurations.
Configuration & Registration
pkg/register/nebiusmysterybox.go, providers/v1/nebius/go.mod, runtime/constants/constants.go, hack/api-docs/mkdocs.yml, go.mod
Provider registration via build tag, module definition for Nebius provider, new constants for provider and API calls, documentation navigation update, and root module dependencies.
Comprehensive Provider Tests
providers/v1/nebius/mysterybox/provider_test.go, providers/v1/nebius/mysterybox/validation_test.go, providers/v1/nebius/mysterybox/token_getter_test.go
Extensive test coverage for provider client lifecycle, token caching with expiry, concurrent access patterns, validation of auth configs and API domains, cache eviction, and error handling.
Test Snapshots & Fixtures
tests/__snapshot__/clustersecretstore-v1.yaml, tests/__snapshot__/secretstore-v1.yaml
Updated snapshot YAML fixtures with nebiusmysterybox provider configuration examples for ClusterSecretStore and SecretStore.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR implements Nebius Mysterybox provider support as requested in issue #5852, with provider infrastructure, authentication, secret retrieval, validation, comprehensive tests, and documentation.
Out of Scope Changes check ✅ Passed All changes directly support Nebius Mysterybox integration. Configuration-related discussions in reviews about singleflight and token caching were addressed; minor tooling updates (mkdocs.yml, go.mod dependencies) align with provider addition.

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


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.

@greenmapc greenmapc force-pushed the nebius-mysterybox-integration branch 2 times, most recently from 69eaa25 to e96bafe Compare January 25, 2026 19:10
@greenmapc greenmapc changed the title feat(providers) Nebius mysterybox integration feat(providers): Nebius mysterybox integration Jan 25, 2026
@greenmapc greenmapc changed the title feat(providers): Nebius mysterybox integration feat(providers): Nebius MysteryBox integration Jan 25, 2026
@github-actions github-actions bot added area/providers kind/feature Categorizes issue or PR as related to a new feature. labels Jan 25, 2026
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: 11

🤖 Fix all issues with AI agents
In `@apis/externalsecrets/v1/secretstore_nebius_types.go`:
- Around line 46-58: The NebiusMysteryboxProvider struct's CAProvider field is
optional but its JSON tag lacks omitempty, causing nil pointers to be serialized
as "caProvider": null; update the CAProvider field tag in the
NebiusMysteryboxProvider definition to `json:"caProvider,omitempty"` (i.e.,
change the tag on the CAProvider *NebiusCAProvider field) so nil values are
omitted during JSON serialization.
- Around line 19-38: Add CRD validation to require at least one credential in
NebiusAuth by adding a kubebuilder marker on the NebiusAuth type (similar to
DopplerAuth): annotate the NebiusAuth struct with a validation directive such as
"// +kubebuilder:validation:MinProperties=1" (or the project's established
marker for "at least one of" constraints) so the CRD schema enforces that at
least one of ServiceAccountCreds or Token is provided; keep the existing fields
ServiceAccountCreds and Token and their json tags unchanged.

In `@config/crds/bases/external-secrets.io_clustersecretstores.yaml`:
- Around line 3716-3787: The auth schema currently allows an empty object
because both serviceAccountCredsSecretRef and tokenSecretRef are optional; add a
CEL validation on the auth object to require exactly one of these fields be set
(i.e., xor). Update the CRD for the auth properties to include a top-level
validation rule (using CEL) that references auth.serviceAccountCredsSecretRef
and auth.tokenSecretRef and enforces (has(serviceAccountCredsSecretRef) !=
has(tokenSecretRef)) so exactly one credential source is provided at validation
time.

In `@config/crds/bases/external-secrets.io_secretstores.yaml`:
- Around line 3716-3787: The auth object currently allows an empty object
because both serviceAccountCredsSecretRef and tokenSecretRef are optional;
update the CRD schema for the auth property to require at least one of these by
adding an anyOf/oneOf constraint (e.g., oneOf with required:
[serviceAccountCredsSecretRef] and required: [tokenSecretRef]) so that auth must
contain either serviceAccountCredsSecretRef or tokenSecretRef (or use oneOf if
they must be mutually exclusive); modify the auth schema block that defines
serviceAccountCredsSecretRef and tokenSecretRef to include this validation.

In `@docs/introduction/stability-support.md`:
- Line 143: Add a missing row for the Nebius Mysterybox provider to the Provider
Stability and Support Level table (the table covering provider stability lines
~65-100) by inserting an entry for "Nebius Mysterybox" with its stability and
maintainer; for example add a row linking to the provider docs and declaring its
stability and maintainer like: Nebius Mysterybox → alpha → `@greenmapc`. Ensure
the display text matches the Feature Support table ("Nebius Mysterybox") and the
link points to the provider docs
(https://external-secrets.io/latest/provider/nebius-mysterybox).

In `@docs/provider/nebius-mysterybox.md`:
- Line 44: The include directive for
'nebius-mysterybox-external-secret-by-key.yaml' is not wrapped in a YAML code
fence, so surround that include line with a triple-backtick code fence
specifying yaml (```yaml ... ```) to match the other includes and ensure the
YAML renders as a formatted code block.
- Line 51: Wrap the include directive {% include
'nebius-mysterybox-external-secret-all.yaml' %} in a fenced code block (triple
backticks with language `yaml`) so the included YAML is rendered/formatted
correctly; locate the line containing the include string and surround it with
```yaml before and ``` after.

In `@providers/v1/nebius/common/sdk/mysterybox/grpc_client.go`:
- Around line 63-77: The code assumes entry.GetData() is non-nil before reading
Payload and can panic; before constructing PayloadEntry and switching on
entry.GetData().Payload (around PayloadEntry, entry.GetData(), and the switch),
add a nil-check for data := entry.GetData(); if data == nil return an error (use
fmt.Errorf with notSupportedPayloadType or a new error indicating missing data
and include key) so you avoid dereferencing nil and provide a clear failure
path; then use data.Payload (and data.GetStringValue()/GetBinaryValue()) inside
the switch.

In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 727-759: The test mutates package-level mysteryboxClientsCacheSize
without restoring it, which can leak state to other tests; update
TestMysteryboxClientsCache_EvictionClosesClient to capture the original
mysteryboxClientsCacheSize at the start, set mysteryboxClientsCacheSize = 1 for
the test, and register a t.Cleanup closure that restores the original value (or
alternatively refactor createOrGetMysteryboxClient/Provider to accept an
injected cache size for the test); ensure references to
mysteryboxClientsCacheSize and the test helper p.createOrGetMysteryboxClient
remain intact so the cleanup reliably restores global state after the test.
- Around line 638-646: The test spawns goroutines that call
p.createOrGetMysteryboxClient and currently use t.Errorf inside each goroutine
which may not be reported; replace that with a thread-safe error collection
(e.g., an errors channel or an atomic/uint32 failure flag) captured by each
goroutine instead of calling t.Errorf, close or read the channel / check the
atomic after wg.Wait() and then call t.Fatalf/t.Errorf from the main test
goroutine if any error was recorded; update the goroutine body that reads from
start and invokes createOrGetMysteryboxClient to send errors into the chosen
collector and ensure the main test checks it after wg.Wait() before finishing.

In `@providers/v1/nebius/mysterybox/secrets_client.go`:
- Around line 63-66: The error message and constant used around json.Marshal in
secrets_client.go are wrong (they reference unmarshal); update the error
handling in the function containing the json.Marshal call (look for the out, err
:= json.Marshal(keyToValue) block) to use a marshal-specific error
constant/message (e.g., errJSONMarshal with "failed to marshal JSON") and
include the original error when returning (wrap or format the err) so the
log/return reflects the actual marshal failure.
🧹 Nitpick comments (13)
providers/v1/nebius/common/sdk/sdk.go (1)

37-43: Simplify the nil check.

The caCertificate != nil check is redundant since len(nil) == 0 in Go for slices.

♻️ Suggested simplification
-	if caCertificate != nil && len(caCertificate) > 0 {
+	if len(caCertificate) > 0 {
		certPool := x509.NewCertPool()
		if ok := certPool.AppendCertsFromPEM(caCertificate); !ok {
			return nil, errors.New("failed to append CA certificate. PEM parse error")
		}
		tlsCfg.RootCAs = certPool
	}
providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (3)

38-47: Inconsistent receiver types between methods.

Close() at line 33 uses a pointer receiver *FakeMysteryboxClient, while GetSecret and GetSecretByKey use value receivers. While this works functionally, it's inconsistent style. Consider using pointer receivers consistently for all methods.

♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {
+func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {

49-64: Apply same receiver fix here for consistency.

♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
+func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {

66-74: Consider adding mutex for thread safety in parallel tests.

MysteryboxService accesses secretData map without synchronization. If tests using this fake service run in parallel and share the same service instance, this could cause data races.

♻️ Suggested fix
+import "sync"
+
 type MysteryboxService struct {
+	mu         sync.RWMutex
 	secretData map[string]map[string][]mysterybox.Entry
 }

Then add lock/unlock calls in GetSecret, CreateSecret, and CreateNewSecretVersion methods.

providers/v1/nebius/mysterybox/token_service_test.go (2)

119-121: Confusing parameter type in addSecondsToClock helper.

The parameter second has type time.Duration but is multiplied by time.Second, which is confusing. If a caller passes 5 * time.Second, the actual advancement would be 5 seconds × 1 second = 5,000,000,000 nanoseconds² (incorrect). The current usage passes integer literals which work due to implicit conversion, but this is fragile.

♻️ Proposed fix - use int64 for clarity
-func addSecondsToClock(clk *clocktesting.FakeClock, second time.Duration) {
-	clk.SetTime(clk.Now().Add(second * time.Second))
+func addSecondsToClock(clk *clocktesting.FakeClock, seconds int) {
+	clk.SetTime(clk.Now().Add(time.Duration(seconds) * time.Second))
 }

200-203: Consider checking errors in key rotation test.

The errors returned by GetToken are discarded. While the test focuses on cache key behavior, ignoring errors could hide unexpected failures during test execution.

♻️ Proposed fix
-	t1, _ := env.TokenCacheService.GetToken(ctx, "api", base, nil)
-	t2, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedKeyID, nil)
-	t3, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedPriv, nil)
-	t4, _ := env.TokenCacheService.GetToken(ctx, "api", rotatedSubject, nil)
+	t1, err := env.TokenCacheService.GetToken(ctx, "api", base, nil)
+	tassert.NoError(t, err)
+	t2, err := env.TokenCacheService.GetToken(ctx, "api", rotatedKeyID, nil)
+	tassert.NoError(t, err)
+	t3, err := env.TokenCacheService.GetToken(ctx, "api", rotatedPriv, nil)
+	tassert.NoError(t, err)
+	t4, err := env.TokenCacheService.GetToken(ctx, "api", rotatedSubject, nil)
+	tassert.NoError(t, err)
providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (1)

130-134: Consider using named fields for struct initialization.

Using positional struct initialization can be fragile if the struct fields are reordered in the future. Named fields improve readability and maintainability.

♻️ Suggested refactor
 	return &GrpcClient{
-		mbox.NewPayloadService(mysteryboxSdk),
-		mysteryboxSdk,
+		PayloadService: mbox.NewPayloadService(mysteryboxSdk),
+		sdk:            mysteryboxSdk,
 	}, nil
providers/v1/nebius/mysterybox/validation_test.go (1)

194-206: Minor: Redundant nil check after assertion.

The if err != nil check on line 202 is redundant since tassert.NotNil on line 201 would have already failed the test if err was nil. Consider simplifying.

♻️ Suggested simplification
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			_, err := p.ValidateStore(tt.store)
 			if tt.wantErr == "" {
 				tassert.NoError(t, err, "%s: unexpected error", tt.name)
 				return
 			}
-			tassert.NotNil(t, err, "%s: expected error containing %q, got nil", tt.name, tt.wantErr)
-			if err != nil {
-				tassert.Contains(t, err.Error(), tt.wantErr, "%s: error %q does not contain %q", tt.name, err.Error(), tt.wantErr)
-			}
+			tassert.Error(t, err, "%s: expected error containing %q, got nil", tt.name, tt.wantErr)
+			tassert.Contains(t, err.Error(), tt.wantErr, "%s: error %q does not contain %q", tt.name, err.Error(), tt.wantErr)
 		})
 	}
providers/v1/nebius/common/sdk/iam/grpc_client.go (2)

51-58: Consider including original error details in the returned error.

When JSON unmarshalling fails, the original error is only passed to the observer but not included in the returned error. This loses diagnostic information that could help users troubleshoot malformed credentials.

♻️ Suggested improvement
 	parsedSubjectCreds := &auth.ServiceAccountCredentials{}
 	if err := json.Unmarshal([]byte(subjectCreds), parsedSubjectCreds); err != nil {
 		if t.exchangeTokenObserveCall != nil {
 			t.exchangeTokenObserveCall(err)
 		}
-		return nil, errors.New(errInvalidSubjectCreds)
+		return nil, fmt.Errorf("%s: %w", errInvalidSubjectCreds, err)
 	}

86-92: Clarify metrics observation on success path.

The observer is called with err (which may be nil on success) before checking if err != nil. This appears intentional for metrics tracking, but a brief comment would clarify the design choice.

📝 Optional: Add clarifying comment
 	tok, err := tokenExchanger.Exchange(ctx, req)
+	// Always observe the result for metrics, regardless of success/failure
 	if t.exchangeTokenObserveCall != nil {
 		t.exchangeTokenObserveCall(err)
 	}
providers/v1/nebius/mysterybox/provider_test.go (1)

761-779: Non-atomic increment in fakeTokenService.

f.calls++ is not atomic, but since this fake is used in single-threaded test contexts, it's acceptable. If concurrent usage is anticipated, consider using atomic.AddInt32.

providers/v1/nebius/mysterybox/token_service.go (1)

88-95: Unsafe type assertion could panic if cache contains unexpected type.

The type assertion value.(*iam.Token) will panic if the cache somehow contains a value of a different type. Use the comma-ok idiom for defensive coding.

♻️ Suggested fix
 	value, ok := c.tokenCache.Get(*cacheKey)
 	if ok {
-		token := value.(*iam.Token)
-		tokenExpired := isTokenExpired(token, c.Clock)
-		if !tokenExpired {
-			return token.Token, nil
+		if token, ok := value.(*iam.Token); ok {
+			if !isTokenExpired(token, c.Clock) {
+				return token.Token, nil
+			}
 		}
 	}
providers/v1/nebius/mysterybox/provider.go (1)

35-35: Consider a more descriptive import alias.

The alias mysterybox2 doesn't convey its purpose. Consider mysteryboxsdk or similar to clarify it's the SDK client package.

♻️ Suggested improvement
-	mysterybox2 "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"
+	mysteryboxsdk "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"

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: 6

🤖 Fix all issues with AI agents
In `@deploy/crds/bundle.yaml`:
- Around line 17431-17540: The auth object for nebiusmysterybox currently allows
empty objects; update the schema for both SecretStore and ClusterSecretStore
nebiusmysterybox entries so that auth requires at least one credential reference
by adding an anyOf with two alternatives: one requiring
auth.serviceAccountCredsSecretRef and another requiring auth.tokenSecretRef
(reference the auth property, serviceAccountCredsSecretRef and tokenSecretRef
symbols to locate where to add the anyOf). Ensure the anyOf sits alongside the
existing properties/type/required block so validation fails unless one of those
two fields is present.

In `@pkg/register/nebiusmysterybox.go`:
- Around line 22-25: The build fails because the package imported as mbox
(github.com/external-secrets/external-secrets/providers/v1/nebius/mysterybox) is
covered by a replace but not listed in go.mod's require; add the missing module
requirement by running "go get
github.com/external-secrets/external-secrets/providers/v1/nebius" or manually
add a require directive for
github.com/external-secrets/external-secrets/providers/v1/nebius in go.mod, then
run "go mod tidy" to update dependencies so imports used in
pkg/register/nebiusmysterybox.go (mbox) resolve.

In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 551-564: The test uses the global log variable instead of the
locally-defined logger in several places; update the Provider instances in
TestCreateOrGetMysteryboxClient_CachesByKey (and the other listed tests at lines
noted in the comment) to set the Logger field to the local logger variable
rather than log so the Provider.Logger uses the test-local logger; specifically
replace occurrences where Provider{ Logger: log, ... } is constructed (and any
similar constructions in this file) with Provider{ Logger: logger, ... } while
keeping NewMysteryboxClient and mysteryboxClientsCache unchanged.

In `@providers/v1/nebius/mysterybox/provider.go`:
- Around line 85-91: The nil check on p.TokenService in Provider.NewClient is
racy and redundant because initTokenService already uses sync.Once (tokenOnce);
remove the conditional and always call p.initTokenService() (retaining the
existing error handling) so initialization is delegated to
initTokenService/tokenOnce, or alternatively move the nil check inside the
tokenOnce.Do, referencing Provider.NewClient, Provider.initTokenService,
TokenService and tokenOnce to locate the relevant code.
- Around line 233-251: The initMysteryboxClientsCache function currently
swallows initialization errors because cacheOnce.Do runs only once; capture and
persist the error from lru.NewWithEvict in a package/struct-level variable
(e.g., add a p.mysteryboxCacheInitErr error) that you set inside the
cacheOnce.Do closure when lru.NewWithEvict fails, return that stored error from
initMysteryboxClientsCache and update callers to check it; apply the same
stored-error pattern to initTokenService (and its tokenServiceInitErr) so
subsequent calls correctly report prior initialization failures while still
using cacheOnce for one-time setup.

In `@providers/v1/nebius/mysterybox/token_service.go`:
- Around line 79-103: GetToken on TokenCacheService reads and writes
c.tokenCache without synchronization causing duplicate concurrent token
exchanges; fix by coalescing concurrent requests for the same cacheKey (built by
buildTokenCacheKey) before calling TokenExchanger.NewIamToken: add
synchronization (e.g., a singleflight.Group keyed by the cacheKey string or a
map of per-key sync.Mutexes on the TokenCacheService struct) so the code checks
tokenCache and if missing/expired uses singleflight.Do (or locks the per-key
mutex) to ensure only one goroutine performs NewIamToken and then stores the
returned token with c.tokenCache.Add; keep the existing isTokenExpired and
c.Clock checks outside/inside the coalesced call as appropriate and ensure any
errors from NewIamToken (wrapped with MapGrpcErrors) are propagated to callers.
♻️ Duplicate comments (9)
docs/introduction/stability-support.md (1)

143-143: Missing entry in Provider Stability table.

The Nebius Mysterybox provider is added to the Feature Support table but still lacks a corresponding entry in the Provider Stability and Support Level table (lines 65-100). Please add an entry declaring stability level and maintainer.

docs/provider/nebius-mysterybox.md (1)

44-51: Wrap the include directives in YAML code fences.

Same issue as previously noted: without fences these includes render as plain text.

providers/v1/nebius/mysterybox/provider_test.go (2)

638-646: Using t.Errorf inside a goroutine may not properly report test failures.

When t.Errorf is called from a goroutine, the test may exit before the error is reported, potentially causing a panic. Use a channel or atomic.Value to collect errors and check them after wg.Wait().


727-759: Global state mutation without cleanup may affect parallel tests.

Setting mysteryboxClientsCacheSize = 1 modifies package-level state without restoring it. Consider using t.Cleanup to restore the original value, or restructure the test to inject the cache size.

config/crds/bases/external-secrets.io_clustersecretstores.yaml (1)

3716-3787: Require exactly one credential in auth.
auth is required but both credential refs are optional, so auth: {} validates and will fail at runtime. Add a CEL rule to enforce exactly one of serviceAccountCredsSecretRef or tokenSecretRef.

🔧 Proposed CRD validation
                   auth:
                     description: Auth defines parameters to authenticate in Mysterybox
                     properties:
                       serviceAccountCredsSecretRef:
@@
                       tokenSecretRef:
@@
-                    type: object
+                    type: object
+                    x-kubernetes-validations:
+                    - message: Exactly one of 'serviceAccountCredsSecretRef' or 'tokenSecretRef' must be specified
+                      rule: (has(self.serviceAccountCredsSecretRef) && !has(self.tokenSecretRef)) || (!has(self.serviceAccountCredsSecretRef) && has(self.tokenSecretRef))
apis/externalsecrets/v1/secretstore_nebius_types.go (2)

19-38: Require at least one auth source in NebiusAuth.
Right now auth: {} passes validation but fails at runtime. Add a CRD rule to require either serviceAccountCredsSecretRef or tokenSecretRef.

Suggested fix
 // NebiusAuth defines the authentication method for the Nebius provider.
+// +kubebuilder:validation:XValidation:rule="has(self.serviceAccountCredsSecretRef) || has(self.tokenSecretRef)",message="either serviceAccountCredsSecretRef or tokenSecretRef must be set"
 type NebiusAuth struct {

55-57: Avoid serializing caProvider: null.
CAProvider is optional but missing omitempty, so nil pointers serialize as null. Align with other optional provider fields.

Suggested fix
-	CAProvider *NebiusCAProvider `json:"caProvider"`
+	CAProvider *NebiusCAProvider `json:"caProvider,omitempty"`
config/crds/bases/external-secrets.io_secretstores.yaml (1)

3709-3824: Require at least one Nebius auth method (repeat).

auth is required but both serviceAccountCredsSecretRef and tokenSecretRef are optional, so auth: {} passes validation and will likely fail at runtime. Please add a schema rule (oneOf/anyOf or x-kubernetes-validations) to require at least one.

providers/v1/nebius/mysterybox/secrets_client.go (1)

63-66: Error message is misleading - operation is marshalling, not unmarshalling.

The error constant errJSONUnmarshal and message "failed to unmarshal JSON" are incorrect here since json.Marshal is being called.

🐛 Suggested fix

Update the error constant and usage:

 const (
 	errNotImplemented             = "not implemented"
-	errJSONUnmarshal              = "failed to unmarshal JSON"
+	errJSONMarshal                = "failed to marshal JSON"
 	...
 )
 		out, err := json.Marshal(keyToValue)
 		if err != nil {
-			return nil, errors.New(errJSONUnmarshal)
+			return nil, fmt.Errorf("%s: %w", errJSONMarshal, err)
 		}
🧹 Nitpick comments (7)
providers/v1/nebius/common/sdk/mysterybox/client.go (1)

1-13: Unusual license header format.

The license header uses // /* which appears to be double-commented. This differs from the standard license format used elsewhere in the codebase. Consider using the standard format without the leading // on each line.

♻️ Suggested fix
-// /*
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-// */
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
providers/v1/nebius/common/sdk/mysterybox/grpc_client_test.go (2)

1-13: Unusual license header format.

Same issue as in client.go - the license header uses double-comment format (// /*). Consider using the standard block comment format for consistency.


159-172: Simplify payload entry validation.

The validation logic copies tt.expected into a new map, then deletes matched entries - but the copy on lines 159-162 is unnecessary since tt.expected is already a map[string][]byte. Also, the assertion argument order should be (expected, actual) per testify conventions.

♻️ Suggested simplification
-			expected := make(map[string][]byte, len(tt.expected))
-			for k, v := range tt.expected {
-				expected[k] = v
-			}
-
-			tassert.Equal(t, len(payload.Entries), len(expected))
-			for _, entry := range payload.Entries {
-				value, _ := expected[entry.Key]
-				if (entry.BinaryValue != nil && bytes.Equal(value, entry.BinaryValue)) || (entry.StringValue != "" && bytes.Equal(value, []byte(entry.StringValue))) {
-					delete(expected, entry.Key)
-					continue
-				}
-			}
-			tassert.Empty(t, expected, "not all expected entries found: %+v", expected)
+			tassert.Equal(t, len(tt.expected), len(payload.Entries))
+			for _, entry := range payload.Entries {
+				expectedValue, ok := tt.expected[entry.Key]
+				tassert.True(t, ok, "unexpected key: %s", entry.Key)
+				if entry.BinaryValue != nil {
+					tassert.Equal(t, expectedValue, entry.BinaryValue)
+				} else {
+					tassert.Equal(t, expectedValue, []byte(entry.StringValue))
+				}
+			}
providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (2)

1-13: Unusual license header format.

Same issue as other files - consider using standard block comment format.


38-47: Inconsistent receiver types and potential version ID issue.

Two concerns:

  1. GetSecret and GetSecretByKey use value receivers while Close uses a pointer receiver. For consistency and to avoid copying the struct, consider using pointer receivers throughout.

  2. In GetSecretByKey (line 57), when versionID is empty (requesting latest), the returned PayloadEntry.VersionID will be empty instead of the actual version ID from the secret. This differs from GetSecret which correctly returns secret.VersionId.

♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {
+func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {

-func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
+func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
 	secret, err := f.mysteryboxService.GetSecret(secretID, versionID)
 	if err != nil {
 		return nil, err
 	}
 	for _, entry := range secret.Entries {
 		if entry.Key == key {
 			return &mysterybox.PayloadEntry{
-				VersionID: versionID,
+				VersionID: secret.VersionId,
 				Entry:     entry,
 			}, nil
 		}
 	}

Also applies to: 49-64

providers/v1/nebius/mysterybox/validation_test.go (1)

290-351: LGTM with minor suggestion: TestValidateStore_APIDomainCases provides thorough domain validation coverage.

The test comprehensively validates:

  • Valid domain formats (simple, subdomain, with port, hyphen, uppercase)
  • Invalid formats (single label, empty label, leading/trailing dots, hyphen positioning, invalid characters)
  • Length constraints (label > 63 chars, overall domain length)

Minor suggestion: The error containment check at lines 346-348 could be made conditional to improve clarity:

 if tc.valid {
     tassert.NoError(t, err, "%s: expected valid, got error", tc.name)
 } else {
     tassert.Error(t, err, "%s: expected error for domain %q", tc.name, tc.domain)
-}
-if err != nil {
     tassert.Contains(t, err.Error(), errInvalidAPIDomain, "%s: error should contain invalid api domain", tc.name)
 }

This makes it explicit that the error message check only applies to invalid cases.

providers/v1/nebius/mysterybox/provider.go (1)

35-35: Consider using a clearer import alias.

The alias mysterybox2 is unconventional. Consider a more descriptive alias like mysteryboxsdk or mbclient to clarify it refers to the SDK client package.

♻️ Suggested change
-	mysterybox2 "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"
+	mysteryboxsdk "github.com/external-secrets/external-secrets/providers/v1/nebius/common/sdk/mysterybox"


// HashBytes calculate a hash of the bytes by sha256 algorithm.
func HashBytes(b []byte) string {
sum := sha256.Sum256(b)

Check failure

Code scanning / CodeQL

Use of a broken or weak cryptographic hashing algorithm on sensitive data High

Sensitive data (password)
is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function.
Sensitive data (password)
is used in a hashing algorithm (SHA256) that is insecure for password hashing, since it is not a computationally expensive hash function.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

it's used only for hashing purposes to be used in the cache key, can we leave it as is?

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.

I closed this, should go away.

@greenmapc greenmapc force-pushed the nebius-mysterybox-integration branch from 25a7179 to 0b77a05 Compare January 25, 2026 19:33
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: 2

🤖 Fix all issues with AI agents
In `@providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go`:
- Around line 56-66: GetSecretByKey currently returns the input versionID
instead of the resolved secret version; update the return to use the resolved
secret.VersionId (not the input versionID) when constructing the
mysterybox.PayloadEntry so callers receive the actual version (e.g., set
VersionID: secret.VersionId in the success return inside GetSecretByKey where
you build the PayloadEntry).
- Around line 68-122: MysteryboxService's secretData is accessed concurrently
causing data races; add a sync.RWMutex field (e.g., mu) to the MysteryboxService
struct, initialize it in InitMysteryboxService, use mu.RLock()/mu.RUnlock() in
GetSecret, and use mu.Lock()/mu.Unlock() in CreateSecret and
CreateNewSecretVersion to protect reads and writes to secretData so concurrent
tests are safe.
♻️ Duplicate comments (4)
providers/v1/nebius/mysterybox/token_service.go (1)

81-105: Coalesce concurrent cache misses to avoid duplicate exchanges.
Parallel callers can miss the cache and exchange in parallel; consider a per‑key mutex to coalesce those calls and reduce redundant IAM requests.

providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (1)

65-79: Potential nil dereference when entry.GetData() returns nil.

This issue was previously flagged. If the gRPC response's Data field is nil, accessing entry.GetData().GetKey() or entry.GetData().Payload will cause a panic.

providers/v1/nebius/mysterybox/secrets_client.go (1)

65-68: Error message is misleading - operation is marshalling, not unmarshalling.

This issue was previously flagged. The error constant errJSONUnmarshal and message "failed to unmarshal JSON" are incorrect since json.Marshal is being called.

providers/v1/nebius/mysterybox/provider.go (1)

230-248: Error from sync.Once is not properly propagated on subsequent calls.

This issue was previously flagged. If lru.NewWithEvict fails, the error is captured but cacheOnce.Do will never execute again on subsequent calls. This means later calls to initMysteryboxClientsCache() will return nil error even though initialization failed.

🧹 Nitpick comments (7)
providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (2)

101-117: Same nil dereference risk in GetSecret method.

The GetSecret method iterates over payload.GetData() entries and accesses entry.Key and entry.Payload directly without checking if entries could be nil. While GetData() returns a slice (less likely to be nil itself), individual entries within could still have nil fields depending on the protobuf generation.

More critically, this duplicates the payload-type switch logic from GetSecretByKey. Consider extracting a helper function to reduce duplication and ensure consistent handling.

♻️ Suggested refactor to extract common payload conversion
func convertPayloadToEntry(data *proto.Payload, key string) (Entry, error) {
	if data == nil {
		return Entry{}, fmt.Errorf("received nil data for key: %v", key)
	}
	entry := Entry{Key: data.GetKey()}
	switch data.Payload.(type) {
	case *proto.Payload_StringValue:
		entry.StringValue = data.GetStringValue()
	case *proto.Payload_BinaryValue:
		entry.BinaryValue = data.GetBinaryValue()
	default:
		return Entry{}, fmt.Errorf(notSupportedPayloadType, key)
	}
	return entry, nil
}

Then use this helper in both GetSecretByKey and GetSecret.


132-135: Use named fields in struct literal for clarity.

The struct literal uses positional initialization which can lead to subtle bugs if field order changes.

♻️ Suggested fix
 	return &GrpcClient{
-		mbox.NewPayloadService(mysteryboxSdk),
-		mysteryboxSdk,
+		PayloadService: mbox.NewPayloadService(mysteryboxSdk),
+		sdk:            mysteryboxSdk,
 	}, nil
providers/v1/nebius/common/sdk/mysterybox/fake/fake_client.go (1)

35-49: Inconsistent receiver types between methods.

Close() uses a pointer receiver *FakeMysteryboxClient, while GetSecret() and GetSecretByKey() use value receivers FakeMysteryboxClient. This is inconsistent and could cause issues since value receivers won't see mutations to the Closed field.

♻️ Suggested fix
-func (f FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {
+func (f *FakeMysteryboxClient) GetSecret(_ context.Context, _, secretId, versionId string) (*mysterybox.Payload, error) {
-func (f FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
+func (f *FakeMysteryboxClient) GetSecretByKey(_ context.Context, _, secretID, versionID, key string) (*mysterybox.PayloadEntry, error) {
providers/v1/nebius/mysterybox/secrets_client.go (3)

119-122: Close() does not close the underlying mysteryboxClient.

The Close method returns nil without closing the mysteryboxClient. Since the client is cached at the provider level, this may be intentional, but it's inconsistent with the mysterybox.Client interface which has a Close() method. Consider adding a comment explaining why the client isn't closed here (i.e., it's managed by the provider's cache).

📝 Suggested clarification
 // Close cleans up resources when the provider is done being used.
+// Note: The underlying mysteryboxClient is not closed here because it is
+// managed by the Provider's client cache and may be shared across multiple SecretsClient instances.
 func (c *SecretsClient) Close(_ context.Context) error {
 	return nil
 }

109-112: Validate() always returns Ready without performing validation.

The Validate method unconditionally returns ValidationResultReady without checking if the client can actually connect to the Mysterybox service. This could mask configuration issues. Consider adding at least a basic connectivity check or documenting why validation is skipped.


163-171: Unreachable code path in handleGetSecretByKeyError.

The condition if ref.Property != "" on line 164 will always be true when this function is called, because GetSecretByKey is only invoked when secretKey := ref.Property is non-empty (see line 54). The outer if ref.Property != "" check is redundant.

♻️ Simplified version
 func handleGetSecretByKeyError(err error, ref esv1.ExternalSecretDataRemoteRef) error {
 	if err == nil {
 		return nil
 	}
 	st, ok := status.FromError(err)
 	if !ok {
 		return err // not a grpc error
 	}
 	if st.Code() == codes.NotFound {
-		if ref.Property != "" {
-			if ref.Version != "" {
-				return fmt.Errorf(errSecretVersionByKeyNotFound, ref.Version, ref.Key, ref.Property, esv1.NoSecretErr)
-			}
-			return fmt.Errorf(errSecretByKeyNotFound, ref.Property, ref.Key, esv1.NoSecretErr)
+		if ref.Version != "" {
+			return fmt.Errorf(errSecretVersionByKeyNotFound, ref.Version, ref.Key, ref.Property, esv1.NoSecretErr)
 		}
+		return fmt.Errorf(errSecretByKeyNotFound, ref.Property, ref.Key, esv1.NoSecretErr)
 	}
 	return MapGrpcErrors("get secret", err)
 }
providers/v1/nebius/mysterybox/provider.go (1)

236-242: Variable shadowing: err in eviction callback shadows outer err.

The err variable declared on line 231 is shadowed by err on line 238 inside the eviction callback. If Close() fails, this inner err is logged but doesn't affect the outer err that is returned. While the current logic handles this correctly (the inner error is only logged), the shadowing is confusing and could lead to bugs during future maintenance.

♻️ Suggested fix
 			func(key, value interface{}) {
 				p.Logger.V(1).Info("Evicting a Nebius Mysterybox client", "apiDomain", key.(ClientCacheKey).APIDomain)
-				err := value.(mysterybox2.Client).Close()
-				if err != nil {
-					p.Logger.Error(err, "Failed to close Nebius Mysterybox client")
+				closeErr := value.(mysterybox2.Client).Close()
+				if closeErr != nil {
+					p.Logger.Error(closeErr, "Failed to close Nebius Mysterybox client")
 				}
 			})

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Jan 25, 2026

Hello. I'm not sure how this would differ from our #5057 grpc plugin initiative.

Copy link
Copy Markdown
Contributor

@evrardj-roche evrardj-roche left a comment

Choose a reason for hiding this comment

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

Do you intend to maintain this long term with us?

If so, the most important is indeed to get to know each other and have contact points...

@greenmapc
Copy link
Copy Markdown
Contributor Author

Hello. I'm not sure how this would differ from our #5057 grpc plugin initiative.

Hey @Skarlso,

Thanks for sharing the issue - it’s an interesting one. The idea itself looks solid, and it seems you already have something similar on the HTTP side with webhook provider.

I had been exploring a slightly different approach: building a kind of “bridge” between our gRPC server and the webhook provider contract. The main goal was to make integrations easier. However, after running a few PoCs, I decided to move away from that idea and go with a direct integration instead. It turned out to be a more stable solution overall, especially since I ran into some authorization-related issues with the bridge approach.

Regarding the issue you shared: in general, I agree it could be a good solution. That said, as far as I understand, it would require implementing a dedicated plugin and maintaining the corresponding API (which is pretty the same as for my initial webhook bridge idea).

Also, unless I’m missing something, it doesn’t look like the solution or design has progressed further so far. I’m also a bit concerned that this might become a significant blocker for the integration. Would it be possible to move forward with the current proposal instead?

@evrardj-roche
Copy link
Copy Markdown
Contributor

This is extremely valuable input @greenmapc ! I personally believe it would be unfair to accept other providers directly while refusing Nebius, especially if you are willing to maintain it.

For progress, I can't judge, I did not put my hands into it yet.
Just keep in mind that we have that proposal that was accepted and it might be impactful for you.

However, I would be eager to discuss with you about the issues you encountered with that bridge you tried. I think those are good inputs for our proposal. Would you be okay for that?

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Jan 27, 2026

It hasn't progressed yeah, but @moolen has better insight on that. I'm just afraid that with the GRPC stuff being shared so much, it will become either obsolete, or duplicated a lot.

We'll see. I'm unsure. This is a ton of code so it's going to take quite some time to go through it and see if it fits the ecosystem and our guidelines. Not saying no, ofc. I'm just saying as a heads up that it's going to take a lot. :)

@greenmapc
Copy link
Copy Markdown
Contributor Author

Hey @evrardj-roche!

Thank you so much for your reply! Yes, of course, let's discuss it! Would you like to discuss this separately, outside of PR?

@greenmapc
Copy link
Copy Markdown
Contributor Author

greenmapc commented Jan 28, 2026

@Skarlso regarding your comment - I agree that there’s already quite a bit of duplication between providers, since they all follow very similar logic to communicate with backends (HTTP or gRPC).

It might be useful to have a shared library for gRPC connections and caching that follows common guidelines and makes provider integrations easier. Just thinking out loud here, but if you have other thoughts, maybe this is something we could start shaping either in this PR or in a follow-up. I’d be happy to contribute, I’ve already spent some time researching the project.

I also understand that this is a fairly large amount of code and may take time to review. I’d really appreciate it if you could find the time to take a look at our integration 🙂

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: 2

🤖 Fix all issues with AI agents
In `@providers/v1/nebius/mysterybox/provider_test.go`:
- Around line 553-731: Fix the failing/unclear assertion messages by replacing
uses of &factoryCalls (pointer) in the fmt.Sprintf calls with the actual count
(atomic.LoadInt32(&factoryCalls)) and ensure the "want" value matches the
asserted expected value; update the messages in
TestCreateOrGetMysteryboxClient_CachesByKey,
TestCreateOrGetMysteryboxClient_EmptyCA_EqualsNil,
TestCreateOrGetMysteryboxClient_Concurrent_SingleClient, and
TestCreateOrGetMysteryboxClient_Concurrent_MultipleClients so the formatted
string prints the current factory call count and the correct expected number
(e.g., for the last test the message should say want 4 to match tassert.Equal(t,
int32(4), ...)).

In `@providers/v1/nebius/mysterybox/secrets_client.go`:
- Around line 155-172: The error mapping in handleGetSecretByKeyError uses a
generic operation label ("get secret") which loses the by-key context; update
the final MapGrpcErrors call in handleGetSecretByKeyError to use a more specific
operation string like "get secret by key" (or similar) so logs and mapped errors
reflect the by-key operation; locate the handleGetSecretByKeyError function and
replace the operation argument passed to MapGrpcErrors("get secret", err) with
the new descriptive label.
🧹 Nitpick comments (5)
providers/v1/nebius/common/sdk/mysterybox/grpc_client.go (2)

105-121: Consider adding nil checks for entries in the loop.

Unlike GetSecretByKey which now validates entry.GetData() != nil, GetSecret iterates over payload.GetData() without checking if individual entries or entry.Payload are nil before the type switch. If the gRPC response contains a nil entry, this could panic.

🛡️ Proposed defensive check
 	payloadEntries := make([]Entry, 0, len(payload.Data))
 	for _, entry := range payload.GetData() {
+		if entry == nil {
+			continue
+		}
 		payloadEntry := Entry{
 			Key: entry.Key,
 		}

136-139: Use named fields in struct initialization.

Positional struct initialization can silently break if fields are reordered or new fields are added to GrpcClient. Using named fields improves maintainability.

♻️ Suggested change
 	return &GrpcClient{
-		mbox.NewPayloadService(mysteryboxSdk),
-		mysteryboxSdk,
+		PayloadService: mbox.NewPayloadService(mysteryboxSdk),
+		sdk:            mysteryboxSdk,
 	}, nil
apis/externalsecrets/v1/secretstore_nebius_types.go (2)

44-47: Add +optional marker and omitempty to Certificate field.

The Certificate field in NebiusCAProvider has omitempty in its JSON tag but lacks the +optional kubebuilder marker. For consistency with other optional fields in the codebase, consider adding the marker. Additionally, if the CA provider itself is optional and Certificate is the only field, users might create an empty caProvider: {} block. Consider whether this struct should also have validation.

♻️ Suggested change
 // NebiusCAProvider The provider for the CA bundle to use to validate Nebius server certificate.
 type NebiusCAProvider struct {
+	// +optional
 	Certificate esmeta.SecretKeySelector `json:"certSecretRef,omitempty"`
 }

55-56: Use // comment style for consistency.

The comment on line 55 uses /* */ style while all other comments in this file use //. Using consistent comment style improves readability.

♻️ Suggested change
-	/* Auth defines parameters to authenticate in Mysterybox */
+	// Auth defines parameters to authenticate in Mysterybox.
 	Auth NebiusAuth `json:"auth"`
config/crds/bases/external-secrets.io_secretstores.yaml (1)

3788-3791: Consider dropping provider-specific CEL validation for auth.

To keep CRD validations consistent across providers, it may be better to rely on runtime validation/documentation instead of adding a provider-specific x-kubernetes-validations rule here.

♻️ Suggested change
-                        x-kubernetes-validations:
-                        - message: either serviceAccountCredsSecretRef or tokenSecretRef
-                            must be set
-                          rule: has(self.serviceAccountCredsSecretRef) || has(self.tokenSecretRef)

Based on learnings: In CRD YAML files under config/crds, avoid provider-specific schema validations (e.g., format: uri or CEL rules) when other providers don’t use them; prioritize consistency across providers.

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

🤖 Fix all issues with AI agents
In `@go.mod`:
- Line 341: Update the Nebius SDK entry in go.mod by changing the version string
for github.com/nebius/gosdk from v0.0.0-20260122112135-b2ef6b4c6a76 to the newer
v0.0.0-20260204094009-511fd4d4f7a1; run `go get
github.com/nebius/gosdk@v0.0.0-20260204094009-511fd4d4f7a1` (or `go mod tidy`)
to apply and verify builds, and add a short note in the repository docs (or
changelog) explaining that nebuis/gosdk is a pre-1.0 module and that an older
commit should be pinned explicitly if compatibility is required.

@greenmapc greenmapc force-pushed the nebius-mysterybox-integration branch from 233d348 to 557cb75 Compare February 6, 2026 14:33
@greenmapc
Copy link
Copy Markdown
Contributor Author

@Skarlso
Hey! I've just fixed all issues from AI agent.

I can't make CI green, looks like linter does not work for the new provider's module. I thought there is a new model/code problem, but I looked around and saw that other PRs where new providers have been added experience the same problem!

Also, there is an highlighted issue regarding SHA256. I don't think it's a real issue here, because it's used only for hashing private keys as a key for the cache. If you agree, can we skip it?

Whenever you get a moment to look at it, I’d love to hear your feedback! Thanks.

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Feb 9, 2026

@greenmapc Hey Anna!

Thanks so much for working on this. I'm still trying to get the other members together on the consensus with this, because there is just so much overlap with the GRPC plugin work we are doing to get out-of-tree providers rolling. Once we go ahead with that, this is going to be completely obsolete and/or would have to be entirely rewritten or consist of a lot of duplicate work.

We are going to meet at KubeCon with the person who is writing that PR, and I'm trying to discuss this in the next community meeting as well, which I'm hoping will have more of the core maintainers joining. It's going to be on the 18th of February. UTC 12:00.

If you would like to further discuss this, please try and join the meeting, or look at the recording for the potential outcome.

Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
@greenmapc greenmapc force-pushed the nebius-mysterybox-integration branch from ccd7585 to 32f906f Compare March 6, 2026 13:41
func NewSDK(ctx context.Context, apiDomain string, caCertificate []byte) (*gosdk.SDK, error) {
tlsCfg := &tls.Config{MinVersion: tls.VersionTLS12}

if caCertificate != nil && len(caCertificate) > 0 {
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.

Just a nit, len check already work with slices and nil. No need for the nil check here.

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Mar 6, 2026

One last nit. :D Otherwise, this is now fine for me.

@moolen Would you like to have one more round?

Signed-off-by: annakuzmenko <annakuzmenko@nebius.com>
@moolen
Copy link
Copy Markdown
Member

moolen commented Mar 6, 2026

👍 It's fine for me to get it in, though i have concerns regarding the missing gRPC Close(). It would be great if we would be able to resolve this soon and have a dedicated regression test for this case. (e.g. SecretStore / domain churn). Though not a blocker, for me at least.

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Mar 6, 2026

Same here. I would ask you @greenmapc if you could monitor some memory usage of the controller while using this provider and then report back on any problems? :) But for now, I would say this is a strong enough alpha.

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Mar 6, 2026

@greenmapc can you please rebase / update? :)

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 6, 2026

@greenmapc
Copy link
Copy Markdown
Contributor Author

greenmapc commented Mar 6, 2026

I would ask you @greenmapc if you could monitor some memory usage of the controller while using this provider and then report back on any problems? :) But for now, I would say this is a strong enough alpha.

Of course! I will deploy it in all our regions (when it will be released) and will monitor :) In case of problems, I will open an issue here and will work on it. Btw, for the gRPC Close() issue, should I open an issue already rn?

@Skarlso
Copy link
Copy Markdown
Contributor

Skarlso commented Mar 6, 2026

@greenmapc Yes please, let's not forget that that is something we really need to fix.

@Skarlso Skarlso dismissed evrardj-roche’s stale review March 6, 2026 17:14

Thanks, concerns have been addressed I believe.

@Skarlso Skarlso merged commit 0e7f449 into external-secrets:main Mar 6, 2026
31 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in External Secrets Mar 6, 2026
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Mar 6, 2026
…2.1.0 (#4491)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [external-secrets/external-secrets](https://github.com/external-secrets/external-secrets) | minor | `v2.0.1` → `v2.1.0` |

---

### Release Notes

<details>
<summary>external-secrets/external-secrets (external-secrets/external-secrets)</summary>

### [`v2.1.0`](https://github.com/external-secrets/external-secrets/releases/tag/v2.1.0)

[Compare Source](external-secrets/external-secrets@v2.0.1...v2.1.0)

Image: `ghcr.io/external-secrets/external-secrets:v2.1.0`
Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi`
Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi-boringssl`

<!-- Release notes generated using configuration in .github/release.yml at main -->

#### What's Changed

##### General

- chore(release): Update helm chart by [@&#8203;evrardj-roche](https://github.com/evrardj-roche) in [#&#8203;5981](external-secrets/external-secrets#5981)
- fix: cosign verify does not use signing config by [@&#8203;gusfcarvalho](https://github.com/gusfcarvalho) in [#&#8203;5982](external-secrets/external-secrets#5982)
- docs: Update release process by [@&#8203;evrardj-roche](https://github.com/evrardj-roche) in [#&#8203;5980](external-secrets/external-secrets#5980)
- fix: allow cross-namespace push with ClusterSecretStore objects by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5998](external-secrets/external-secrets#5998)
- feat(charts): add new flag enable leader for cert-manager by [@&#8203;nutmos](https://github.com/nutmos) in [#&#8203;5863](external-secrets/external-secrets#5863)
- feat(kubernetes): fall back to system CA roots when no CA is configured by [@&#8203;rajsinghtech](https://github.com/rajsinghtech) in [#&#8203;5961](external-secrets/external-secrets#5961)
- feat: dedup sbom but keep it monolithic by [@&#8203;moolen](https://github.com/moolen) in [#&#8203;6004](external-secrets/external-secrets#6004)
- fix: add missing metrics and fundamentally fix the caching logic by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5894](external-secrets/external-secrets#5894)
- docs: designate Oracle Vault provider as 'stable' by [@&#8203;anders-swanson](https://github.com/anders-swanson) in [#&#8203;6020](external-secrets/external-secrets#6020)
- docs: Oracle Vault provider capabilities by [@&#8203;anders-swanson](https://github.com/anders-swanson) in [#&#8203;6023](external-secrets/external-secrets#6023)
- docs(azurekv): cert-manager pushsecret example and cleanups by [@&#8203;illrill](https://github.com/illrill) in [#&#8203;5972](external-secrets/external-secrets#5972)
- feat(kubernetes): implement SecretExists by [@&#8203;Saku2](https://github.com/Saku2) in [#&#8203;5973](external-secrets/external-secrets#5973)
- fix(charts): Fix wrongly set annotations for cert-controller metrics service by [@&#8203;josemaia](https://github.com/josemaia) in [#&#8203;6029](external-secrets/external-secrets#6029)
- feat(providers): Nebius MysteryBox integration by [@&#8203;greenmapc](https://github.com/greenmapc) in [#&#8203;5868](external-secrets/external-secrets#5868)

##### Dependencies

- chore(deps): bump aquasecurity/trivy-action from 0.34.0 to 0.34.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5986](external-secrets/external-secrets#5986)
- chore(deps): bump mkdocs-material from 9.7.1 to 9.7.2 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5992](external-secrets/external-secrets#5992)
- chore(deps): bump ubi9/ubi from `b8923f5` to `cecb1cd` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5984](external-secrets/external-secrets#5984)
- chore(deps): bump helm/kind-action from 1.13.0 to 1.14.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5985](external-secrets/external-secrets#5985)
- chore(deps): bump actions/dependency-review-action from 4.8.2 to 4.8.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5990](external-secrets/external-secrets#5990)
- chore(deps): bump github/codeql-action from 4.32.3 to 4.32.4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5989](external-secrets/external-secrets#5989)
- chore(deps): bump goreleaser/goreleaser-action from 6.4.0 to 7.0.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5987](external-secrets/external-secrets#5987)
- chore(deps): bump regex from 2026.1.15 to 2026.2.19 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5991](external-secrets/external-secrets#5991)
- chore(deps): bump actions/stale from 10.1.1 to 10.2.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5988](external-secrets/external-secrets#5988)
- chore(deps): bump regex from 2026.2.19 to 2026.2.28 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6012](external-secrets/external-secrets#6012)
- chore(deps): bump mkdocs-material from 9.7.2 to 9.7.3 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6014](external-secrets/external-secrets#6014)
- chore(deps): bump step-security/harden-runner from 2.14.2 to 2.15.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6015](external-secrets/external-secrets#6015)
- chore(deps): bump anchore/sbom-action from 0.22.2 to 0.23.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6016](external-secrets/external-secrets#6016)
- chore(deps): bump certifi from 2026.1.4 to 2026.2.25 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6013](external-secrets/external-secrets#6013)
- chore(deps): bump actions/setup-go from 6.2.0 to 6.3.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6010](external-secrets/external-secrets#6010)
- chore(deps): bump hashicorp/setup-terraform from [`ce70bcf`](external-secrets/external-secrets@ce70bcf) to [`5e8dbf3`](external-secrets/external-secrets@5e8dbf3) by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6011](external-secrets/external-secrets#6011)
- chore(deps): bump actions/attest-build-provenance from 3.2.0 to 4.1.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6009](external-secrets/external-secrets#6009)
- chore(deps): bump distroless/static from `972618c` to `28efbe9` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6008](external-secrets/external-secrets#6008)

#### New Contributors

- [@&#8203;nutmos](https://github.com/nutmos) made their first contribution in [#&#8203;5863](external-secrets/external-secrets#5863)
- [@&#8203;rajsinghtech](https://github.com/rajsinghtech) made their first contribution in [#&#8203;5961](external-secrets/external-secrets#5961)
- [@&#8203;illrill](https://github.com/illrill) made their first contribution in [#&#8203;5972](external-secrets/external-secrets#5972)
- [@&#8203;Saku2](https://github.com/Saku2) made their first contribution in [#&#8203;5973](external-secrets/external-secrets#5973)
- [@&#8203;greenmapc](https://github.com/greenmapc) made their first contribution in [#&#8203;5868](external-secrets/external-secrets#5868)

**Full Changelog**: <external-secrets/external-secrets@v2.0.1...v2.1.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41MS4wIiwidXBkYXRlZEluVmVyIjoiNDMuNTEuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiaW1hZ2UiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4491
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
alexlebens pushed a commit to alexlebens/infrastructure that referenced this pull request Mar 7, 2026
This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [external-secrets](https://github.com/external-secrets/external-secrets) | minor | `2.0.1` → `2.1.0` |

---

### Release Notes

<details>
<summary>external-secrets/external-secrets (external-secrets)</summary>

### [`v2.1.0`](https://github.com/external-secrets/external-secrets/releases/tag/v2.1.0)

[Compare Source](external-secrets/external-secrets@v2.0.1...v2.1.0)

Image: `ghcr.io/external-secrets/external-secrets:v2.1.0`
Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi`
Image: `ghcr.io/external-secrets/external-secrets:v2.1.0-ubi-boringssl`

<!-- Release notes generated using configuration in .github/release.yml at main -->

#### What's Changed

##### General

- chore(release): Update helm chart by [@&#8203;evrardj-roche](https://github.com/evrardj-roche) in [#&#8203;5981](external-secrets/external-secrets#5981)
- fix: cosign verify does not use signing config by [@&#8203;gusfcarvalho](https://github.com/gusfcarvalho) in [#&#8203;5982](external-secrets/external-secrets#5982)
- docs: Update release process by [@&#8203;evrardj-roche](https://github.com/evrardj-roche) in [#&#8203;5980](external-secrets/external-secrets#5980)
- fix: allow cross-namespace push with ClusterSecretStore objects by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5998](external-secrets/external-secrets#5998)
- feat(charts): add new flag enable leader for cert-manager by [@&#8203;nutmos](https://github.com/nutmos) in [#&#8203;5863](external-secrets/external-secrets#5863)
- feat(kubernetes): fall back to system CA roots when no CA is configured by [@&#8203;rajsinghtech](https://github.com/rajsinghtech) in [#&#8203;5961](external-secrets/external-secrets#5961)
- feat: dedup sbom but keep it monolithic by [@&#8203;moolen](https://github.com/moolen) in [#&#8203;6004](external-secrets/external-secrets#6004)
- fix: add missing metrics and fundamentally fix the caching logic by [@&#8203;Skarlso](https://github.com/Skarlso) in [#&#8203;5894](external-secrets/external-secrets#5894)
- docs: designate Oracle Vault provider as 'stable' by [@&#8203;anders-swanson](https://github.com/anders-swanson) in [#&#8203;6020](external-secrets/external-secrets#6020)
- docs: Oracle Vault provider capabilities by [@&#8203;anders-swanson](https://github.com/anders-swanson) in [#&#8203;6023](external-secrets/external-secrets#6023)
- docs(azurekv): cert-manager pushsecret example and cleanups by [@&#8203;illrill](https://github.com/illrill) in [#&#8203;5972](external-secrets/external-secrets#5972)
- feat(kubernetes): implement SecretExists by [@&#8203;Saku2](https://github.com/Saku2) in [#&#8203;5973](external-secrets/external-secrets#5973)
- fix(charts): Fix wrongly set annotations for cert-controller metrics service by [@&#8203;josemaia](https://github.com/josemaia) in [#&#8203;6029](external-secrets/external-secrets#6029)
- feat(providers): Nebius MysteryBox integration by [@&#8203;greenmapc](https://github.com/greenmapc) in [#&#8203;5868](external-secrets/external-secrets#5868)

##### Dependencies

- chore(deps): bump aquasecurity/trivy-action from 0.34.0 to 0.34.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5986](external-secrets/external-secrets#5986)
- chore(deps): bump mkdocs-material from 9.7.1 to 9.7.2 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5992](external-secrets/external-secrets#5992)
- chore(deps): bump ubi9/ubi from `b8923f5` to `cecb1cd` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5984](external-secrets/external-secrets#5984)
- chore(deps): bump helm/kind-action from 1.13.0 to 1.14.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5985](external-secrets/external-secrets#5985)
- chore(deps): bump actions/dependency-review-action from 4.8.2 to 4.8.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5990](external-secrets/external-secrets#5990)
- chore(deps): bump github/codeql-action from 4.32.3 to 4.32.4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5989](external-secrets/external-secrets#5989)
- chore(deps): bump goreleaser/goreleaser-action from 6.4.0 to 7.0.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5987](external-secrets/external-secrets#5987)
- chore(deps): bump regex from 2026.1.15 to 2026.2.19 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5991](external-secrets/external-secrets#5991)
- chore(deps): bump actions/stale from 10.1.1 to 10.2.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5988](external-secrets/external-secrets#5988)
- chore(deps): bump regex from 2026.2.19 to 2026.2.28 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6012](external-secrets/external-secrets#6012)
- chore(deps): bump mkdocs-material from 9.7.2 to 9.7.3 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6014](external-secrets/external-secrets#6014)
- chore(deps): bump step-security/harden-runner from 2.14.2 to 2.15.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6015](external-secrets/external-secrets#6015)
- chore(deps): bump anchore/sbom-action from 0.22.2 to 0.23.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6016](external-secrets/external-secrets#6016)
- chore(deps): bump certifi from 2026.1.4 to 2026.2.25 in /hack/api-docs by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6013](external-secrets/external-secrets#6013)
- chore(deps): bump actions/setup-go from 6.2.0 to 6.3.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6010](external-secrets/external-secrets#6010)
- chore(deps): bump hashicorp/setup-terraform from [`ce70bcf`](external-secrets/external-secrets@ce70bcf) to [`5e8dbf3`](external-secrets/external-secrets@5e8dbf3) by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6011](external-secrets/external-secrets#6011)
- chore(deps): bump actions/attest-build-provenance from 3.2.0 to 4.1.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6009](external-secrets/external-secrets#6009)
- chore(deps): bump distroless/static from `972618c` to `28efbe9` by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;6008](external-secrets/external-secrets#6008)

#### New Contributors

- [@&#8203;nutmos](https://github.com/nutmos) made their first contribution in [#&#8203;5863](external-secrets/external-secrets#5863)
- [@&#8203;rajsinghtech](https://github.com/rajsinghtech) made their first contribution in [#&#8203;5961](external-secrets/external-secrets#5961)
- [@&#8203;illrill](https://github.com/illrill) made their first contribution in [#&#8203;5972](external-secrets/external-secrets#5972)
- [@&#8203;Saku2](https://github.com/Saku2) made their first contribution in [#&#8203;5973](external-secrets/external-secrets#5973)
- [@&#8203;greenmapc](https://github.com/greenmapc) made their first contribution in [#&#8203;5868](external-secrets/external-secrets#5868)

**Full Changelog**: <external-secrets/external-secrets@v2.0.1...v2.1.0>

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My41OS4yIiwidXBkYXRlZEluVmVyIjoiNDMuNTkuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOlsiY2hhcnQiXX0=-->

Reviewed-on: https://gitea.alexlebens.dev/alexlebens/infrastructure/pulls/4516
Co-authored-by: Renovate Bot <renovate-bot@alexlebens.net>
Co-committed-by: Renovate Bot <renovate-bot@alexlebens.net>
evs-secops pushed a commit to evs-secops/external-secrets that referenced this pull request Mar 8, 2026
Signed-off-by: Evyatar Shtern <evyatar.shtern@gong.io>
@coderabbitai coderabbitai bot mentioned this pull request Mar 16, 2026
5 tasks
AlexOQ pushed a commit to AlexOQ/external-secrets that referenced this pull request Mar 18, 2026
Signed-off-by: AlexOQ <30403857+AlexOQ@users.noreply.github.com>
dsp0x4 pushed a commit to dsp0x4/external-secrets that referenced this pull request Mar 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/providers kind/dependency dependabot and upgrades kind/documentation Categorizes issue or PR as related to documentation. kind/feature Categorizes issue or PR as related to a new feature. size/l

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Integration with Nebius Mysterybox Secret Service

5 participants