Skip to main content
Experimental Preview Feature - Alternative Policy EngineThe WASM policy engine is NOT the default policy engine in Chainloop. The default policy engine uses Rego (Open Policy Agent).This WASM policy engine is currently in experimental preview for users who want to write policies in Go or JavaScript instead of Rego. APIs and features may change in future releases. We recommend testing thoroughly before using in production.Currently supported languages for WASM policies:
  • ✅ Go (via TinyGo)
  • ✅ JavaScript (via Extism JS)
  • 🚧 Additional languages coming soon
For most users, we recommend using the default Rego-based policy engine documented in Writing Custom Policies.
We recommend getting familiar with the Policy concept before writing your first WASM policy.

What are WASM Policies?

WASM (WebAssembly) policies are custom validation rules that run in a secure sandbox environment using Chainloop’s experimental WASM policy engine. This is an alternative to Chainloop’s default Rego-based policy engine. Unlike Rego policies (the default), WASM policies let you write validation logic in familiar programming languages like Go or JavaScript, with full access to standard libraries and ecosystem tools. Key benefits of WASM policies:
  • Write policies in Go or JavaScript using high-level SDKs
  • Access to language ecosystems (JSON parsing, HTTP clients, etc.)
  • Type safety and IDE support
  • Secure execution in WebAssembly sandbox
  • Portable across platforms

When to Use WASM vs Rego

FeatureWASM Policies (Experimental)Rego Policies (Default)
StatusExperimental previewProduction-ready default
LanguagesGo, JavaScript, Rust, etc.Rego only
Learning CurveUse familiar languagesLearn Rego syntax
LibrariesFull language ecosystemLimited built-ins
Type SafetyFull type checkingDynamic typing
PerformanceFast (native code)Fast (optimized)
SandboxWASM isolationOPA runtime
Use WASM policies when:
  • You need complex logic with external libraries
  • Your team prefers Go/JavaScript over Rego
  • You need HTTP API integration
  • You want strong type safety and IDE support
  • You’re willing to use experimental features
Use Rego policies (recommended default) when:
  • You want production-ready, stable policy engine
  • You need declarative policy syntax
  • Your policies are simple queries and filters
  • You’re already familiar with OPA/Rego
  • You prefer the standard, widely-adopted approach

How WASM Policies Work

Execution Architecture

WASM policies run in a host/guest architecture where:
  • Host (Chainloop): The policy engine that loads and executes WASM modules
  • Guest (Policy): Your WASM policy code running in an isolated sandbox
WASM Policy Architecture Key characteristics:
  • In-process execution: Policies run embedded in the control plane process (not as sidecars)
  • Sandboxed environment: WebAssembly isolation prevents filesystem access and unsafe operations
  • Single-threaded: Each policy execution is single-threaded and deterministic
  • Bytes-in/bytes-out: Material data flows through a simple byte interface
  • Host functions: Controlled capabilities (HTTP, discovery) exposed by Chainloop
The Execute Function is RequiredEvery WASM policy must export an Execute() function as the entry point. This is how Chainloop invokes your policy:
//export Execute
func Execute() int32 {
    return chainloop.Run(validate)
}
function Execute() {
  return run(() => {
    // validation logic
  });
}

module.exports = { Execute };
Without the Execute export, the policy will fail to load.

Technology Stack

Chainloop WASM policies are powered by Extism, a universal plugin system that allows policies written in different languages to run securely in WebAssembly sandboxes. Why Extism?
  • Multi-language support: Extensible architecture for multiple languages
  • Security: WASM sandbox isolates policies from the host system
  • Performance: Fast in-process execution with minimal overhead
  • Portability: Same policy runs anywhere WASM is supported
  • Flexible serialization: Supports JSON, Protocol Buffers, and other formats
Chainloop SDKs wrap Extism PDKs:
While Extism supports additional languages (Rust, Python, C#, etc.), Chainloop currently provides official SDKs for Go and JavaScript only. Additional language support is planned for future releases.
You don’t need to interact with Extism directly - Chainloop’s SDKs provide all the high-level functions you need for policy development.

Quick Start

1. Choose Your Language

2. Create Your First Policy

Here’s a minimal policy that validates a string message:
package main

import (
    "encoding/json"

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

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

func validate() error {
    // Get the material to validate
    var input struct {
        Message string `json:"message"`
    }

    if err := chainloop.GetMaterialJSON(&input); err != nil {
        return chainloop.Skip("Material is not valid JSON")
    }

    // Validate the message
    result := chainloop.Success()

    if input.Message == "" {
        result.AddViolation("message cannot be empty")
    }

    return chainloop.OutputResult(result)
}

func main() {}

3. Build the Policy

# Install TinyGo first
brew install tinygo  # macOS
# or download from https://tinygo.org/

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

4. Create Policy Configuration

Create a policy.yaml file:
apiVersion: workflowcontract.chainloop.dev/v1
kind: Policy
metadata:
  name: my-first-policy
  description: Validates message field in materials
spec:
  policies:
    - kind: EVIDENCE  # Material type to validate
      path: policy.wasm

5. Test Locally

Create test data (test.json):
{
  "message": "hello world"
}
Test the policy:
chainloop policy devel eval \
  --policy policy.yaml \
  --material test.json \
  --kind EVIDENCE
Expected output:
{
  "result": {
    "violations": [],
    "skip_reasons": [],
    "skipped": false
  }
}

Policy Result States

Every policy execution returns one of three states:

Success

Material passes all validation rules.
result := chainloop.Success()
return chainloop.OutputResult(result)

Fail

Material violates one or more rules. Include specific violation messages.
result := chainloop.Success()
result.AddViolation("license GPL-3.0 not approved")
result.AddViolation("missing required field 'version'")
return chainloop.OutputResult(result)

Skip

Policy doesn’t apply to this material (wrong format, missing fields, etc.).
return chainloop.Skip("Material is not a CycloneDX SBOM")
Important: Use Skip when the material isn’t applicable to your policy. Don’t fail policies for materials they weren’t designed to validate.

Testing Workflow

  1. Write the policy in Go or JavaScript
  2. Build to WASM using TinyGo or extism-js
  3. Test locally with chainloop policy devel eval
  4. Iterate based on test results
  5. Deploy to Chainloop once validated
See the Policies concept for details on policy management.

Next Steps