Client

Struct Client 

Source
pub struct Client { /* private fields */ }
Expand description

XJP Secret Store client

The main client for interacting with the XJP Secret Store API. Provides methods for managing secrets, including get, put, delete, and batch operations. Supports caching, retries, and conditional requests.

Implementations§

Source§

impl Client

Source

pub fn cache_stats(&self) -> &CacheStats

Get cache statistics

Returns statistics about the cache including hit rate, number of hits/misses, and evictions. Useful for monitoring cache performance.

§Example
let stats = client.cache_stats();
println!("Cache hit rate: {:.2}%", stats.hit_rate());
println!("Total hits: {}, misses: {}", stats.hits(), stats.misses());
Source

pub fn clear_cache(&self)

Clear the cache

Removes all entries from the cache and resets cache statistics. This is useful when you need to force fresh data retrieval.

§Example
// Clear all cached secrets
client.clear_cache();
Source

pub async fn invalidate_cache(&self, namespace: &str, key: &str)

Invalidate a specific cache entry

Removes a single secret from the cache, forcing the next retrieval to fetch fresh data from the server.

§Arguments
  • namespace - The namespace of the secret
  • key - The key of the secret
§Example
// Invalidate a specific secret from cache
client.invalidate_cache("production", "api-key").await;
Source

pub async fn get_secret( &self, namespace: &str, key: &str, opts: GetOpts, ) -> Result<Secret>

Get a secret from the store

Retrieves a secret value from the specified namespace and key. Supports caching and conditional requests via ETags.

§Arguments
  • namespace - The namespace containing the secret
  • key - The key identifying the secret
  • opts - Options controlling cache usage and conditional requests
§Returns

The secret value with metadata on success, or an error if the secret doesn’t exist or access is denied.

§Errors
  • Error::Http with status 404 if the secret doesn’t exist
  • Error::Http with status 403 if access is denied
  • Error::Http with status 401 if authentication fails
  • Error::Network for connection issues
  • Error::Timeout if the request times out
§Example
// Simple get with default options (cache enabled)
let secret = client.get_secret("production", "database-url", GetOpts::default()).await?;
println!("Secret version: {}", secret.version);

// Get without cache
let opts = GetOpts { use_cache: false, ..Default::default() };
let fresh_secret = client.get_secret("production", "api-key", opts).await?;

// Conditional get with ETag
let opts = GetOpts {
    if_none_match: Some(secret.etag.unwrap()),
    ..Default::default()
};
match client.get_secret("production", "database-url", opts).await {
    Ok(updated) => println!("Secret was updated"),
    Err(e) if e.status_code() == Some(304) => println!("Not modified"),
    Err(e) => return Err(e.into()),
}
Source

pub async fn put_secret( &self, namespace: &str, key: &str, value: impl Into<String>, opts: PutOpts, ) -> Result<PutResult>

Put a secret into the store

Creates or updates a secret in the specified namespace. Automatically invalidates any cached value for this key.

§Arguments
  • namespace - The namespace to store the secret in
  • key - The key for the secret
  • value - The secret value (will be securely stored)
  • opts - Options including TTL, metadata, and idempotency key
§Returns

A PutResult containing the operation details and timestamp.

§Security

The secret value is transmitted over HTTPS and stored encrypted. The SDK uses the secrecy crate to prevent accidental exposure of secret values in logs or debug output.

§Example
// Simple put
client.put_secret("production", "new-key", "secret-value", PutOpts::default()).await?;

// Put with TTL and metadata
let opts = PutOpts {
    ttl_seconds: Some(3600), // Expires in 1 hour
    metadata: Some(json!({
        "owner": "backend-team",
        "rotation_date": "2024-12-01"
    })),
    idempotency_key: Some("deploy-12345".to_string()),
};
client.put_secret("production", "api-key", "new-api-key", opts).await?;
Source

pub async fn delete_secret( &self, namespace: &str, key: &str, ) -> Result<DeleteResult>

Delete a secret from the store

Source

pub async fn list_secrets( &self, namespace: &str, opts: ListOpts, ) -> Result<ListSecretsResult>

List secrets in a namespace

Source

pub async fn batch_get( &self, namespace: &str, keys: BatchKeys, format: ExportFormat, ) -> Result<BatchGetResult>

Batch get secrets

Source

pub async fn batch_operate( &self, namespace: &str, operations: Vec<BatchOp>, transactional: bool, idempotency_key: Option<String>, ) -> Result<BatchOperateResult>

Batch operate on secrets

Source

pub async fn export_env( &self, namespace: &str, opts: ExportEnvOpts, ) -> Result<EnvExport>

Export secrets as environment variables

Exports all secrets from a namespace in the specified format. Supports conditional requests using ETag for efficient caching.

§Arguments
  • namespace - The namespace to export
  • opts - Export options including format and conditional request headers
§Returns

Returns EnvExport::Json for JSON format or EnvExport::Text for other formats.

§Errors
  • Returns Error::Http with status 304 if content hasn’t changed (when using if_none_match)
  • Returns other errors for authentication, network, or server issues
§Example
// Simple export
let opts = ExportEnvOpts {
    format: ExportFormat::Dotenv,
    ..Default::default()
};
let export = client.export_env("production", opts).await?;

// Conditional export with ETag
let opts = ExportEnvOpts {
    format: ExportFormat::Json,
    use_cache: true,
    if_none_match: Some("previous-etag".to_string()),
};
match client.export_env("production", opts).await {
    Ok(export) => println!("Content updated"),
    Err(e) if e.status_code() == Some(304) => println!("Not modified"),
    Err(e) => return Err(e.into()),
}
Source

pub async fn list_namespaces(&self) -> Result<ListNamespacesResult>

List all namespaces

Source

pub async fn create_namespace( &self, name: &str, description: Option<String>, idempotency_key: Option<String>, ) -> Result<CreateNamespaceResult>

Create a new namespace

Creates a new namespace in the secret store. Requires global admin permissions.

§Arguments
  • name - The name of the namespace to create
  • description - Optional description for the namespace
  • idempotency_key - Optional idempotency key to prevent duplicate creation
§Returns

A CreateNamespaceResult containing the creation details.

§Errors
  • Error::Http with status 403 if not authorized to create namespaces
  • Error::Http with status 409 if the namespace already exists
  • Error::Http with status 400 for invalid namespace names
§Example
let result = client.create_namespace(
    "production",
    Some("Production environment secrets".to_string()),
    None
).await?;
println!("Created namespace: {}", result.namespace);
Source

pub async fn get_namespace(&self, namespace: &str) -> Result<NamespaceInfo>

Get namespace information

Source

pub async fn init_namespace( &self, namespace: &str, template: NamespaceTemplate, idempotency_key: Option<String>, ) -> Result<InitNamespaceResult>

Initialize a namespace with a template

Initializes a new namespace using a predefined template to create a set of initial secrets.

§Arguments
  • namespace - The namespace to initialize
  • template - The template configuration
  • idempotency_key - Optional idempotency key to prevent duplicate initialization
§Example
let template = NamespaceTemplate {
    template: "web-app".to_string(),
    params: json!({
        "environment": "staging",
        "region": "us-west-2"
    }),
};

let result = client.init_namespace(
    "staging-app",
    template,
    Some("init-staging-12345".to_string())
).await?;
println!("Created {} secrets", result.secrets_created);
Source

pub async fn delete_namespace( &self, namespace: &str, ) -> Result<DeleteNamespaceResult>

Delete a namespace and all its secrets

Warning: This operation is irreversible and will delete all secrets in the namespace. Use with extreme caution.

This operation may take some time for namespaces with many secrets. The response includes the number of secrets that were deleted.

§Arguments
  • namespace - The namespace to delete
§Returns

A DeleteNamespaceResult containing deletion details.

§Errors
  • Error::Http with status 404 if the namespace doesn’t exist
  • Error::Http with status 403 if deletion is forbidden
  • Error::Http with status 409 if namespace has protection enabled
§Example
let result = client.delete_namespace("test-namespace").await?;
println!("Deleted {} secrets from namespace {}",
    result.secrets_deleted,
    result.namespace
);
Source

pub async fn delete_namespace_idempotent( &self, namespace: &str, idempotency_key: Option<String>, ) -> Result<DeleteNamespaceResult>

Delete a namespace and all its secrets with idempotency support

Same as delete_namespace but with idempotency key support for safe retries.

§Arguments
  • namespace - The namespace to delete
  • idempotency_key - Optional idempotency key to prevent duplicate deletion
§Example
let result = client.delete_namespace_idempotent(
    "test-namespace",
    Some("delete-ns-12345".to_string())
).await?;
println!("Deleted {} secrets", result.secrets_deleted);
Source

pub async fn list_versions( &self, namespace: &str, key: &str, ) -> Result<VersionList>

List versions of a secret

Source

pub async fn get_version( &self, namespace: &str, key: &str, version: i32, ) -> Result<Secret>

Get a specific version of a secret

Source

pub async fn rollback( &self, namespace: &str, key: &str, version: i32, ) -> Result<RollbackResult>

Rollback a secret to a previous version

Source

pub async fn audit(&self, query: AuditQuery) -> Result<AuditResult>

Query audit logs

Source

pub async fn list_api_keys(&self) -> Result<ListApiKeysResult>

List all API keys

Retrieves a list of all API keys associated with the current account. The response includes metadata about each key but not the key values themselves.

§Returns

A ListApiKeysResult containing the list of API keys and total count.

§Errors
  • Error::Http with status 403 if not authorized to list keys
§Example
let keys = client.list_api_keys().await?;
for key in &keys.keys {
    println!("Key {}: {} (active: {})", key.id, key.name, key.active);
}
Source

pub async fn create_api_key( &self, request: CreateApiKeyRequest, idempotency_key: Option<String>, ) -> Result<ApiKeyInfo>

Create a new API key

Creates a new API key with the specified permissions and restrictions. The key value is only returned in the creation response and cannot be retrieved later.

§Arguments
  • request - The API key creation request containing name, permissions, etc.
  • idempotency_key - Optional idempotency key to prevent duplicate creation
§Returns

An ApiKeyInfo containing the newly created key details including the key value.

§Security

The returned API key value should be stored securely. It cannot be retrieved again after this call.

§Errors
  • Error::Http with status 403 if not authorized to create keys
  • Error::Http with status 400 for invalid permissions or parameters
§Example
let request = CreateApiKeyRequest {
    name: "CI/CD Pipeline Key".to_string(),
    expires_at: Some("2024-12-31T23:59:59Z".to_string()),
    namespaces: vec!["production".to_string()],
    permissions: vec!["read".to_string()],
    metadata: None,
};

let key_info = client.create_api_key(request, Some("unique-key-123".to_string())).await?;
if let Some(key) = &key_info.key {
    println!("New API key: {}", key.expose_secret());
    // Store this securely - it won't be available again!
}
Source

pub async fn get_api_key(&self, key_id: &str) -> Result<ApiKeyInfo>

Get API key details

Retrieves detailed information about a specific API key. Note that the key value itself is never returned for security reasons.

§Arguments
  • key_id - The ID of the API key to retrieve
§Returns

An ApiKeyInfo with the key’s metadata (without the key value).

§Errors
  • Error::Http with status 404 if the key doesn’t exist
  • Error::Http with status 403 if not authorized to view the key
§Example
let key_info = client.get_api_key("key_123abc").await?;
println!("Key {} last used: {:?}", key_info.name, key_info.last_used_at);
Source

pub async fn revoke_api_key(&self, key_id: &str) -> Result<RevokeApiKeyResult>

Revoke an API key

Revokes an API key, immediately invalidating it for future use. This operation is irreversible.

§Arguments
  • key_id - The ID of the API key to revoke
§Returns

A RevokeApiKeyResult confirming the revocation.

§Errors
  • Error::Http with status 404 if the key doesn’t exist
  • Error::Http with status 403 if not authorized to revoke the key
§Example
let result = client.revoke_api_key("key_123abc").await?;
println!("Revoked key: {}", result.key_id);
Source

pub async fn search_secrets( &self, opts: SearchSecretsOpts, ) -> Result<SearchSecretsResult>

Search secrets across namespaces

Searches for secrets matching a pattern across multiple namespaces. Supports exact, prefix, and contains matching modes.

§Arguments
  • opts - Search options including pattern, mode, and namespace filters
§Returns

A SearchSecretsResult containing matching secrets.

§Errors
  • Error::Http with status 400 if the pattern is empty
  • Error::Http with status 403 if no accessible namespaces
§Example
// Search for all secrets containing "api" in their key
let opts = SearchSecretsOpts {
    pattern: "api".to_string(),
    mode: SearchMode::Contains,
    ..Default::default()
};
let result = client.search_secrets(opts).await?;
for m in &result.matches {
    println!("{}/{}: v{}", m.namespace, m.key, m.version);
}

// Search with prefix in specific namespaces
let opts = SearchSecretsOpts {
    pattern: "db-".to_string(),
    mode: SearchMode::Prefix,
    namespaces: vec!["production".to_string(), "staging".to_string()],
    include_values: false,
};
let result = client.search_secrets(opts).await?;
println!("Found {} secrets", result.total);
Source

pub async fn bulk_delete_secrets( &self, opts: BulkDeleteOpts, ) -> Result<BulkDeleteResult>

Bulk delete a secret across namespaces

Deletes a secret with the given key from multiple namespaces in a single operation. Requires admin permission on each namespace.

§Arguments
  • opts - Bulk delete options including the key and target namespaces
§Returns

A BulkDeleteResult containing lists of successful and failed deletions.

§Errors
  • Error::Http with status 400 if the key is empty
  • Error::Http with status 403 if no accessible namespaces
§Example
// Delete a secret from all accessible namespaces
let opts = BulkDeleteOpts {
    key: "deprecated-api-key".to_string(),
    namespaces: vec![], // empty = all accessible
};
let result = client.bulk_delete_secrets(opts).await?;
println!("Deleted from: {:?}", result.deleted);
if !result.failed.is_empty() {
    println!("Failed in: {:?}", result.failed);
}

// Delete from specific namespaces only
let opts = BulkDeleteOpts {
    key: "old-secret".to_string(),
    namespaces: vec!["staging".to_string(), "dev".to_string()],
};
let result = client.bulk_delete_secrets(opts).await?;
Source

pub async fn discovery(&self) -> Result<Discovery>

Get API discovery information

Source

pub async fn livez(&self) -> Result<()>

Check liveness

Performs a simple liveness check against the service. Returns Ok(()) if the service is alive and responding.

This endpoint is typically used by Kubernetes liveness probes. It does not check dependencies and should respond quickly.

§Errors

Returns an error if the service is not responding or returns a non-2xx status code.

§Example
match client.livez().await {
    Ok(()) => println!("Service is alive"),
    Err(e) => eprintln!("Service is down: {}", e),
}
Source

pub async fn readyz(&self) -> Result<HealthStatus>

Check readiness with detailed status

Performs a comprehensive readiness check that may include checking dependencies (database, cache, etc.).

This endpoint is typically used by Kubernetes readiness probes to determine if the service is ready to accept traffic.

§Returns

Returns a HealthStatus with details about the service health including individual component checks.

§Errors

Returns an error if the service is not ready or if the request fails.

§Example
let health = client.readyz().await?;
println!("Service status: {}", health.status);

for (check, result) in &health.checks {
    println!("  {}: {} ({}ms)",
        check,
        result.status,
        result.duration_ms.unwrap_or(0)
    );
}
Source

pub async fn metrics(&self, metrics_token: Option<&str>) -> Result<String>

Get service metrics

Retrieves metrics from the service in Prometheus format. This endpoint may require special authentication using a metrics token.

§Arguments
  • metrics_token - Optional metrics-specific authentication token. If not provided, uses the client’s default authentication.
§Returns

Returns the metrics as a raw string in Prometheus exposition format.

§Errors
  • Error::Http with status 401 if authentication fails
  • Error::Http with status 403 if not authorized to view metrics
§Example
// Using default authentication
let metrics = client.metrics(None).await?;
println!("Metrics:\n{}", metrics);

// Using specific metrics token
let metrics = client.metrics(Some("metrics-token-xyz")).await?;
println!("Metrics with token:\n{}", metrics);

Trait Implementations§

Source§

impl Clone for Client

Source§

fn clone(&self) -> Client

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Client

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl Freeze for Client

§

impl !RefUnwindSafe for Client

§

impl Send for Client

§

impl Sync for Client

§

impl Unpin for Client

§

impl !UnwindSafe for Client

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,