Parameters API
API reference for the opt-in parameters subsystem. This page lists the public surface verified against source; for the conceptual model, refresh semantics, and config keys, read the Parameters guide.
The subsystem is opt-in: the gg accessor returns nothing unless a parameters section is present
in the active config (Rust additionally requires the parameters cargo feature). Reads are always
served from a local cache — the network is touched only on refresh().
In hierarchical-config deployments, an inherited hierarchy layer may define common
parameters.source and cache defaults. Each component still owns its own ParameterService, cache
lifecycle, and sync set.
The accessor
Section titled “The accessor”Fetch the ParameterService from your EdgeCommons instance. It is null / None / undefined
when the parameters section is absent (Rust: also when the parameters feature is off). Depend on
the ParameterService interface, not the concrete DefaultParameterService.
| SDK | Accessor | Returns | Empty when |
|---|---|---|---|
| Java | ParameterService getParameters() |
ParameterService or null |
no parameters section |
| Python | get_parameters() |
service or None |
no parameters section |
| Rust | fn parameters(&self) -> Option<Arc<dyn ParameterService>> |
Some / None |
no section or parameters feature off |
| TypeScript | parameters(): ParameterService | undefined |
service or undefined |
no parameters section |
The gg instance owns the service lifecycle — it stops the background refresher at component
shutdown — so you do not call close() yourself on the accessor-returned service.
Rust feature gating
Section titled “Rust feature gating”The Rust accessor is compiled in only with the parameters feature; the awsSsm source
additionally needs parameters-aws. Without parameters, gg.parameters() always returns None.
[dependencies]edgecommons = { version = "*", features = ["parameters"] }# add "parameters-aws" as well to use the awsSsm sourceParameterService
Section titled “ParameterService”The interface to depend on. Six core methods plus four typed accessors (default/concrete methods that parse the underlying string). Signatures as they appear in source:
public interface ParameterService { Optional<String> get(String name); Optional<byte[]> getBytes(String name); Map<String, String> getByPath(String path); // path = prefix; no recursive arg List<String> names(String prefix); // pass "" for all void refresh(); ParameterStats stats();
// typed accessors (default methods on the interface) default Optional<Long> getInt(String name); default Optional<Boolean> getBool(String name); default Optional<JsonElement> getJson(String name); default Optional<List<String>> getStringList(String name);}class ParameterService(ABC): def get(self, name) -> Optional[str]: ... def get_bytes(self, name) -> Optional[bytes]: ... def get_by_path(self, path) -> Dict[str, str]: ... # path = prefix; no recursive arg def names(self, prefix) -> List[str]: ... # pass "" for all def refresh(self) -> None: ... def stats(self) -> ParameterStats: ...
# typed accessors (concrete methods on the base class) def get_int(self, name) -> Optional[int]: ... def get_bool(self, name) -> Optional[bool]: ... def get_json(self, name): ... # parsed JSON object def get_string_list(self, name) -> Optional[List[str]]: ...pub trait ParameterService: Send + Sync { fn get(&self, name: &str) -> Result<Option<String>>; fn get_bytes(&self, name: &str) -> Result<Option<Vec<u8>>>; fn get_by_path(&self, path: &str) -> Result<BTreeMap<String, String>>; // prefix; no recursive arg fn names(&self, prefix: &str) -> Result<Vec<String>>; // pass "" for all fn refresh(&self) -> Result<()>; fn stats(&self) -> ParameterStats;
// typed accessors (trait-default methods) fn get_int(&self, name: &str) -> Result<Option<i64>>; fn get_bool(&self, name: &str) -> Result<Option<bool>>; fn get_json(&self, name: &str) -> Result<Option<serde_json::Value>>; fn get_string_list(&self, name: &str) -> Result<Option<Vec<String>>>;}interface ParameterService { get(name: string): string | undefined; getBytes(name: string): Buffer | undefined; getByPath(path: string): Map<string, string>; // path = prefix; no recursive arg names(prefix: string): string[]; // pass "" for all refresh(): Promise<void>; // async — unlike the other three SDKs stats(): ParameterStats;
// typed accessors getInt(name: string): number | undefined; getBool(name: string): boolean | undefined; getJson(name: string): unknown | undefined; getStringList(name: string): string[] | undefined;}Method semantics
Section titled “Method semantics”These hold in all four SDKs unless noted:
get(name)— the cached value ofnamedecoded as a UTF-8 string. Errors on a non-UTF-8 value.getBytes(name)— the raw value bytes (no decoding).getByPath(path)— every cached parameter under thepathprefix as aname -> stringmap.pathis a prefix only; there is norecursiveargument at read time (recursion is fixed bysync.pathsat config time). Non-UTF-8 entries are silently skipped.names(prefix)— cached parameter names underprefix(metadata only, no values). Theprefixargument is required — pass""to list everything.refresh()— pulls the declaredsync.names+sync.pathsfrom the source into the cache. Async (Promise<void>) in TypeScript; synchronous in Java, Python, and Rust.stats()— a non-sensitiveParameterStatssnapshot, safe to log.- Typed accessors parse the underlying string.
getIntisLong(Java, 64-bit),int(Python, arbitrary precision),i64(Rust),number(TypeScript — IEEE-754, loses precision above2^53; validated with a strict^[+-]?\d+$regex first).getBoolacceptstrue/1/yes/onandfalse/0/no/off(case-insensitive).getStringListcomma-splits and trims; an empty string yields an empty list. All reject malformed values. - Ordering —
names/getByPathresults are sorted in Java (TreeMap), Python (sorted), and Rust (BTreeMap). The TypeScript in-memory cache returns insertion order.
Reading parameters
Section titled “Reading parameters”A complete read example. Parameters are addressed by SSM-style path names like /myapp/region.
ParameterService params = gg.getParameters();if (params == null) return; // parameters subsystem is off
Optional<String> region = params.get("/myapp/region");Optional<Long> poolSize = params.getInt("/myapp/poolSize");Optional<Boolean> debug = params.getBool("/myapp/debug");Map<String, String> db = params.getByPath("/myapp/db");List<String> all = params.names(""); // every cached name
LOGGER.info("parameter subsystem stats [{}]", params.stats());params = gg.get_parameters()if params is None: return # parameters subsystem is off
region = params.get("/myapp/region") # Optional[str]pool_size = params.get_int("/myapp/poolSize") # Optional[int]debug = params.get_bool("/myapp/debug") # Optional[bool]db = params.get_by_path("/myapp/db") # Dict[str, str]all_names = params.names("") # every cached name
st = params.stats()logger.info(f"source={st.source} count={st.parameter_count}")let Some(params) = gg.parameters() else { return Ok(()); }; // subsystem off / feature off
let region = params.get("/myapp/region")?; // Option<String>let pool_size = params.get_int("/myapp/poolSize")?; // Option<i64>let debug = params.get_bool("/myapp/debug")?; // Option<bool>let db = params.get_by_path("/myapp/db")?; // BTreeMap<String, String>let all = params.names("")?; // every cached name
let st = params.stats();tracing::info!(source = %st.source, count = st.parameter_count, "parameters");const params = gg.parameters();if (!params) return; // parameters subsystem is off
const region = params.get("/myapp/region"); // string | undefinedconst poolSize = params.getInt("/myapp/poolSize"); // number | undefinedconst debug = params.getBool("/myapp/debug"); // boolean | undefinedconst db = params.getByPath("/myapp/db"); // Map<string, string>const all = params.names(""); // every cached name
const st = params.stats();console.log(`source=${st.source} count=${st.parameterCount}`);The factory
Section titled “The factory”open / open_from_config / openFromConfig build a DefaultParameterService from a parsed
parameters config section. The gg accessor calls these for you (after resolving template
variables); call them directly only when wiring the service yourself.
| SDK | Signature |
|---|---|
| Java | Parameters.open(JsonObject parametersConfig) · Parameters.open(JsonObject parametersConfig, String namespace) — static methods on the dedicated factory class Parameters (not on DefaultParameterService); both return DefaultParameterService |
| Python | open_from_config(parameters_cfg: dict, namespace: str = "") -> DefaultParameterService |
| Rust | pub fn open(config: &ParametersConfig) -> Result<DefaultParameterService> |
| TypeScript | async function openFromConfig(cfg: ParametersConfig = {}): Promise<DefaultParameterService> |
Notes:
- The
namespaceargument exists only in Java and Python, and is deliberately ignored. It is present for signature parity withCredentials.open; parameter keys are never namespaced (isolation comes from the per-component templatedcache.path). Rustopenand TypeScriptopenFromConfigtake no namespace argument. - TypeScript
openFromConfigis async (Promise) because building a remote source or KMS key provider is promise-based. The other three are synchronous. - The default
cache.pathwhen unset is the relative"param-cache". Before calling the factory, theggaccessor substitutes template variables such as{ComponentFullName}and{ThingName}across the entireparametersconfig section —source.root,source.prefix,sync.names,sync.paths,cache.path, and so on — by templating the section’s JSON text and re-parsing it. The raw factory itself does no templating, so a directopencall sees any{...}tokens literally.
DefaultParameterService factories and lifecycle
Section titled “DefaultParameterService factories and lifecycle”The concrete implementation. Use these static/builder factories to inject a custom
ParameterSource (the only way to do so — config and the gg accessor know only the
three built-in source types).
| SDK | Factories and lifecycle |
|---|---|
| Java | withMemoryCache(source, syncNames, syncPaths) · withPersistentCache(source, LocalVault, Object lock, syncNames, syncPaths) · withRefresh(long intervalSecs) (returns this) · close() (AutoCloseable, stops the daemon) |
| Python | with_memory_cache(source, sync_names, sync_paths) · with_persistent_cache(source, vault, lock, sync_names, sync_paths) · with_refresh(interval_secs) · close() |
| Rust | with_memory_cache(source, sync_names, sync_paths) · with_persistent_cache(source, Arc<Mutex<LocalVault>>, sync_names, sync_paths) · with_refresh(interval_secs) (consumes + returns self) · no close() — Refresher::drop stops + joins the thread (RAII) |
| TypeScript | static withMemoryCache(...) · static withPersistentCache(source, vault, syncNames, syncPaths) · withRefresh(intervalSecs): this · close(): void |
The syncPaths element is a (path, recursive) pair: Java List<Map.Entry<String, Boolean>>,
Python List[Tuple[str, bool]], Rust Vec<(String, bool)>, TypeScript SyncPath = [string, boolean].
DefaultParameterService s = DefaultParameterService.withMemoryCache( myCustomSource, List.of("/myapp/region"), List.of());s.refresh();// ... use s ...s.close(); // you own the lifecycle when you build it yourselfs = DefaultParameterService.with_memory_cache(my_custom_source, ["/myapp/region"], [])s.refresh()# ... use s ...s.close() # you own the lifecycle when you build it yourselflet s = DefaultParameterService::with_memory_cache( my_custom_source, vec!["/myapp/region".into()], vec![]);s.refresh()?;// ... use s ...// no close(): dropping `s` stops and joins the refresh thread (RAII)const s = DefaultParameterService.withMemoryCache(myCustomSource, ["/myapp/region"], []);await s.refresh();// ... use s ...s.close(); // you own the lifecycle when you build it yourselfSources
Section titled “Sources”A ParameterSource is the pluggable seam between the network/filesystem and the cache. There are
three config-selectable built-ins — env, mountedDir, and awsSsm only (there is no custom
config type). See the Parameters guide for config examples.
The ParameterSource interface:
fetch(name) -> Option<ParamValue>fetchByPath(path, recursive) -> [(name, ParamValue)]sourceId() -> "env" | "mountedDir" | "awsSsm"
In TypeScript fetch / fetchByPath are async (Promise). In Java, Python, and Rust they are
synchronous (the Rust SSM implementation block_ons an internal runtime).
| Source | Constructor | Behavior |
|---|---|---|
EnvSource |
EnvSource(prefix) |
Maps a name to an env var: /myapp/db/host ⇄ <PREFIX>MYAPP_DB_HOST (/, -, . → _, uppercased). Always non-secure. Default prefix = "GG_PARAM_". |
MountedDirSource |
MountedDirSource(root, securePaths) |
Each file under root is a parameter (bytes = value; name = / + path relative to root). A name under any securePaths prefix is secure=true. version is always null. Dotfiles are skipped (the kubelet ..data symlink farm); a directory at a name is “not a parameter”. |
AwsSsmSource |
AwsSsmSource(region, endpointUrl, withDecryption=true) |
GetParameter / GetParametersByPath (paginated) with WithDecryption. SecureString → secure=true; version = SSM version. Uses the default credential chain (TES on Greengrass). endpointUrl overrides for floci / LocalStack / a VPC endpoint. |
Optional SSM dependency
Section titled “Optional SSM dependency”The AWS SDK for SSM is an optional dependency in every SDK, but enforced differently:
- Rust — feature-gated at compile time.
awsSsmrequires theparameters-awsfeature; without it,build_source("awsSsm")errors (the message names onlyenv/mountedDiras available). - Java / Python / TypeScript — loaded lazily at runtime: Java
software.amazon.awssdk:ssm, Pythonboto3, TypeScript@aws-sdk/client-ssm(anoptionalDependency). A missing dependency surfaces as a parameter error at first use.
ParamValue
Section titled “ParamValue”The value type a source returns. Component developers rarely handle it directly — the typed accessors hand back plain strings, ints, and so on — but it is the source↔cache seam.
ParamValue { value: bytes // the raw parameter value secure: bool // set automatically: securePaths (mountedDir) or SecureString (awsSsm) version: Option<String> // source-assigned version, when available}| SDK | Type + plain constructor | Redaction on log |
|---|---|---|
| Java | ParamValue record; plain(byte[]), plain(String) |
toString() redacts the value |
| Python | ParamValue; ParamValue.plain(value) |
__repr__ redacts the value |
| Rust | ParamValue; ParamValue::plain(...) |
derives Debug — does NOT redact (raw bytes print) |
| TypeScript | ParamValue interface; plainValue(value) |
plain interface — no redaction (the value buffer prints) |
ParameterStats
Section titled “ParameterStats”A non-sensitive observability snapshot returned by stats() — safe to log.
| Field (Java / TS · Python / Rust) | Type | Meaning |
|---|---|---|
parameterCount · parameter_count |
long / u64 / int |
Number of parameters currently cached. |
lastRefreshAgeMs · last_refresh_age_ms |
nullable Long / Option<u64> / int |
Age of the last fully-clean refresh. null / None / undefined until the first fully-successful refresh. |
refreshFailures · refresh_failures |
long / u64 / int |
Number of refresh cycles in which at least one synced item or path failed — incremented at most once per refresh(), not once per failing item (so three items failing in one cycle add 1, not 3). |
source |
String |
The active source id (env / mountedDir / awsSsm). |
Type definitions:
- Java —
record ParameterStats(long parameterCount, Long lastRefreshAgeMs, long refreshFailures, String source) - Python —
@dataclass ParameterStats - Rust —
pub struct ParameterStats { parameter_count: u64, last_refresh_age_ms: Option<u64>, refresh_failures: u64, source: String } - TypeScript —
interface ParameterStats
Cross-language divergences
Section titled “Cross-language divergences”A quick reference to the differences that affect calling code:
- TypeScript is async where the others are sync.
refresh()returnsPromise<void>,openFromConfigis async, andParameterSource.fetch/fetchByPathreturn promises. The read accessors (get,getBytes,getByPath,names, typed getters) are still synchronous in TypeScript — they read the already-loaded cache. - Rust reads return
Result. Every method isResult<…>; the other SDKs throw/raise. - Rust gates the subsystem behind cargo features (
parameters, plusparameters-awsfor SSM). namespaceexists only in Java/Pythonopenand is ignored.- Lifecycle: Java/Python/TypeScript expose
close(); Rust uses RAII (drop stops the refresher). ParamValueredaction is enforced only in Java and Python.names/getByPathordering is sorted in Java/Python/Rust, insertion order in TypeScript.