Documentation
¶
Overview ¶
Package wasm provides WASM compilation, binary scanning, metadata embedding, and component model support for cleat workflows.
It builds WASM modules from Go source, embeds custom sections ("cleat.metadata"), scans binaries for exports and imports, and manages concurrent compilation with build locks.
Key types:
- BuildConfig — parameters for WASM build directory assembly
- Metadata — workflow metadata embedded in WASM custom sections
- ScanResult — exports, imports, and memory info from a WASM binary
Package wasm generates WASM import/export stubs and host adapter code for the cleat workflow transformer.
Supported wasm compilation targets:
- "go" — Standard Go wasip1 (go build with GOOS=wasip1 GOARCH=wasm)
- "rust" — Rust via cargo + wasm32-wasip1
- "java" — Java via Gradle + TeaVM
- "assemblyscript" — AssemblyScript via asc
- "python" — Python via componentize-py
Index ¶
- Constants
- Variables
- func BuildPythonWasm(entry, output string, verbose bool) error
- func BuildPythonWasmWithRuntime(entry, output, targetRuntime string, verbose bool) error
- func DetectLanguage(wasmBytes []byte) string
- func FindCleatOrphanedImports(wasmBytes []byte, expectedImports map[string]bool) []string
- func FindRepoRoot(from string) (string, error)
- func GenerateExports(pkgName string, result *analyzer.AnalysisResult, target string) []byte
- func GenerateHostAdapter(pkgName string, usage *UsageInfo, target string) []byte
- func GenerateImports(pkgName string, usage *UsageInfo) []byte
- func GenerateMemory(pkgName string) []byte
- func HasImport(wasmBytes []byte, module, name string) bool
- func HasWasiImports(wasmBytes []byte) bool
- func NeededEnvImports(wasmBytes []byte) map[string]bool
- func PatchEmptyImportModuleName(raw []byte, replacementName string) []byte
- func PrepareBuildDir(cfg *BuildConfig) error
- func ReadMemoryInitialPages(wasmBytes []byte) uint32
- func RewriteWitImports(wasmBytes []byte) ([]byte, error)
- func ToSnakeCase(s string) string
- func WriteLockFile(dir string, lf *LockFile) error
- func WriteMetadata(wasmBytes []byte, meta *Metadata) ([]byte, error)
- type BuildConfig
- type ComponentBundle
- type ComponentExport
- type CoreInstance
- type ExportSpec
- type HostFunction
- type InstantiateArg
- type LockEntry
- type LockFile
- type Metadata
- type OutputFiles
- type ScannedImport
- type UsageInfo
Constants ¶
const CurrentABIVersion = 1
CurrentABIVersion is the ABI version produced by this version of cleat.
const GoTarget = "go"
GoTarget identifies the standard Go WASM compilation target. Uses GOOS=wasip1 GOARCH=wasm go build.
const LockFileName = "cleat.lock"
LockFileName is the standard lock file name.
const LockFileVersion = 2
LockFileVersion is the current cleat.lock schema version.
const PythonTarget = "python"
PythonTarget identifies the Python WASM compilation target. Used by the Go build system to dispatch to the componentize-py pipeline.
const RootModulePath = "github.com/cleat-team/cleat"
RootModulePath is the engine module's path. The SDK requires it, so a build directory replacing the SDK with a local checkout must be able to resolve it too -- see where this is used.
const SDKModulePath = "github.com/cleat-team/cleat/cleat"
SDKModulePath is the guest SDK's module path. It is a constant of the project, not something to derive from whichever module a workflow happens to live in.
Variables ¶
var IsCleatHostFunction = func(name string) bool { return strings.HasPrefix(name, "cleat_") || strings.HasPrefix(name, "set_") || strings.HasPrefix(name, "plugin_") || strings.HasPrefix(name, "schedule_") }
IsCleatHostFunction returns true if the import name corresponds to a cleat host function import.
var WitToEnvImport = map[string]map[string]string{
"cleat:host-calls/durable-call": {
"durable-call": "cleat_call",
"durable-call-retry": "cleat_call_retry",
"durable-call-heartbeat": "cleat_call_heartbeat",
},
"cleat:host-calls/durable-sleep": {
"durable-sleep": "cleat_sleep",
"durable-now": "cleat_now",
"durable-random": "cleat_random",
"durable-log": "cleat_log",
},
"cleat:host-calls/durable-version": {
"durable-version": "cleat_version",
"durable-min-version": "cleat_min_version",
},
"cleat:host-calls/durable-lifecycle": {
"durable-defer": "cleat_defer",
"durable-continue-as-new": "cleat_continue_as_new",
"durable-poll-cancellation": "cleat_poll_cancellation",
},
"cleat:host-calls/durable-signals": {
"durable-await-signals": "cleat_await_signals",
"durable-poll-signal": "cleat_poll_signal",
"durable-send-signal-and-wait": "cleat_send_signal_and_wait",
"durable-reply-to-signal": "cleat_reply_to_signal",
"durable-signal-workflow": "cleat_signal_workflow",
},
"cleat:host-calls/durable-children": {
"durable-child-workflow": "cleat_child_workflow",
"durable-await-child": "cleat_await_child",
"durable-await-all-children": "cleat_await_all_children",
"durable-child-workflow-with-options": "cleat_child_workflow_with_options",
},
"cleat:host-calls/durable-promises": {
"durable-create-promise": "cleat_create_promise",
"durable-await-promise": "cleat_await_promise",
"durable-resolve-promise": "cleat_resolve_promise",
"durable-reject-promise": "cleat_reject_promise",
},
"cleat:host-calls/durable-state": {
"set-query-state": "set_query_state",
},
"cleat:host-calls/durable-handlers": {
"durable-register-update-handler": "cleat_register_update_handler",
"durable-register-query-handler": "cleat_register_query_handler",
},
"cleat:host-calls/durable-messaging": {
"durable-send": "cleat_send",
"durable-schedule-invoke": "cleat_schedule_invoke",
},
"cleat:host-calls/durable-identity": {
"durable-workflow-id": "cleat_workflow_id",
"durable-run-id": "cleat_run_id",
},
"cleat:host-calls/plugin": {
"plugin-call": "plugin_call",
"plugin-call-streaming": "plugin_call_streaming",
},
"cleat:host-calls/durable-lock": {
"durable-acquire-lock": "cleat_acquire_lock",
"durable-release-lock": "cleat_release_lock",
},
"cleat:host-calls/durable-scope": {
"set-scope": "cleat_set_scope",
"get-scope": "cleat_get_scope",
"uuid": "cleat_uuid",
},
"cleat:host-calls/durable-stream-state": {
"set-state": "cleat_set_state",
"get-state": "cleat_get_state",
"delete-state": "cleat_delete_state",
"incr-state": "cleat_incr_state",
"has-state": "cleat_has_state",
"list-state": "cleat_list_state",
},
"cleat:host-calls/durable-extended-lifecycle": {
"continue-as-new-versioned": "cleat_continue_as_new_versioned",
"side-effect": "cleat_side_effect",
},
"cleat:host-calls/durable-extended-children": {
"child-workflow-in-schema": "cleat_child_workflow_in_schema",
},
"cleat:host-calls/durable-fetch": {
"fetch": "cleat_fetch",
},
"cleat:host-calls/durable-cron": {
"durable-schedule-cron": "cleat_schedule_cron",
"durable-delete-cron": "cleat_delete_cron",
"durable-list-crons": "cleat_list_crons",
},
}
witToEnvImport maps WIT-style import (module, function) pairs to flat "env" module function names. Used to rewrite decomposed componentize-py core WASM binaries so they link against the existing host function registrations.
The outer key is the WIT module name (the import module in the decomposed core WASM). The inner key is the WIT function name (the import field name). The value is the flat function name to use under the "env" module. WitToEnvImport maps WIT (module, function) pairs to flat "env" function names. Exported for use by the wasmtime backend to register host functions under WIT module names in addition to "env".
Functions ¶
func BuildPythonWasm ¶
func BuildPythonWasmWithRuntime ¶
BuildPythonWasmWithRuntime compiles a Python workflow to WASM, selecting the output format based on targetRuntime:
- "wasmtime" — Component Model binary (skip decomposition)
- "wazero" — decomposed core WASM module
- "" — both formats (default)
func DetectLanguage ¶
DetectLanguage attempts to determine the source language of a WASM binary. It first checks the "cleat.metadata" custom section for an explicit Language field. If absent, it scans the import section for Component Model import patterns (imports with module names starting with "cleat:"). If neither provides a result, "go" is returned as the default.
func FindCleatOrphanedImports ¶
FindCleatOrphanedImports scans a WASM binary and detects imported host functions (from the "env" module) that reference cleat operations but were not predicted by the closure analysis. Returns descriptions of each orphaned import found.
expectedImports is the set of import names that the closure analysis predicted (e.g. "cleat_call", "cleat_sleep").
func FindRepoRoot ¶
FindRepoRoot walks up from the given directory looking for go.mod to locate the repository root. Returns the absolute path to the directory containing go.mod, or an error if not found.
func GenerateExports ¶
func GenerateExports(pkgName string, result *analyzer.AnalysisResult, target string) []byte
GenerateExports produces the content of gen_wasm_exports.go. When target is "go", complex parameter types are deserialized via encoding/json instead of the manual extractJSONString fallback.
func GenerateHostAdapter ¶
GenerateHostAdapter produces the content of gen_host_adapter.go. It generates closure-based HostCallsOptions fields that call through WASM imports. The workflow code calls h.MethodName(...) through the HostCalls interface.
func GenerateImports ¶
GenerateImports produces the content of gen_wasm_imports.go.
func GenerateMemory ¶
GenerateMemory produces the content of gen_wasm_memory.go.
func HasImport ¶
HasImport checks whether a WASM binary imports a specific function from a specific module. This is used to detect features like the cleat_poll_work dispatch protocol.
func HasWasiImports ¶
HasWasiImports scans the WASM binary for wasi_snapshot_preview1 import module.
func NeededEnvImports ¶
NeededEnvImports parses the WASM import section and returns the set of function names imported from the "env" module. Used to skip registration of host functions the module doesn't need.
func PatchEmptyImportModuleName ¶
PatchEmptyImportModuleName patches core WASM bytecode that has imports with empty ("") module names by replacing them with replacementName. This is needed because wazero rejects imports with empty module names, but component-model core modules (produced by componentize-py) use them for cross-module references that the component runtime resolves via the instance DAG.
Returns the modified WASM bytes (a new allocation if changes were made, or the original slice if no empty module names were found).
func PrepareBuildDir ¶
func PrepareBuildDir(cfg *BuildConfig) error
PrepareBuildDir assembles the build directory: copies user source files, writes generated files, and creates a go.mod for wasip1 compilation.
func ReadMemoryInitialPages ¶
ReadMemoryInitialPages reads the initial (minimum) memory pages required by a WASM binary. It checks the memory section (section ID 5) for directly defined memories and the import section (section ID 2) for imported memories. Returns 0 if no memory requirement is found.
NOTE: This reads the initial/minimum pages from the WASM declaration, not the maximum. Runtime enforcement via wazero's WithMemoryLimitPages is the actual security boundary. The claim-time check here is an early-advisory filter only.
func RewriteWitImports ¶
RewriteWitImports rewrites WIT-style import module names in a core WASM binary to flat "env" module names. This allows componentize-py compiled Python workflows (decomposed to core WASM) to link against the existing host function registrations.
Returns nil if the binary has no WIT-style imports that need rewriting. Returns the modified binary on success, or an error if the binary is invalid.
func ToSnakeCase ¶
func WriteLockFile ¶
WriteLockFile writes a cleat.lock file to dir.
Types ¶
type BuildConfig ¶
type BuildConfig struct {
// SrcDir is the directory containing the user's source files.
SrcDir string
// OutDir is the directory where generated files and the WASM binary
// are written.
OutDir string
// PkgName is the Go package name for the build directory.
PkgName string
// ModulePath is the module path from the project's go.mod
// (e.g., "github.com/cleat-team/cleat").
ModulePath string
// ProjectRoot is the absolute path to the project root (where go.mod lives).
ProjectRoot string
// GoVersion is the Go version from the project's go.mod (e.g., "1.26").
GoVersion string
// Outputs holds the generated file contents.
Outputs *OutputFiles
// WASMOutput is the filename for the compiled WASM binary
// (e.g., "place_order.wasm").
WASMOutput string
// Target is the compilation target. Only "go" (standard Go/wasip1) is
// currently supported.
Target string
// XfrmSource, if non-nil, provides transformed source files to write
// instead of copying original source files. Keyed by filename.
XfrmSource map[string][]byte
}
BuildConfig holds the parameters for assembling a build directory.
type ComponentBundle ¶
type ComponentBundle struct {
// Modules are the raw core WASM binaries extracted from the component.
Modules [][]byte
// Instances describes how to instantiate each core module instance in the
// component's instance DAG. The slice index IS the instance index.
Instances []CoreInstance
// Exports maps component-level export names (e.g. "handler") to the
// specific instance and export that provides them.
Exports map[string]ComponentExport
// ImportModules lists the module names used in import sections across
// all core modules (e.g. "env", "wasi_snapshot_preview1", "teavm").
ImportModules []string
}
ComponentBundle represents a decomposed WASM Component Model binary into its constituent core WASM modules, the instance instantiation DAG, and the top-level exports that expose component entry points.
func ParseComponentBundle ¶
func ParseComponentBundle(wasmBytes []byte) (*ComponentBundle, error)
ParseComponentBundle parses a WASM Component Model binary and returns the decomposed ComponentBundle (core modules, instance DAG, and exports).
type ComponentExport ¶
type ComponentExport struct {
// Name is the component-level export name (e.g. "handler").
Name string
// Kind is the export kind: 0=func, 1=table, 2=memory, 3=global.
Kind byte
// InstanceIndex is the component instance index that provides this export.
InstanceIndex int
// ExportIndex is the index of the export within the instance's module.
ExportIndex int
}
ComponentExport maps a top-level component export to a specific instance export.
type CoreInstance ¶
type CoreInstance struct {
// ModuleIndex is the index into ComponentBundle.Modules. -1 means this
// instance has no module of its own (FromExports-only alias).
ModuleIndex int
// Args maps each import module name that the module's WASM import section
// declares to the source instance that supplies it.
Args []InstantiateArg
// FromExports lists export aliases that re-export exports from other
// instances under different names.
FromExports []ExportSpec
}
CoreInstance describes how a single core module instance is created in the component's instance DAG.
type ExportSpec ¶
type ExportSpec struct {
// Name is the export name in THIS instance (the alias).
Name string
// Kind is the export kind: 0=func, 1=table, 2=memory, 3=global.
Kind byte
// Index is the position within this instance's module's exports
// (unused for FromExports-only instances).
Index int
// SourceInstance is the index of the source instance.
SourceInstance int
// SourceName is the export name in the source instance.
SourceName string
}
ExportSpec describes a single export alias that re-exports an export from another instance under a potentially different name.
type HostFunction ¶
type HostFunction struct {
ImportName string // snake_case name used in //go:wasmimport
FieldName string // the HostCallsOptions field name (e.g., "DurableCall")
}
HostFunction identifies a host function that can be imported from the WASM host environment (e.g., "cleat_call", "cleat_sleep").
type InstantiateArg ¶
type InstantiateArg struct {
// Name is the import module name the WASM binary uses, e.g. "env",
// "wasi_snapshot_preview1", or a cross-module name like "libpython3.14.so".
Name string
// InstanceIndex is the index of the source instance in
// ComponentBundle.Instances.
InstanceIndex int
}
InstantiateArg maps an import module name (as used in the WASM import section) to the source instance that provides those imports.
type LockEntry ¶
type LockEntry struct {
Version int `json:"version"`
}
LockEntry records a pinned child workflow version.
type LockFile ¶
type LockFile struct {
Version int `json:"version"`
Policy string `json:"policy,omitempty"` // the binding policy used ("frozen", "stable", "latest", "tag:X")
Entries map[string]LockEntry `json:"entries"`
}
LockFile stores resolved child workflow versions for reproducible builds.
func ReadLockFile ¶
ReadLockFile reads and parses a cleat.lock file from dir.
type Metadata ¶
type Metadata struct {
WorkflowName string `json:"workflow_name"`
WorkflowVersion int `json:"workflow_version"`
ABIVersion int `json:"abi_version"`
MinCompatibleVersion int `json:"min_compatible_version"`
PluginDeps map[string]string `json:"plugin_deps,omitempty"`
ChildVersions map[string]int `json:"child_versions,omitempty"`
ChildBindingPolicy string `json:"child_binding_policy,omitempty"` // deployment channel / binding policy
Language string `json:"language,omitempty"`
}
Metadata is embedded in the "cleat.metadata" custom section of compiled WASM binaries. It carries the information needed for deployment.
func ReadMetadata ¶
ReadMetadata extracts the "cleat.metadata" custom section from a WASM binary and unmarshals it into a Metadata struct.
func (*Metadata) EffectivePolicy ¶
EffectivePolicy returns the effective child binding policy after applying defaults:
- "" (empty) — backwards compatible: if ChildVersions populated -> "frozen", else -> "latest"
- "frozen" — strictly use pinned ChildVersions, never resolve at runtime
- "stable" — resolve to version with "stable" tag at child creation time
- "latest" — always resolve to MAX(version) at runtime
- "tag:X" — resolve to version with tag X (e.g. "tag:canary", "tag:experiment-b")
type OutputFiles ¶
type OutputFiles struct {
Imports string // gen_wasm_imports.go
Memory string // gen_wasm_memory.go
Adapter string // gen_host_adapter.go
Exports string // gen_wasm_exports.go
}
OutputFiles holds the content of generated files keyed by filename.
func BuildOutputs ¶
func BuildOutputs(pkgName string, usage *UsageInfo, result *analyzer.AnalysisResult, target string) *OutputFiles
BuildOutputs generates all output files for a given analysis result and package name. Returns the generated file contents.
type ScannedImport ¶
type ScannedImport struct {
Module string // import module name (e.g. "env")
Name string // import field name (e.g. "cleat_call")
Kind byte // 0 = func, 1 = table, 2 = mem, 3 = global
}
ScannedImport represents a single import entry in a WASM binary.
func ScanWasmImports ¶
func ScanWasmImports(wasmBytes []byte) ([]ScannedImport, error)
ScanWasmImports reads a WASM binary and returns all import entries. This is used post-compilation to verify that the set of host functions actually imported matches what the closure analysis predicted.
The WASM binary format is:
- magic: \0asm (4 bytes)
- version: 1 (4 bytes, little-endian u32)
- sections: each with id (1 byte) + size (LEB128 u32) + content
Section 2 = Import section.
type UsageInfo ¶
type UsageInfo struct {
Used map[string]bool // keyed by ImportName
// Funcs lists the HostFunction descriptors that are actually used,
// in a stable order (by ImportName).
Funcs []HostFunction
// Children records child workflow names detected in the AST.
// Keys are the first argument string literals of h.ChildWorkflow(name, ...),
// h.ChildWorkflowWithOptions(name, ...), and h.ChildWorkflowTyped(name, ...).
Children map[string]bool
}
UsageInfo records which host functions are actually called by the cleat closure and provides lookup helpers for code generation.
func AnalyzeUsage ¶
func AnalyzeUsage(result *analyzer.AnalysisResult, cr *closure.Result) *UsageInfo
AnalyzeUsage scans every function in the cleat closure and returns which HostCalls methods are called.