Skip to content

docs(sdk/rust): clarify SecretsManager::new() deferred bind lifecycle (KSM-973)#1024

Open
maksimu wants to merge 1 commit into
masterfrom
fix/ksm-973-rust-sdk-bind-lifecycle
Open

docs(sdk/rust): clarify SecretsManager::new() deferred bind lifecycle (KSM-973)#1024
maksimu wants to merge 1 commit into
masterfrom
fix/ksm-973-rust-sdk-bind-lifecycle

Conversation

@maksimu
Copy link
Copy Markdown
Collaborator

@maksimu maksimu commented May 25, 2026

Summary

SecretsManager::new() does not make any network calls — the one-time token is redeemed on the first get_secrets() call. This contract was previously undocumented and caused real integration bugs (most recently while integrating the Rust SDK into KeeperDB).

What changes

Doc only — no public API change.

  • Expanded the doc comment on SecretsManager::new() to make the deferred-bind contract explicit, explain when the storage is "unbound" (missing appKey and appOwnerPublicKey), and warn against persisting the config before the first network call.
  • Updated the inline example to call secrets_manager.get_secrets(vec![])? immediately after new() with an explanatory comment.

Why it matters

If an integrator reads from the SDK's storage and persists it (to OS keychain, AWS Secrets Manager, HashiCorp Vault, …) between new() and the first network call, the saved blob is missing appKey + appOwnerPublicKey. On the next run, loading that blob produces a SecretsManager that cannot fetch secrets. The fix is to force the bind by calling get_secrets(vec![]) first — but until now nothing in the public surface signalled this.

Tests

New test file sdk/rust/tests/bind_lifecycle_tests.rs with four tests covering the bind lifecycle contract:

Test Asserts
test_new_does_not_perform_network_call new() never invokes the HTTP layer — verified by injecting a panicking custom_post_function
test_storage_is_unbound_after_new appKey and appOwnerPublicKey are absent immediately after new()
test_pre_bind_keys_present_after_new clientId, privateKey, hostname ARE present after new() (written locally, no network)
test_bound_config_can_be_reloaded_without_token A persisted bound config can be loaded by a fresh SecretsManager with no token (the second-run path)

All four pass locally: cargo test --test bind_lifecycle_tests4 passed; 0 failed.

Follow-ups (not in this PR)

  • KSM-974 covers mirroring this lifecycle documentation across the other SDK pages (Python, JS, Java, .NET, Go) on docs.keeper.io.
  • A future change could add explicit bind() and is_bound() convenience methods, but that needs to be done consistently across all SDKs — out of scope here.

Jira: KSM-973

… (KSM-973)

SecretsManager::new() does not make any network calls — the one-time token is
redeemed on the first get_secrets() call. Before that call the storage backend
is 'unbound' and is missing appKey + appOwnerPublicKey, which means an exported
copy cannot be loaded into a new SecretsManager later.

This was previously undocumented and the canonical example stopped at new(),
implying initialisation was complete. An integrator persisting the config to
an external store (OS keychain, AWS Secrets Manager, KMS, etc.) between new()
and the first network call ended up with an unbound config that broke on
restart (reported during a KeeperDB integration).

Changes:
- Expand the doc comment on SecretsManager::new() to make the deferred-bind
  contract explicit and add a worked example that calls get_secrets(vec![])
  immediately after new().
- Add 4 unit tests in tests/bind_lifecycle_tests.rs covering:
  - new() does not make a network call (verified via panicking custom_post_function)
  - storage is unbound after new() (no appKey / appOwnerPublicKey)
  - pre-bind keys (clientId, privateKey, hostname) are present after new()
  - a fully-bound config can be reloaded without a token

No public API change.
@maksimu maksimu force-pushed the fix/ksm-973-rust-sdk-bind-lifecycle branch from 4a1ba7f to 05ae679 Compare May 25, 2026 07:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant