Skip to content

Quickstart

This guide takes you from an empty directory to a running Greengrass v2 component talking to a local MQTT broker. You will scaffold a component with the edgecommons CLI, look at what it generated, wire up the bootstrap code, build it, and run it on the HOST platform.

Pick your language with the tabs in each code block — the tabs stay in sync across the page.

  1. The scaffolding CLI is a single static binary. At a minimum you need git, a rustup toolchain (to build the CLI), Docker (for the local MQTT broker), and the toolchain for the language you are targeting:

    • Java — JDK 25 and Maven. (The library targets Java 25, so the JDK must be 25 even though the generated component compiles to an older bytecode level.)
    • Python — Python 3.9+.
    • Rust — a rustup toolchain. The generated component crate is edition 2021 / MSRV 1.85.
    • TypeScript — Node 18+ and npm.

    See the Installation guide for full setup, including how to make the edgecommons library resolvable for each language. Once the toolchain is in place, install the CLI and confirm your environment:

Terminal window
# From the monorepo root — installs the `edgecommons` command.
cargo install --path cli/crates/ec-cli
edgecommons doctor --platforms HOST # checks only what a HOST component needs

edgecommons doctor prints [ok] <name> -> <path> (with the tool’s version) or [missing] <name> for each tool, and exits non-zero when a required one is missing. --platforms and -l/--language narrow it to the tools your workflow actually uses.

  1. edgecommons component new generates a component from a template carried inside the binary, so scaffolding works offline. The output directory is <path>/<kebab-name>, where kebab-name is derived from the last dot-segment of the name you pass by a case-boundary- and acronym-aware kebab conversion (com.example.MyComponent produces a my-component/ directory; --bin-name and --dir override the derived name and the directory outright — see the command reference for the full flag set). The Greengrass component name itself stays the PascalCase reverse-DNS form you passed — only the crate/bin/artifact tokens and the directory move to kebab.

    Run it with no flags on a terminal and it asks for the language and the name:

Terminal window
edgecommons component new

Or pass the flags directly. Use -l to choose the language (JAVA, PYTHON, RUST, or TYPESCRIPT):

Terminal window
# Choose -l to match the language tab you are following.
edgecommons component new -n com.example.MyComponent -l JAVA
# -l PYTHON | -l RUST | -l TYPESCRIPT

On success the CLI prints Done. Component generated at: <dir>. By default the component targets all platforms (--platforms GREENGRASS,HOST,KUBERNETES) and uses a local library dependency (--dep-source local). --platforms selects the artifact packs: the Kubernetes Dockerfile and k8s/ manifests are emitted only when KUBERNETES is among them, and the Greengrass recipe.yaml and gdk-config.json only when GREENGRASS is.

Every scaffold also ships a config.schema.json describing the component’s own configuration — the object at component.global. It is what edgecommons component validate checks your config against, so a typo’d key is caught before it reaches a device.

  1. The layout differs slightly per language, but every component carries your business-logic file, a build manifest, the Greengrass recipe.yaml + gdk-config.json, and sample configs under test-configs/.

my-component/
├─ src/main/java/.../MyComponent.java # your business logic — class stays PascalCase
├─ pom.xml # Maven build (shaded JAR), artifactId = my-component
├─ recipe.yaml gdk-config.json # Greengrass recipe + GDK config
└─ test-configs/MyComponent.json # sample component config

Every layout above also carries AGENTS.md, CLAUDE.md, DESIGN.md, a Diátaxis docs/ set, and .github/workflows/ — trimmed from these listings for brevity. The top-level directory is always the kebab name; only the Java class file and the Greengrass component identity keep the PascalCase form.

The sample config under test-configs/ already declares the component’s UNS identity — a hierarchy (["site", "device"]) plus an identity value for the site — and the default heartbeat, so the generated component announces itself on the unified namespace out of the box. The app skeleton mints any topic it publishes through gg.uns() rather than hand-building topic strings.

  1. The entry point builds one EdgeCommons runtime from the standard CLI args, then reads each subsystem off it via typed accessors. The accessor names follow each language’s conventions (getMessaging() / get_messaging() / messaging()), but the shape is the same everywhere.

import com.mbreissi.edgecommons.EdgeCommons;
import com.mbreissi.edgecommons.EdgeCommonsBuilder;
public class MyComponent {
public static void main(String[] args) {
// Build the runtime from the standard CLI args (use the FULL component name).
EdgeCommons gg = EdgeCommonsBuilder.create("com.example.MyComponent")
.withArgs(args)
.build();
var config = gg.getConfigManager();
var messaging = gg.getMessaging();
var metrics = gg.getMetrics();
// ... your business logic ...
// Do NOT register your own shutdown hook: the library wires SIGTERM/SIGINT to a
// graceful, idempotent shutdown() for you.
}
}

The shipped Java template uses the deprecated direct constructor (new EdgeCommons(name, args)); the builder shown here is the canonical convention and is what the worked example uses.

  1. Build with your language’s standard toolchain. These commands are the same on Linux and Windows (forward-slash paths work in PowerShell too).

Terminal window
mvn clean package # produces target/my-component-1.0.0.jar (shaded, self-contained)
  1. The HOST platform with the MQTT transport connects to a local broker (and, optionally, to AWS IoT Core). Bring up the EMQX broker that ships with the repo — it exposes 1883 (plaintext) and 8883 (mutual TLS):

Terminal window
docker compose -f test-infra/compose.yaml up -d
# Or a throwaway broker anywhere: docker run -d -p 1883:1883 emqx/emqx:latest

The MQTT transport needs a small messaging-config JSON (the --transport MQTT <path> payload): messaging.local is required and messaging.northbound is optional. Each running process needs a unique clientId. The Rust and TypeScript templates already ship one at test-configs/standalone-messaging.json; for Java and Python, create a standalone-messaging.json in the component directory:

{
"messaging": {
"local": {
"host": "localhost",
"port": 1883,
"clientId": "my-component-local"
}
}
}
  1. Start the component on the HOST platform, pointing --transport MQTT at the messaging config and -c FILE at a component config, with a Thing name via -t. Run from inside the generated component directory.

Terminal window
java -jar target/my-component-1.0.0.jar \
--platform HOST --transport MQTT ./standalone-messaging.json \
-c FILE test-configs/MyComponent.json -t my-thing

To confirm the component is alive, subscribe to ecv1/+/+/state (add ecv1/+/+/+/state for instance-scoped publishers) on the broker (for example with MQTTX) and watch the periodic state keepalives arrive on ecv1/<thing>/<component>/state — every scaffolded component publishes them by default, and mints its own topics through the Unified Namespace builder (gg.uns()).