This repository contains the shared ABI contracts and Wasmtime runtime helpers used across the Greentic platform.
- Component: the WASM module that implements
describe()/invoke()and is referenced by descriptors. - Flow: the graph definition whose runtime steps are wired together and whose metadata is stored as a
CallSpec. - Step: a flow node instance. Each step has a
step_idand resolves to one component plus anop. - CallSpec: the canonical
{op, payload_cbor, metadata_cbor}that flows persist. This is everything stored in packs; the host never persists the invocation envelope. - InvocationEnvelope: the host-owned wrapper of
TenantCtx,flow_id,step_id,component_id, andattemptplus the call spec bytes. Components only see the call spec. - TenantCtx / i18n_id: the tenant context carries tenant/team/user/env IDs and the mandatory
i18n_idthat threads localization and telemetry through every call.
Mapping aliases such as in_map, out_map, and err_map are flow-level authoring/runtime concepts used to shape payloads between steps. They are not component ABI fields. Components continue to implement the current component@0.6.0 WIT exports, while hosts and runtimes own CallSpec persistence, invocation wrapping, and orchestration.
Refer to contracts/0.6.0/RENAMES.md for the full naming dictionary and contracts/0.6.0/CANONICAL_MAP.md for the decisions driving each term.
ci/local_check.sh now runs scripts/naming_lint.sh to enforce this vocabulary in the canonical contract tree before the rest of the checks execute.
greentic:types-core@0.6.0exposes the canonical identifier aliases plus theTenantCtxrecord (tenant/team/user/env IDs, requiredi18n_id, correlation/trace IDs, deadline, attempt, and idempotency key) so every host can build an invocation envelope with consistent metadata.greentic:codec@0.6.0is a lightweight helper world for encoding/decoding CBOR payloads; tooling that emits descriptor examples or call specs can import it to keep hashes stable.greentic:component@0.6.0is the CBOR-first component world:describe()returns acomponent-descriptorwith ops, schema refs (hash + uri), optional setup, and capabilities, whileinvoke()consumes the host-builtInvocationEnvelopeand returns a structuredInvocationResult.
Canonical runtime target:
greentic:component@0.6.0+greentic:types-core@0.6.0+greentic:codec@0.6.0. Removed legacy component and pack-export surfaces are documented historically indocs/vision/legacy.md.
-
Docs index:
docs/README.md -
Vision docs:
docs/vision/README.md -
Canonical v0.6 direction:
docs/vision/v0_6.md -
Historical compatibility notes:
docs/vision/legacy.md -
Host/guest reexports:
greentic-interfaces-hostandgreentic-interfaces-guestnow surface the v1 worlds plus mapper helpers: component outcomes (ComponentOutcome,ComponentOutcomeStatus) and pack/flow descriptors (PackDescriptor,FlowDescriptor) for the new pack-export-v1 ABI.
-
component-v0-6: enablesgreentic:component/component@0.6.0guest bindings. -
common-types-v0-1: enablesgreentic:common-types@0.1.0. -
component-v1: enablesgreentic:component-v1@0.1.0(component-host world) and reexports the component outcome mappers. -
pack-export-v1: enablesgreentic:pack-export-v1@0.1.0(pack-host world) and reexports the pack/flow descriptor mappers. -
Default and
wit-allnow include these flags; guest builds also stage the v1 WIT packages when the features are on. -
crates/greentic-interfacesexposes the WebAssembly Interface Types (WIT) packages, generated Rust bindings, and thin mappers that bridge the generated types to the richer structures ingreentic-types. It is intentionally ABI-only and has no Wasmtime dependency. -
crates/greentic-interfaces-hostcurates the host-facing bindings: Wasmtime-ready WIT worlds plus the shared mappers. -
crates/greentic-interfaces-guestcurates the guest-facing bindings for components built againstwasm32-wasip2, including distributor API import bindings plusDistributorApiImports(distributor-api-imports) andDistributorApiImportsV1_1(distributor-api-v1-1-imports) for resolve/get/get-v2/warm and ref-based resolution + digest fetches. -
crates/greentic-interfaces-wasmtimehosts the Wasmtime integration layer. It wires the Greentic host imports into a Wasmtime linker, instantiates components, and forwards calls through the ABI bindings.
Node configuration schemas always live alongside their components. This repository only ships shared WIT contracts plus the corresponding bindings/mappers.
All shared greentic:* WIT packages live exclusively under:
wit/
No other crate may define or copy these packages.
Binding generation must reference the canonical path via build.rs.
CI enforces this.
Provider protocols are now unified under greentic:provider-schema-core@1.0.0. Legacy messaging/events/secrets provider WIT worlds have been removed; migrate provider components to provider-core JSON schemas instead of typed provider-specific worlds.
These crates are published from this workspace. Downstream components that only need the ABI can depend solely on greentic-interfaces. Runtimes that execute packs should add greentic-interfaces-wasmtime and choose whether to stay on the stable Wasmtime feature path or opt into the nightly configuration. Hosts that just want re-exported bindings can depend on greentic-interfaces-host, while guest components can pull greentic-interfaces-guest for wasm32-wasip2 builds.
// Host side: wire imports into a Wasmtime linker.
use greentic_interfaces_host::host_import::v0_6::add_to_linker;
// Guest side: call host capabilities from inside a component.
use greentic_interfaces_guest::component_v0_6::node::Guest;
use greentic_interfaces_guest::secrets_store::secrets_store;- Hosts (runner, deployer, gateways):
greentic-interfaces-host - Wasm components (
wasm32-wasip2):greentic-interfaces-guest - Wasmtime glue / linker helpers:
greentic-interfaces-wasmtime - ABI/WIT tooling and validation:
greentic-interfaces
For debugging wkg resolution with fully staged dependencies, use
scripts/wkg-build-staged.sh (defaults to attestation@1.0.0).
use greentic_interfaces_host::http::http_client;
use greentic_interfaces_host::secrets::store_v1::secrets_store;
use greentic_interfaces_host::telemetry::log;use greentic_interfaces_guest::component_v0_6::{
component_i18n, component_qa, node,
};
struct Component;
impl node::Guest for Component {
# fn describe() -> node::ComponentDescriptor {
# node::ComponentDescriptor {
# name: "demo".into(),
# version: "0.1.0".into(),
# summary: None,
# capabilities: vec![],
# ops: vec![],
# schemas: vec![],
# setup: None,
# }
# }
# fn invoke(
# _op: String,
# _envelope: node::InvocationEnvelope,
# ) -> Result<node::InvocationResult, node::NodeError> {
# Ok(node::InvocationResult {
# ok: true,
# output_cbor: vec![],
# output_metadata_cbor: None,
# })
# }
}
impl component_qa::Guest for Component {
# fn qa_spec(_mode: component_qa::QaMode) -> Vec<u8> { vec![] }
# fn apply_answers(
# _mode: component_qa::QaMode,
# _current_config: Vec<u8>,
# _answers: Vec<u8>,
# ) -> Vec<u8> { vec![] }
}
impl component_i18n::Guest for Component {
# fn i18n_keys() -> Vec<String> { vec![] }
}
greentic_interfaces_guest::export_component_v060!(
Component,
component_qa: Component,
component_i18n: Component,
);use greentic_interfaces_guest::component_v0_6::node::Guest;
use greentic_interfaces_guest::secrets_store::secrets_store;
use greentic_interfaces_guest::http_client::http_client;
use greentic_interfaces_guest::telemetry_logger::logger_api;
// Distributor imports (host calls) — enable the feature in Cargo.toml:
// greentic-interfaces-guest = { version = "0.4", features = ["distributor-api-imports"] }
use greentic_interfaces_guest::distributor_api::DistributorApiImports;
use greentic_interfaces_guest::distributor_api::ResolveComponentRequest;
let api = DistributorApiImports::new();
let _resp = api.resolve_component(&ResolveComponentRequest {
tenant_id: "tenant".into(),
environment_id: "env".into(),
pack_id: "pack".into(),
component_id: "comp".into(),
version: "1.0.0".into(),
extra: "{}".into(),
});
// Ref-based distributor imports (host calls) — enable the feature in Cargo.toml:
// greentic-interfaces-guest = { version = "0.4", features = ["distributor-api-v1-1-imports"] }
use greentic_interfaces_guest::distributor_api_v1_1::DistributorApiImportsV1_1;
let ref_api = DistributorApiImportsV1_1::new();
let resolved = ref_api.resolve_ref("oci://registry.example/greentic/component@sha256:...");
let _artifact = ref_api.get_by_digest(&resolved.digest);- Replace direct
greentic-interfacesimports in hosts withgreentic-interfaces-hostand switch to the curated modules (secrets,state,http,telemetry,oauth). - Replace direct bindgen usage in wasm components with
greentic-interfaces-guest; import from the module for the capability you need (secrets_store,state_store,http_client,oauth). - Update your target/toolchain: guests should build with
--target wasm32-wasip2; hosts stay native. - For Wasmtime wiring, depend on
greentic-interfaces-wasmtimealongside the host crate if you need linker helpers. - Drop local WIT regeneration: the host/guest crates ship the generated bindings; WIT remains the source of truth here.
For local development you can override the crates.io dependency on greentic-types by copying .cargo/local-patch.example.toml to .cargo/config.toml and pointing it at a sibling checkout of greentic-types.
| Feature | World(s) enabled | Published package | Notes |
|---|---|---|---|
secrets-store-v1 |
greentic:secrets-store/store@1.0.0 (store) |
package.wit |
Read-only secret lookup (get) returning bytes with structured errors. |
state-store-v1 |
greentic:state/store@1.0.0 (store) |
package.wit |
Tenant-scoped blob store aligned with HostCapabilities.state. |
http-client-v1 |
greentic:http/client@1.0.0 (client) |
package.wit |
Preview 2 HTTP client matching HostCapabilities.http. |
http-client-v1-1 |
greentic:http/client@1.1.0 (client) |
package.wit |
Adds optional request-options + tenant context; hosts should also expose @1.0.0 for legacy bundles. |
telemetry-logger-v1 |
greentic:telemetry/logger@1.0.0 (logger) |
package.wit |
Tenant-aware telemetry logger aligned with HostCapabilities.telemetry. |
worker-api |
greentic:worker/worker@1.0.0 (worker) |
package.wit |
Generic worker request/response envelope; see docs/worker.md for details. |
gui-fragment |
greentic:gui/gui-fragment@1.0.0 (gui-fragment) |
package.wit |
Server-rendered HTML fragments for Greentic-GUI; hosts call render-fragment(fragment-id, ctx) and inject the returned HTML. |
oauth-broker-v1 |
greentic:oauth-broker@1.0.0 (broker, broker-client) |
package.wit |
Generic OAuth broker: hosts implement the broker world; guest components import via the new broker-client world to build consent URLs, exchange codes, and fetch tokens. |
describe-v1 |
greentic:component@1.0.0 (describe-v1) |
package.wit |
Describe-only schema export for packs without the full component ABI. |
runner-host-v1 |
greentic:host@1.0.0 (http-v1, kv-v1) |
package.wit |
Legacy runner host bundle (now secrets-free; kept only for HTTP/KV). |
operator-hooks-v1 |
greentic:operator@1.0.0 (hook-provider) |
package.wit |
Operation envelope + hook decision contracts for pre/post operator interceptors. |
component-lifecycle-v1 |
greentic:lifecycle@1.0.0 (lifecycle-v1) |
package.wit |
Optional lifecycle hooks (init, health, shutdown). |
source-v1 |
greentic:source/source-sync@1.0.0 |
package.wit |
Tenant-scoped source provider interface (list repos/branches, commit metadata, webhooks). |
build-v1 |
greentic:build/builder@1.0.0 |
package.wit |
Tenant-scoped build execution (build plan/status/log refs). |
scan-v1 |
greentic:scan/scanner@1.0.0 |
package.wit |
Tenant-scoped scan execution (scan kind/result/SBOM refs). |
signing-v1 |
greentic:signing/signer@1.0.0 |
package.wit |
Tenant-scoped signing/verification using signing key refs. |
attestation-v1 |
greentic:attestation/attester@1.0.0 |
package.wit |
Tenant-scoped attestation generation (predicate/statement refs). |
policy-v1 |
greentic:policy/policy-evaluator@1.0.0 |
package.wit |
Tenant-scoped policy evaluation (allow/deny with reasons). |
metadata-v1 |
greentic:metadata/metadata-store@1.0.0 |
package.wit |
Tenant-scoped metadata upsert/query for components/versions. |
distributor-api |
greentic:distributor-api/distributor-api@1.0.0 |
package.wit |
Active distributor API for runner/deployer flows: resolve-component (includes secret requirements), legacy get-pack-status string, structured get-pack-status-v2 (status + secret requirements), and warm-pack; guests can also enable distributor-api-imports for import bindings plus a DistributorApiImports helper. |
distributor-api-v1-1 |
greentic:distributor-api/distributor-api@1.1.0 |
package.wit |
Adds ref-based resolution (resolve-ref) and digest fetching (get-by-digest) for OCI component references (tag or digest); keep @1.0.0 for pack-id + component-id flows. |
distribution-v1 |
greentic:distribution/distribution@1.0.0 |
package.wit |
Experimental desired state submission/retrieval (tenant + IDs + JSON blobs), not used by current flows. |
oci-v1 |
greentic:oci/oci-distribution@1.0.0 |
package.wit |
Tenant-scoped OCI distribution helpers (push/get pull reference). |
wit-all |
Aggregates every feature above | – | Handy opt-in when you just want “everything on”. |
Additional shared package: provider:common@0.0.2 (under wit/provider-common/world.wit) carries messaging provider metadata, capability flags, limits, render tiers, warnings, and encoded payload helpers for provider components. Enable the provider-common feature to generate bindings; the package remains additive and shared across messaging providers.
Packs and runners that need to resolve remote components should use the ref-based distributor surface in greentic:distributor-api@1.1.0:
- pass a ComponentRef string (
oci://registry/repo:tagoroci://registry/repo@sha256:<digest>) intoresolve-ref. - read the returned digest + metadata and persist the digest alongside the pack manifest.
- fetch the actual artifact with
get-by-digest(returns bytes or a filesystem path).
Older flows that only have pack-id + component-id + version should keep using resolve-component from @1.0.0.
greentic-interfaces-host exposes optional features for host bindings:
worker-v1: enablesgreentic_interfaces_host::worker::*forgreentic:worker@1.0.0.oauth-broker-v1: enablesgreentic_interfaces_host::oauth_broker::*forgreentic:oauth-broker@1.0.0; pair this withgreentic-oauth-sdkwhen calling the broker from services.
Example:
greentic-interfaces-host = { version = "0.4", features = ["worker-v1", "oauth-broker-v1"] }Host quickstart (workers + broker):
use greentic_interfaces_host::{mappers, worker};
use greentic_types::TenantCtx;
use serde_json::json;
// Convert the shared TenantCtx into the WIT shape expected by worker bindings.
let wit_ctx = mappers::tenant_ctx_to_wit(TenantCtx::default())?;
// Build a worker request using the host bindings.
let request = worker::exports::greentic::worker::worker_api::WorkerRequest {
version: "1.0".into(),
tenant: wit_ctx,
worker_id: "example-worker".into(),
correlation_id: None,
session_id: None,
thread_id: None,
payload_json: "{}".into(),
timestamp_utc: "2025-01-01T00:00:00Z".into(),
};
// Reverse mapping when you need to turn a WIT tenant back into greentic_types::TenantCtx:
let tenant_ctx = mappers::tenant_ctx_from_wit(request.tenant.clone())?;
// OAuth broker bindings live under:
// greentic_interfaces_host::oauth_broker::exports::greentic::oauth_broker::broker_v1
// Host-friendly worker request/response with serde_json payloads:
use greentic_interfaces_host::worker::{HostWorkerRequest, HostWorkerResponse};
let host_req = HostWorkerRequest {
version: "1.0".into(),
tenant: TenantCtx::default(),
worker_id: "my-worker".into(),
payload: json!({"input": "value"}),
timestamp_utc: "2025-01-01T00:00:00Z".into(),
correlation_id: None,
session_id: None,
thread_id: None,
};
// Convert to WIT and invoke via generated bindings (e.g., Wasmtime host):
let wit_req = greentic_interfaces_host::worker::exports::greentic::worker::worker_api::WorkerRequest::try_from(host_req)?;
// Convert responses back to host types:
let host_resp: HostWorkerResponse = wit_resp.try_into()?;All MCP protocol WIT packages live here; routers should not redefine them elsewhere. New work should target wasix:mcp@25.06.18; older snapshots remain only for compatibility.
| WIT package | MCP spec revision | Link |
|---|---|---|
wasix:mcp@24.11.05 |
2024-11-05 (+ Greentic config/secret/output descriptors) | https://modelcontextprotocol.io/specification/2024-11-05 |
wasix:mcp@25.03.26 |
2025-03-26 (annotations, audio content, completions, progress; metadata carries config/secrets/output hints) | https://modelcontextprotocol.io/specification/2025-03-26 |
wasix:mcp@25.06.18 |
2025-06-18 (structured output, resource/resource-link, elicitation, titles/_meta, tightened auth/resource metadata) | https://modelcontextprotocol.io/specification/2025-06-18 |
Deployment packs can import greentic:deploy-plan@1.0.0 to read the current DeploymentPlan and emit status updates. The world exposes two funcs:
get-deployment-plan()– returns the JSON-encodedDeploymentPlanbuilt by the host/deployer for this execution.emit-status(message)– reports a free-form status line that hosts may log or display in a UI.
Hosts wire this world alongside the existing runner-host imports, so deployment flows still run as ordinary events flows with an additional channel for structured deployment context.
The workspace targets Rust 1.91 or newer (required by the 2024 edition). CI pins the same stable toolchain for formatting/clippy, so make sure your local toolchain matches 1.91+ when hacking.
Two smoke-level examples live under examples/:
component-describe: ano_stdcomponent that implementsdescribe-v1::describe-json.runner-host-smoke: a host-side binary that links the runner-host imports, instantiates thecomponent-describeWasm artifact, and executesdescribe-json. The runner repository also ships a secrets-oriented guest fixture (component-secrets) that exercises thesecrets-storeimports end-to-end.
# Install the WASI Preview 2 target once (matches CI)
rustup target add wasm32-wasip2 --toolchain 1.95.0
# Compile the component to Wasm (targets wasm32-wasip2)
CARGO_TARGET_DIR=target cargo build --manifest-path examples/component-describe/Cargo.toml --target wasm32-wasip2
# Run the host smoke test (reuses the artifact above)
COMPONENT_DESCRIBE_WASM=target/wasm32-wasip2/debug/component_describe.wasm \
CARGO_TARGET_DIR=target cargo run --manifest-path examples/runner-host-smoke/Cargo.tomlThe runtime crate provides Wasmtime glue for the Greentic WIT packages: an engine builder, feature-gated add_*_to_linker helpers, and mapper utilities that bridge between the ABI structs re-exported from greentic-interfaces and the richer models in greentic-types. It does not regenerate WIT on its own – everything flows through the ABI crate – so downstream consumers (runner, MCP, packs) can instantiate components and call exports without duplicating linker boilerplate.
Every tagged release publishes a tarball, checksum, raw package.wit files, and a signed provenance note that enumerates per-package hashes. Grab the latest release notes to verify what you downloaded.
Run the CI-equivalent checks locally with:
ci/local_check.shToggles:
LOCAL_CHECK_ONLINE=1– enable networked steps (none today, reserved for future use).LOCAL_CHECK_STRICT=1– fail immediately if required tools are missing.LOCAL_CHECK_VERBOSE=1– print every command before executing it.LOCAL_CHECK_EXAMPLES=1– build/run the example crates (requires thewasm32-wasip2target).- The example steps expect
rustup target add wasm32-wasip2 --toolchain 1.95.0to have been run first.
A pre-push hook is installed automatically (if absent) to run the script before pushing; remove .git/hooks/pre-push if you prefer to opt out.
The published WIT bundles live in GitHub Container Registry under ghcr.io/greenticai/wit. The registry metadata served from https://greentic.ai/.well-known/wasm-pkg/registry.json advertises this prefix, so any wkg client can resolve the greentic:* namespace automatically.
# 1. Install the wasm packaging CLI
cargo install wkg
# 2. Point your config at the Greentic registry (writes ~/.config/wasm-pkg/config.toml)
wkg config --default-registry greentic.ai
# 3. Fetch the desired package (auto-discovers ghcr.io/greenticai/wit/<namespace>/<pkg>)
wkg get greentic:component@1.0.0 --output ./component.wasm
# or grab the raw WIT:
wkg get greentic:component@1.0.0 --output ./component.wit --format witIf you prefer to edit the config file manually, add this stanza:
[namespace_registries]
greentic = "greentic.ai"With that mapping in place the CLI will transparently pull from GHCR using the namespace prefix advertised by the registry metadata (greenticai/wit/).
Legacy secrets provider surfaces are documented for migration notes in docs/secrets-provider.md; provider-core JSON schemas have replaced the typed provider WIT worlds.
The secrets-store-v1 feature gates the greentic:secrets-store/store@1.0.0 package. Secret requirement metadata now lives in greentic:secrets-types@1.0.0 (key/scope/format/schema/examples); distributor responses surface secret-requirements using that shared type, and no secret values are returned.
Components that need to work with secrets should:
All secret requirement modeling is handled in greentic-types; greentic-interfaces only defines the WIT surface.
- Enable
secrets-store-v1(orwit-all) on the dependency. - Import the interface in their WIT (
use greentic:secrets-store/store@1.0.0) or viawit-bindgen. - Call the synchronous host function surfaced by the runner:
interface secrets-store {
/// Secret lookup failures.
enum secrets-error { not-found, denied, invalid-key, internal }
/// Fetch a secret by key; returns `none` when the key is missing.
get: func(key: string) -> result<option<list<u8>>, secrets-error>;
}getreturnsSome(bytes)when the secret exists,Nonewhen absent, and a structuredsecrets-errorwhen the host rejects or cannot service the lookup.