CLI contract
Every GGCommons component selects two things at startup: a platform (--platform — where it
runs) and a messaging transport (--transport — how it talks). A pure precedence resolver
maps the command-line flags plus the process environment to one resolved profile, so the same
business logic runs unchanged on Greengrass (IPC), a host or Docker container (dual-MQTT), or
Kubernetes (MQTT).
The contract is identical in all four languages — the same flags, the same defaults, the same
validation. Only the launcher in front of the flags (java -jar, python3, the Rust binary,
node) differs.
The four flags
Section titled “The four flags”| Flag | Values | Default |
|---|---|---|
-c / --config <SOURCE> [args...] |
FILE | CONFIGMAP | ENV | GG_CONFIG | SHADOW | CONFIG_COMPONENT |
from the resolved platform profile |
--platform <PLATFORM> |
GREENGRASS | HOST | KUBERNETES | auto |
auto (auto-detected) |
--transport <TRANSPORT> [path] |
IPC | MQTT [messaging_config.json] |
derived from the platform |
-t / --thing <name> |
IoT Thing name (full string) | resolved from env (see below) |
Platform tokens parse case-insensitively. auto (or an absent --platform) lets the resolver
auto-detect the platform from the environment.
-c / --config — the config source
Section titled “-c / --config — the config source”The first positional argument is the source name; any following positionals are source-specific.
When --config is omitted entirely, the source comes from the resolved platform profile
(GREENGRASS → GG_CONFIG, HOST → FILE, KUBERNETES → CONFIGMAP).
| Source | Extra args | Default when args omitted |
|---|---|---|
FILE |
[path] |
config.json |
CONFIGMAP |
[mountDir] [key] |
/etc/ggcommons + config.json |
ENV |
[var] |
env var CONFIG |
GG_CONFIG |
[component] [key] |
key ComponentConfig |
SHADOW |
[name] |
the component’s shadow |
CONFIG_COMPONENT |
(none) | — |
FILE is the host/dev path, CONFIGMAP is the Kubernetes-native path (it reads the component
config from a mounted ConfigMap directory with ..data-swap hot-reload), and GG_CONFIG reads from
the Greengrass deployment. See the Configuration guide for what each source
reads.
--platform — where it runs
Section titled “--platform — where it runs”GREENGRASS is the on-device, Nucleus-managed path; HOST is for bare hosts and Docker;
KUBERNETES is the k8s-native profile. Left at auto, the resolver auto-detects (first match wins):
- GREENGRASS — if env
AWS_GG_NUCLEUS_DOMAIN_SOCKET_FILEPATH_FOR_COMPONENTorSVCUIDis set. - KUBERNETES — if the service-account token file
/var/run/secrets/kubernetes.io/serviceaccount/tokenexists, or envKUBERNETES_SERVICE_HOSTis set. - HOST — the fallback.
An empty environment value is not treated as a signal, and GREENGRASS wins when both Greengrass and
Kubernetes signals are present. An explicit --platform always overrides auto-detection.
--transport — how it talks
Section titled “--transport — how it talks”IPC uses Greengrass inter-process communication; MQTT is the dual-MQTT provider that connects to
a local broker and to AWS IoT Core at once. When omitted, the transport defaults from the platform
(GREENGRASS → IPC, HOST → MQTT, KUBERNETES → MQTT).
IPC is only valid on GREENGRASS — the resolver rejects IPC on HOST or KUBERNETES with a
validation error. For MQTT, an optional trailing path supplies the broker/TLS config
(--transport MQTT ./messaging.json).
-t / --thing — the IoT Thing name
Section titled “-t / --thing — the IoT Thing name”Takes the full string value. When --thing is absent, the identity resolves in this order:
explicit -t/--thing ▸ (KUBERNETES only) GGCOMMONS_THING_NAME ▸ POD_NAME ▸ AWS_IOT_THING_NAME ▸ "NOT_GREENGRASS"Empty environment values count as absent, and the Kubernetes Downward-API tier
(GGCOMMONS_THING_NAME ▸ POD_NAME) only applies when the platform is KUBERNETES. The resolver
returns the raw value; sanitization happens later, when the name is interpolated into templates such
as {ThingName}.
Per-platform defaults
Section titled “Per-platform defaults”Leaving a flag off lets the platform profile fill it in. The CLI-relevant defaults:
| Default | GREENGRASS | HOST | KUBERNETES |
|---|---|---|---|
| transport | IPC |
MQTT |
MQTT |
| config source | GG_CONFIG |
FILE |
CONFIGMAP |
Each profile also carries downstream subsystem defaults — logging format, metric target, metric-log path, credentials key provider, and health-endpoint enablement — applied by the respective subsystems rather than by the CLI resolver:
| Profile default | GREENGRASS | HOST | KUBERNETES |
|---|---|---|---|
| logging format | library default | library default | json (stdout-JSON) |
| metric target | library log |
library log |
prometheus |
| credentials key provider | library file |
library file |
env (base64 KEK) |
| health endpoint | off | off | on |
The effective precedence is explicit flag ▸ explicit config value ▸ platform-profile default ▸ library default.
The runtime axes (transport, config source, identity) are resolved from the flag tier by the
resolver; the subsystem settings above honour an explicit config value first, then fall back to
the profile default at each subsystem’s init site.
How the flags reach your code
Section titled “How the flags reach your code”You rarely call the parser directly — the builder parses the contract for you. Hand it the process arguments and read the resolved services back off the built instance.
import com.mbreissi.ggcommons.GGCommons;import com.mbreissi.ggcommons.GGCommonsBuilder;
public final class App { public static void main(String[] args) throws Exception { GGCommons gg = GGCommonsBuilder.create("com.example.MyComponent") .withArgs(args) // parses --platform/--transport/-c/-t .build();
var messaging = gg.getMessaging(); // gg.getConfigManager(), gg.getMetrics(), gg.getStreams(), // gg.getCredentials(), gg.getParameters() are also available. }}import sysfrom ggcommons.ggcommons_builder import GGCommonsBuilder
gg = ( GGCommonsBuilder.create("com.example.MyComponent") .with_args(sys.argv[1:]) # args WITHOUT the program name .build())
messaging = gg.get_messaging()# gg.get_streams(), gg.get_credentials(), gg.get_parameters() are also available.use ggcommons::GgCommonsBuilder;
#[tokio::main]async fn main() -> anyhow::Result<()> { // Rust takes the FULL argv, including the program name at index 0. let argv: Vec<String> = std::env::args().collect();
let gg = GgCommonsBuilder::new("com.example.MyComponent") .args(argv) .build() .await?;
let _args = gg.args(); // the resolved ParsedArgs let _messaging = gg.messaging()?; // returns Result // gg.config(), gg.streams() are also available. Ok(())}import { GGCommonsBuilder } from "ggcommons";
// Supply argv WITHOUT the node/script prefix (process.argv.slice(2)).const gg = await new GGCommonsBuilder("com.example.MyComponent") .args(process.argv.slice(2)) .build();
const parsed = gg.args(); // the resolved ParsedArgsconst messaging = gg.messaging();Full command lines
Section titled “Full command lines”The flags are identical across languages; only the launcher changes. A HOST run with the dual-MQTT transport, a local config file, and an explicit thing name:
# Javajava --enable-native-access=ALL-UNNAMED -jar target/my-component.jar \ --platform HOST --transport MQTT ./messaging.json \ -c FILE ./config.json -t my-thing
# Pythonpython3 main.py \ --platform HOST --transport MQTT ./messaging.json \ -c FILE ./config.json -t my-thing
# Rust./target/release/my-component \ --platform HOST --transport MQTT ./messaging.json \ -c FILE ./config.json -t my-thing
# TypeScriptnode dist/main.js \ --platform HOST --transport MQTT ./messaging.json \ -c FILE ./config.json -t my-thing# Javajava --enable-native-access=ALL-UNNAMED -jar target\my-component.jar ` --platform HOST --transport MQTT .\messaging.json ` -c FILE .\config.json -t my-thing
# Pythonpython main.py ` --platform HOST --transport MQTT .\messaging.json ` -c FILE .\config.json -t my-thing
# Rust.\target\release\my-component.exe ` --platform HOST --transport MQTT .\messaging.json ` -c FILE .\config.json -t my-thing
# TypeScriptnode dist\main.js ` --platform HOST --transport MQTT .\messaging.json ` -c FILE .\config.json -t my-thingOther platforms lean on the profile defaults, so the command lines shrink (shown with the Java launcher; swap in your language’s launcher as above):
# GREENGRASS — on-device: transport defaults to IPC, config to GG_CONFIG,# identity from AWS_IOT_THING_NAME. Auto-detected, so --platform is optional.java --enable-native-access=ALL-UNNAMED -jar target/my-component.jar \ --platform GREENGRASS
# KUBERNETES — transport defaults to MQTT, config to CONFIGMAP (the mounted# ConfigMap is both component and messaging config), identity from the# Downward API (GGCOMMONS_THING_NAME ▸ POD_NAME). Auto-detected in-cluster.java --enable-native-access=ALL-UNNAMED -jar target/my-component.jar \ --platform KUBERNETES# GREENGRASS — transport/config/identity all default from the profile + env.java --enable-native-access=ALL-UNNAMED -jar target\my-component.jar ` --platform GREENGRASS
# KUBERNETES — config from the mounted ConfigMap; identity from the Downward API.java --enable-native-access=ALL-UNNAMED -jar target\my-component.jar ` --platform KUBERNETESMigrating from -m / --mode
Section titled “Migrating from -m / --mode”The legacy single -m / --mode axis has been removed. The two orthogonal flags
--platform and --transport replace it. All four SDKs reject the old flag with the same guidance:
The -m/--mode flag has been removed. Use --platform GREENGRASS|HOST|KUBERNETES and--transport IPC|MQTT instead (e.g. '-m STANDALONE <path>' becomes'--platform HOST --transport MQTT <path>').Typical translations:
| Old | New |
|---|---|
-m STANDALONE ./messaging.json |
--platform HOST --transport MQTT ./messaging.json |
-m GREENGRASS |
--platform GREENGRASS (transport defaults to IPC) |
Cross-language notes
Section titled “Cross-language notes”The contract is identical, but a few details of the parsed result differ between SDKs — relevant only if you inspect the parsed args directly rather than just reading services off the builder.
thingvs resolved identity. Rust’sParsedArgsexposes boththing: Option<String>(the raw-tvalue, verbatim) andidentity: String(the resolved name). TypeScript’sParsedArgs.thingis the resolved identity (never undefined) — there is no separate raw field. Java’sParsedCommandLine.thingNameis likewise the resolved identity. Do not assumethingmeans the raw flag everywhere.- Where the messaging-config path lives. Java threads the MQTT messaging-config path through the
resolver (its
ResolverInputs/ResolvedProfilecarry it). Python, Rust, and TypeScript keep those resolver types at four fields and compute the ConfigMap-default messaging path in the CLI layer instead. Either way the resolved path is on the parsed result. - Config-source typing. Rust and TypeScript parse
-cinto a typedConfigSourceSpec(a Rust enum / a TS discriminated union, both includingCONFIGMAP). Python validates the token against itsConfigSourceenum. Java passes the rawconfigArgsarray straight through to the config-manager builder. - Enum casing (Rust). Rust uses
Platform::{Greengrass, Host, Kubernetes}andTransport::{Ipc, Mqtt}(PascalCase) internally; Java, Python, and TypeScript use the uppercase token names. All four parse the CLI token case-insensitively, so this never affects the command line. - Metric-log filename. The default local metric-log path is
./logs/{ComponentFullName}.metric.login Java, Rust, and TypeScript, but./logs/{ComponentFullName}_metric.log(underscore) in Python — each library matches its own default filename suffix. - Public parser entry point. Java
GGCommons.processArgs, Rustcli::parse_from, and TSparseArgsare public; Python’s_process_argsis private — the public Python path is the builder (with_args) only.
See also
Section titled “See also”- Quickstart — scaffold, build, and run your first component.
- Configuration guide — what each
-csource reads and how hot-reload works. - Messaging guide — IPC vs dual-MQTT and the messaging config file.
- API reference — the resolver and parsed-args types per language.