Extension Quickstart
Konten ini belum tersedia dalam bahasa Anda.
This page walks the whole authoring path with real commands: scaffold a project, build it,
lint it, sign it, and publish it. It takes a few minutes on a machine that already has
Rust. For the concepts behind each step — what describe.json declares, what a component
contributes — read Writing Extensions after this.
Prerequisites
Section titled “Prerequisites”- Rust 1.95 or later, with the
wasm32-wasip2target:rustup target add wasm32-wasip2 cargo-component:cargo install --locked cargo-componentgtdx1.2.8 or newer (installed below)
gtdx new checks all three before it writes a single file, so a missing tool is reported
up front rather than at your first build.
Build and publish an extension
Section titled “Build and publish an extension”-
Install the CLI. This downloads a prebuilt binary — no compile:
Terminal window cargo binstall greentic-extension-sdk-cligtdx --versionPrefer a manual download? Every release archive holds the binary one directory deep, named after the asset, so extract from that path:
Terminal window TAG=v1.2.8TARGET=aarch64-apple-darwin # swap for your platformcurl -L -o gtdx.tgz \"https://github.com/greenticai/greentic-designer-sdk/releases/download/$TAG/gtdx-$TAG-$TARGET.tgz"tar -xzf gtdx.tgzchmod +x "gtdx-$TAG-$TARGET/gtdx"mv "gtdx-$TAG-$TARGET/gtdx" ~/.cargo/bin/ -
Scaffold a project. Pick the kind that matches the surface you are implementing:
Terminal window gtdx new my-ext --kind design --yescd my-ext--yesresolves everything from flags and defaults, which is what you want in scripts and CI. Prefer to be asked instead? Rungtdx newwith no arguments — see Scaffold with the wizard below.Curious what just got written? What gtdx new Generates walks every file — which three you edit, and which are regenerated on each build.
--kind wasm-componentis the one kind that needs an extra flag: it wraps an already-published component, so it needs that component’s digest-pinned OCI reference. Omit it and the scaffold writes a placeholder thatgtdx lint --publishrefuses:Terminal window gtdx new my-node --kind wasm-component \--component-ref oci://ghcr.io/greenticai/component/component-my-node@sha256:461c6a68… -
Build, pack, and install locally. This is the inner loop:
Terminal window gtdx dev --onceIt rebuilds the component, packs
dist/<name>-<version>.gtxpack, and installs it into your local dev registry. Drop--onceto watch the source tree and repeat on every save.The pack carries a
manifest.jsonintegrity ledger — a sha256 of every entry — which the runtime verifies at install. -
Lint before you publish. Two levels:
Terminal window gtdx lint --dir . # cross-field invariantsgtdx lint --publish --dir . # also enforces the publish-only rules--publishaddsE_SHA256_ZERO, which rejects placeholder digests. A scaffold that has been throughgtdx dev --oncepasses: the packer fills in the real digest of the wasm it just produced. -
Create a signing key, once, and keep it out of the repo:
Terminal window gtdx keygen --out my-key.pem # PKCS8 PEM ed25519, mode 0600 -
Publish. Dry-run first — it needs no account and does everything except the registry write:
Terminal window gtdx publish --dry-run --sign --key my-key.pemgtdx publish --sign --key my-key.pemTo publish to the public store rather than your local registry, authenticate first with
gtdx loginand pass--registry greentic-store. The store is built in — you do not needgtdx registries addfor it.
Scaffold with the wizard
Section titled “Scaffold with the wizard”Run gtdx new with no arguments on a terminal and it prompts for everything instead. The
wizard is the same code path as the flags — it just asks first, and every answer has a
default you can accept with Enter:
$ gtdx newgtdx new — interactive wizard (press Enter to accept defaults)
Project name (kebab-case): wizard-demoExtension kind:> design Designer/agentic tools (default) mcp MCP router (wasix:mcp/router) — usable as flow node + agentic tool provider Messaging/event provider wasm-component Generic WASM flow component llm LLM provider extension bundle Bundle extension deploy Deploy target extensionExtension id (reverse-DNS) [greentic.wizard-demo]:Version [0.1.0]:Author [bimapangestu28]:License (SPDX id) [Apache-2.0]:
About to scaffold: name wizard-demo kind design id greentic.wizard-demo version 0.1.0 author bimapangestu28 license Apache-2.0Create this extension? [Y/n]Nothing is written until you answer that last prompt, so it is safe to walk through and
back out. The id defaults to greentic.<name>, and the author comes from your
git config user.name.
Two details worth knowing:
- Flags become the defaults.
gtdx new my-ext --wizardpre-fills the name and still asks the rest, which is handy when you know some answers but not all.--wizardalso forces the wizard even when you passed enough flags to skip it. - Choosing
mcpadds a prompt. The wizard offers to seed the extension from an OpenAPI spec, and asks for the spec path if you accept.
The wizard needs a terminal. Without one — in CI, or when stdin is a pipe — gtdx new
with no name fails rather than hanging, and tells you to pass a name or use --yes.
Publishing with prompts
Section titled “Publishing with prompts”gtdx publish has the same option, and it is the friendlier way in while you are still
learning the flags:
gtdx publish --wizardIt asks for the registry, then the mode — publish for real, dry-run, or verify-only — then a version override, whether to sign (and where the key comes from: file, key id, or env var), the trust policy, and whether to overwrite an existing version. It prints the full summary and asks Proceed? before anything is uploaded.
Left to itself, publish keeps its scripted defaults — the wizard is strictly opt-in, so
existing CI invocations are unaffected.
Testing while you develop
Section titled “Testing while you develop”Three layers, fastest first. Most of your testing belongs in the first one.
| Layer | Command | What it proves |
|---|---|---|
| Unit, on the host | cargo test | Your logic is right. Milliseconds — no WASM, no Designer. |
| Full gate | ./ci/local_check.sh | fmt + clippy + tests + the wasm actually builds. |
| Integration | gtdx dev --once | It packs and installs. Not that it behaves — that is the layer above. |
The guest exports are plain Rust functions, so a host test calls them directly. No harness, no runtime:
#[cfg(test)]mod tests { use super::*;
#[test] fn echo_returns_its_arguments() { let out = <Component as tools::Guest>::invoke_tool( "echo".to_string(), r#"{"message":"hi"}"#.to_string(), ) .expect("echo is implemented"); assert!(out.contains("echoed")); }}Since 1.2.8 the scaffold ships tests like this for design, bundle, deploy and
provider — covering the error paths too, not just the happy one. Extend them rather than
deleting them: ci/local_check.sh runs cargo test, so an empty test module makes that
step green while verifying nothing.
Testing code that calls the host
Section titled “Testing code that calls the host”For logic that reaches http, secrets, state, logging or translation, the SDK publishes in-memory mocks:
[dev-dependencies]greentic-extension-sdk-testing = "1.2.8"use greentic_extension_sdk_testing::mock_host::{MockHttpClient, MockLogger};They are ordinary objects — MockHttpClient, MockSecretsBackend, MockLogger,
MockTranslator, MockBroker — not automatic implementations of the generated import
traits. They help when your code takes the host dependency as a parameter rather than
calling the binding directly. Structuring it that way is what keeps the logic testable on
the host at all.
Check what you built
Section titled “Check what you built”gtdx verify dist/my-ext-0.1.0.gtxpackWithout a trusted key this reports integrity only: the pack is self-consistent and
signed, but the publisher is not authenticated. Pass --trusted-key to verify who signed
it. The tool is deliberate about the distinction — an unauthenticated signature is not the
same as a verified one.
To inspect an installed extension:
gtdx list # everything installed, grouped by kindgtdx info greentic.my-ext # metadata for onegtdx doctor # toolchain, registries, credentials, installed extensionsgtdx doctor exits non-zero when it finds real problems. It groups failures by cause and
summarises the extensions that pass; add --verbose for the full per-extension listing.
Every kind
Section titled “Every kind”All eight scaffold, build, lint, sign, publish, and install:
--kind | What it is |
|---|---|
design | A designer extension — nodes, tools, and validation surfaces |
bundle | Bundle-level assets and behaviour |
deploy | A deployment target |
provider | Messaging and event providers |
wasm-component | Wraps an already-published component as a palette node |
llm | An LLM backend |
mcp | A wasix:mcp/router component — flow-capable, addressable as dw.mcp.<id> |
addon | An infrastructure workload (Qdrant, Redis, Postgres, …) a flow depends on — see Addon Extensions |
When the extension does not show up
Section titled “When the extension does not show up”A freshly built extension that installs cleanly can still be invisible in Designer. In order of likelihood:
The Designer is older than the contract. gtdx scaffolds against greentic.ai/v2,
which a Designer below 1.2.0 screens out at boot — the extension never reaches
/api/extensions, and nothing in the logs points at the version. See
Designer Compatibility.
The Designer binary is older than its version string. This one is worth knowing
because the version number will not tell you. A Designer built from a checkout embeds
whichever greentic-extension-sdk-contract it was compiled against, and that is what
actually decides which manifests it can parse — regardless of what --version reports. A
binary that predates the compat field rejects every v2 manifest with:
json: unknown field `compat`, expected one of `$schema`, `apiVersion`, `kind`,`metadata`, `engine`, `capabilities`, `runtime`, `execution`, `contributions`, `signature`engine in that list is the tell: it is the v1 shape, and gtdx lint forbids engine
precisely because compat replaced it. Check what the binary actually contains rather
than what it claims:
strings $(which greentic-designer) | grep -o 'greentic-extension-sdk-contract-[0-9][^/]*' | sort -uA 0.4.x result means the binary is from before the v2 contract, whatever its
--version says. Rebuild or reinstall the Designer.
The Designer reads only ~/.greentic. It ignores GREENTIC_HOME, and there is no flag
to point it elsewhere — so an extension installed into an isolated home will not be seen.
Install into the default home when you want the Designer to pick it up.
- Writing Extensions — what to put in
describe.jsonand the component - describe.json Manifest — the full field reference
- Publishing Extensions — signing, trust policies, and the store
- gtdx CLI — every command and flag