scconfig-rs is a production-oriented Rust library for consuming Spring Cloud Config Server from non-Spring applications.
It is built for Rust services that need:
- typed configuration loading from the standard Spring
Environmentendpoint - YAML and Java properties output from the alternative format endpoints
- arbitrary file retrieval through the plain-text resource endpoint
- binary-safe downloads for files such as
.p12,.jks, or any other non-text asset - a clean async API with explicit request models, error types, and documentation
GET /{application}/{profile}GET /{application}/{profile}/{label}GET /{application}/{profile}.ymlGET /{application}/{profile}.yamlGET /{application}/{profile}.propertiesGET /{application}/{profile}/{label}.ymlGET /{application}/{profile}/{label}.yamlGET /{application}/{profile}/{label}.propertiesGET /{application}/{profile}/{label}/{path}GET /{application}/{profile}/{path}?useDefaultLabel=true
All file extensions are supported on the resource endpoint.
Built-in parsing and typed deserialization are available for:
- JSON
- YAML / YML
- TOML
- Java properties
Unknown extensions are still supported. The library returns them as UTF-8 text when possible, or as raw bytes otherwise.
From crates.io:
[dependencies]
scconfig-rs = "0.1.3"
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use scconfig_rs::{EnvironmentRequest, SpringConfigClient};
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct AppConfig {
server: ServerConfig,
features: FeatureFlags,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
port: u16,
}
#[derive(Debug, Deserialize)]
struct FeatureFlags {
enabled: bool,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SpringConfigClient::builder("http://localhost:8888")?
.default_label("main")
.build()?;
let request = EnvironmentRequest::new("inventory-service", ["dev"])?;
let config: AppConfig = client.fetch_typed(&request).await?;
println!("{config:#?}");
Ok(())
}use scconfig_rs::{EnvironmentFormat, EnvironmentRequest, SpringConfigClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SpringConfigClient::builder("http://localhost:8888")?.build()?;
let request = EnvironmentRequest::new("inventory-service", ["dev"])?
.label("main")
.resolve_placeholders(true);
let yaml = client
.fetch_environment_as_text(&request, EnvironmentFormat::Yaml)
.await?;
println!("{yaml}");
Ok(())
}use scconfig_rs::{ResourceRequest, SpringConfigClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SpringConfigClient::builder("http://localhost:8888")?
.default_label("main")
.build()?;
let request = ResourceRequest::new("inventory-service", ["dev"], "nginx.conf")?;
let resource = client.fetch_resource(&request).await?;
println!("{}", resource.text()?);
Ok(())
}use scconfig_rs::{ConfigDocument, ResourceRequest, SpringConfigClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SpringConfigClient::builder("http://localhost:8888")?
.default_label("main")
.build()?;
let request = ResourceRequest::new("inventory-service", ["prod"], "keystore.p12")?;
let resource = client.fetch_resource(&request).await?;
match resource.parse()? {
ConfigDocument::Binary(bytes) => println!("downloaded {} bytes", bytes.len()),
other => println!("received {:?}", other.format()),
}
Ok(())
}use scconfig_rs::BootstrapConfig;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct AppConfig {
server: ServerConfig,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
port: u16,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let bootstrap = BootstrapConfig::from_env()?;
let config: AppConfig = bootstrap.load_typed().await?;
println!("server.port={}", config.server.port);
Ok(())
}Supported bootstrap environment variables:
SPRING_CONFIG_SERVER_URLrequiredSPRING_APPLICATION_NAMErequiredSPRING_PROFILES_ACTIVEoptional, defaults todefaultSPRING_CONFIG_LABELoptionalSPRING_CONFIG_USERNAMEoptionalSPRING_CONFIG_PASSWORDoptionalSPRING_CONFIG_BEARER_TOKENoptionalSPRING_CONFIG_INSECURE_TLSoptional, defaults tofalseSPRING_CONFIG_TIMEOUT_SECSoptional
When bootstrapping against development Config Server endpoints that use private or
self-signed certificates, set SPRING_CONFIG_INSECURE_TLS=true to disable both
certificate and hostname validation for bootstrap requests only.
For local development or controlled smoke tests against self-signed or otherwise untrusted certificates, the builder can disable TLS validation explicitly:
use scconfig_rs::{EnvironmentRequest, SpringConfigClient};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = SpringConfigClient::builder("https://config.dev.example.org")?
.danger_accept_invalid_tls(true)
.build()?;
let request = EnvironmentRequest::new("sample-api", ["dev"])?;
let environment = client.fetch_environment(&request).await?;
println!("{}", environment.name);
Ok(())
}Available builder methods:
danger_accept_invalid_certs(true)disables certificate validationdanger_accept_invalid_hostnames(true)disables hostname validationdanger_accept_invalid_tls(true)disables both
These options are unsafe for production and should only be used when you fully control the target environment.
- The
Environmentendpoint is the preferred source when you want typed Rust structs. - The library preserves Spring property-source precedence. Earlier property sources win.
- Labels containing
/are automatically encoded as(_), matching Spring Cloud Config server rules. - Resource downloads always request
application/octet-stream, which keeps binary retrieval safe without breaking text-based files. - Smart scalar coercion is enabled for typed binding. Strings such as
"8080"and"true"can bind to numeric and boolean fields.
All fallible operations return scconfig_rs::Error.
Errors include:
- invalid base URL or request input
- transport failures
- non-success HTTP responses with response body captured
- invalid JSON, YAML, TOML, or Java properties content
- UTF-8 decoding failures for text resources
- typed binding failures