Configuration
You can read runtime configuration values in a Rust component with the wasi:cli/environment interface. Configuration values can be supplied from inline key-value pairs, Kubernetes ConfigMaps, and Kubernetes Secrets. All arrive through the same interface regardless of source.
This guide walks through reading wasi:cli/environment from a Rust component and shows how to provide values via a Kubernetes workload manifest.
Overview
wasi:cli/environment is part of the standard WASI P2 suite and is always available in a wasmCloud host. It exposes a get-environment function that returns all configuration key-value pairs available to the component. In production, values come from localResources.environment in a Workload or WorkloadDeployment manifest, which accepts inline key-value pairs, Kubernetes ConfigMaps, and Kubernetes Secrets.
The Secrets and Configuration Management page in the Operator Manual describes how the Kubernetes operator injects values via wasi:cli/environment. This guide covers the component author's side: how to read those values in Rust.
wasi:cli/environment is provided by the wasip2 crate maintained by the Bytecode Alliance. wstd already depends on wasip2 internally, but does not re-export it publicly, so you add wasip2 as a direct dependency and call wasip2::cli::environment::get_environment from your handler. No wit-bindgen setup is required.
Step 1: Add the dependency
In Cargo.toml:
[dependencies]
wasip2 = "1.0"
wstd = "0.6"Step 2: Read configuration values
In src/lib.rs, add a helper that reads all key-value pairs into a HashMap for convenient lookup:
use std::collections::HashMap;
use wstd::http::{Body, Request, Response};
fn load_config() -> HashMap<String, String> {
wasip2::cli::environment::get_environment()
.into_iter()
.collect()
}
#[wstd::http_server]
async fn main(_req: Request<Body>) -> Result<Response<Body>, wstd::http::Error> {
let config = load_config();
let app_name = config
.get("APP_NAME")
.cloned()
.unwrap_or_else(|| "My App (dev)".to_string());
let upstream_url = config
.get("UPSTREAM_URL")
.cloned()
.unwrap_or_else(|| "http://localhost:9090".to_string());
let body = format!("app={app_name}\nupstream={upstream_url}\n");
Ok(Response::new(Body::from(body)))
}Call get_environment() (or your load_config helper) inside each request handler rather than at module scope. Configuration is stable for the lifetime of an invocation, but reading at module scope can run before the host has finished injecting values.
Synchronous API
get_environment is synchronous in WIT. It returns Vec<(String, String)> directly without an async wrapper. The call works the same way whether your handler is synchronous or asynchronous.
Step 3: Provide configuration values
In production (Kubernetes manifests)
All configuration values are delivered to the component through the localResources.environment field in a Workload or WorkloadDeployment manifest. Three sources are supported and can be combined.
Inline values
The simplest approach: provide key-value pairs directly in the manifest:
components:
- name: http-component
image: ghcr.io/your-org/your-component:latest
localResources:
environment:
config:
APP_NAME: My Service
UPSTREAM_URL: https://api.example.comInline values are suitable for non-sensitive configuration that is safe to store in version control.
From a Kubernetes ConfigMap
Reference a ConfigMap by name. Each key in the ConfigMap becomes a configuration value in the component:
components:
- name: http-component
image: ghcr.io/your-org/your-component:latest
localResources:
environment:
configFrom:
- name: app-configCreate the ConfigMap separately:
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
APP_NAME: My Service
UPSTREAM_URL: https://api.example.comFrom a Kubernetes Secret
Reference a Secret by name for sensitive values such as API keys or credentials:
components:
- name: http-component
image: ghcr.io/your-org/your-component:latest
localResources:
environment:
secretFrom:
- name: app-secretsSecret values are delivered to the component as plain strings. No decoding is required in your component code.
Combining sources
All three sources can be used together. When the same key appears in multiple sources, the order of precedence (lowest to highest) is: config → configFrom → secretFrom.
apiVersion: runtime.wasmcloud.dev/v1alpha1
kind: WorkloadDeployment
metadata:
name: my-app
namespace: default
spec:
replicas: 1
template:
spec:
hostSelector:
hostgroup: default
components:
- name: http-component
image: ghcr.io/your-org/your-component:latest
localResources:
environment:
config:
APP_NAME: My Service # inline (lowest precedence)
configFrom:
- name: app-config # non-sensitive config from ConfigMap
secretFrom:
- name: app-secrets # sensitive values from Secret (highest precedence)
hostInterfaces:
- namespace: wasi
package: http
interfaces:
- incoming-handler
config:
host: my-app.example.comThe host: value is the HTTP Host header the wasmCloud host uses to route requests to this workload. If you want to override the listen address instead, use address: '0.0.0.0:8080'. See Secrets and Configuration Management for the full hostInterfaces reference.
For a complete reference to localResources.environment fields, including precedence rules and RBAC considerations, see Secrets and Configuration Management.
During development (wash dev)
wash dev provides the wasi:cli/environment interface, but localResources.environment defaults to empty. There is currently no mechanism in .wash/config.yaml or the wash dev CLI to inject test configuration values into a component locally.
The practical approach for development is to supply fallback defaults in your component code, as shown in the example above. This lets your component run correctly during wash dev while reading real values in production.
Build and verify
wash devwash dev builds the component and serves it on http://localhost:8000. With no configuration injected, your fallback defaults take effect:
curl http://localhost:8000app=My App (dev)
upstream=http://localhost:9090To verify the import is correctly declared, inspect the built component:
wasm-tools component wit target/wasm32-wasip2/release/<crate>.wasm | grep environmentYou should see a line like import wasi:cli/environment@0.2.x. The exact patch version is governed by the wasip2 crate version that wstd resolves to.
Alternative: the wit-bindgen path
If you would rather not depend on wasip2 directly, you can declare the import in your WIT world and generate bindings with wit-bindgen.
In wit/world.wit:
package wasmcloud:my-component;
world my-component {
import wasi:cli/environment@0.2.2;
export wasi:http/incoming-handler@0.2.2;
}In src/lib.rs:
mod bindings {
wit_bindgen::generate!({ generate_all });
}
use bindings::wasi::cli::environment::get_environment;
let config: std::collections::HashMap<String, String> = get_environment().into_iter().collect();The function name and signature are identical to the wasip2 crate path. Choose whichever fits your existing setup.
Summary: checklist for adding configuration
- Add
wasip2 = "1.0"toCargo.toml(or usewit-bindgenif you prefer to declare the import in your WIT world). - Call
wasip2::cli::environment::get_environment()inside request handlers, not at module scope. - Collect into a
HashMapfor ergonomic key lookup:get_environment().into_iter().collect::<HashMap<_, _>>(). - Provide fallback defaults with
unwrap_or_elsefor every value your component reads. This keeps the component functional duringwash devwhen no configuration is injected. - In production, provide values via
localResources.environmentin your Kubernetes manifest, usingconfig:,configFrom:, orsecretFrom:as appropriate.
API reference: wasi:cli/environment functions
For the upstream WIT definitions, see the wasi:cli 0.2.x interfaces in the WebAssembly/wasi-cli repository.
| Function | Signature | Description |
|---|---|---|
get_environment | fn() -> Vec<(String, String)> | Returns all configuration key-value pairs. Returns an empty Vec if no configuration has been provided to the component. |
get_arguments | fn() -> Vec<String> | Returns POSIX-style command-line arguments. Not used for configuration. |
initial_cwd | fn() -> Option<String> | Returns the initial working directory. Not used for configuration. |
Further reading
- Secrets and Configuration Management — operator reference for
localResources.environment, ConfigMaps, and Secrets - Rust Language Guide — toolchain overview, HTTP patterns, async runtime guidance, and crate compatibility
- Key-Value Storage — persistent key-value storage for component state
- Language Support overview — summary of all supported languages and toolchains