Skip to main content
Experimental Feature - WASM Policy EngineThis SDK is specifically for writing WASM policies using Chainloop’s experimental WASM policy engine. The WASM policy engine is NOT the default policy engine in Chainloop.
  • Default engine: Rego-based (recommended for most users)
  • WASM engine: Experimental alternative for Go/JavaScript policies
  • Status: Experimental preview - APIs may change in future releases
For the default Rego-based policy engine, see Writing Custom Policies.

Prerequisites

The Chainloop Go SDK for WASM policies is built on top of the Extism Go PDK, which provides the WebAssembly plugin interface. Required tools:
TinyGo compiles Go code to WebAssembly with minimal binary size.
# macOS
brew install tinygo

# Linux
wget https://github.com/tinygo-org/tinygo/releases/download/v0.30.0/tinygo_0.30.0_amd64.deb
sudo dpkg -i tinygo_0.30.0_amd64.deb

# Verify installation
tinygo version
For dependency management and module support.
# Verify installation
go version
Dependencies:
  • github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go - Chainloop WASM Policy SDK
  • github.com/extism/go-pdk - Extism Plugin Development Kit (auto-installed)
The Extism Go PDK provides the low-level WASM interface, while Chainloop’s WASM Policy SDK provides high-level policy-specific functions for material validation.

Project Setup

Create go.mod

module myorganization.com/chainloop-policies/my-policy

go 1.25

require (
    github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go v0.0.0
    github.com/extism/go-pdk v1.0.0
)

// Replace with local path during development
replace github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go => /path/to/chainloop/sdks/go

Create policy.yaml

apiVersion: workflowcontract.chainloop.dev/v1
kind: Policy
metadata:
  name: my-go-policy
  description: My custom Go policy
spec:
  policies:
    - kind: EVIDENCE  # Or SBOM_CYCLONEDX_JSON, ATTESTATION, etc.
      path: policy.wasm

Complete Example

Here’s a complete policy that validates SBOM components:
package main

import (
	"fmt"

	chainloop "github.com/chainloop-dev/chainloop/labs/wasm-policy-sdk/go"
	"github.com/extism/go-pdk"
)

// SBOM represents a CycloneDX SBOM structure
type SBOM struct {
	Components []Component `json:"components"`
}

type Component struct {
	Name    string `json:"name"`
	Version string `json:"version"`
	Licenses []License `json:"licenses"`
}

type License struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

//export Execute
func Execute() int32 {
	return chainloop.Run(validateSBOM)
}

func validateSBOM() error {
	// Parse the SBOM material
	var sbom SBOM
	if err := chainloop.GetMaterialJSON(&sbom); err != nil {
		return chainloop.Skip("Material is not a valid CycloneDX SBOM")
	}

	chainloop.LogInfo(fmt.Sprintf("Validating SBOM with %d components", len(sbom.Components)))

	// Create result
	result := chainloop.Success()

	// Approved licenses
	approved := []string{"MIT", "Apache-2.0", "BSD-3-Clause", "ISC"}

	// Validate each component
	for _, component := range sbom.Components {
		// Check version
		if component.Version == "" {
			result.AddViolationf("component '%s' missing version", component.Name)
		}

		// Check licenses
		if len(component.Licenses) == 0 {
			result.AddViolationf("component '%s' missing license", component.Name)
			continue
		}

		// Validate license against approved list
		for _, license := range component.Licenses {
			licenseID := license.ID
			if licenseID == "" {
				licenseID = license.Name
			}

			if !contains(approved, licenseID) {
				result.AddViolationf("component '%s' has unapproved license: %s", component.Name, licenseID)
			}
		}
	}

	if result.HasViolations() {
		chainloop.LogError(fmt.Sprintf("SBOM validation failed with %d violations", len(result.Violations)))
	} else {
		chainloop.LogInfo("SBOM validation passed")
	}

	return chainloop.OutputResult(result)
}

func contains(slice []string, item string) bool {
	for _, s := range slice {
		if s == item {
			return true
		}
	}
	return false
}

func main() {}

API Quick Reference

The Go SDK provides functions for:
  • Execution: Run() - Entry point wrapper
  • Material Extraction: GetMaterialJSON(), GetMaterialString(), GetMaterialBytes()
  • Arguments: GetArgs(), GetArgString(), GetArgStringDefault()
  • Results: Success(), Fail(), Skip(), OutputResult(), AddViolation(), HasViolations()
  • Logging: LogInfo(), LogDebug(), LogWarn(), LogError()
  • HTTP Requests: HTTPGet(), HTTPGetJSON(), HTTPPost(), HTTPPostJSON()
  • Artifact Discovery: Discover(), DiscoverByDigest()

Building

Basic Build

tinygo build -target=wasi -o policy.wasm policy.go

Build Flags

  • -target=wasi - WebAssembly System Interface target
  • -o policy.wasm - Output file name

Typical File Sizes

  • Simple policy: ~770KB
  • SBOM policy: ~770KB
  • HTTP policy: ~793KB

Testing

Create Test Script

test.sh:
#!/bin/bash

# Build policy
tinygo build -target=wasi -o policy.wasm policy.go

# Test with valid SBOM
echo "Testing valid SBOM..."
chainloop policy devel eval \
  --policy policy.yaml \
  --material test-data/valid-sbom.json \
  --kind SBOM_CYCLONEDX_JSON

# Test with invalid SBOM
echo "Testing invalid SBOM..."
chainloop policy devel eval \
  --policy policy.yaml \
  --material test-data/invalid-sbom.json \
  --kind SBOM_CYCLONEDX_JSON
Make executable:
chmod +x test.sh
./test.sh

TinyGo Compatibility

TinyGo has some limitations compared to standard Go: Supported:
  • ✅ Flat structs with simple types
  • ✅ Slices and maps with string keys
  • ✅ json.Unmarshal for parsing
  • ✅ String manipulation
  • ✅ Basic math operations
Limited/Unsupported:
  • ⚠️ Generics (limited support)
  • ❌ Reflection (limited)
  • ❌ Goroutines (limited)

Best Practices

  1. Keep types simple - Use flat structs with concrete types
  2. Validate early - Check input format before complex logic
  3. Return specific violations - Include field names and values in messages
  4. Use skip for non-applicable cases - Don’t fail policies for wrong material types
  5. Test with real data - Use actual SBOMs/attestations from your projects
  6. Log validation progress - Use logging for debugging
  7. Handle errors gracefully - Always check error returns

Next Steps