> ## Documentation Index
> Fetch the complete documentation index at: https://docs.chainloop.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK for WASM Policies

> Complete guide to writing Chainloop policies in Go with TinyGo

<Warning>
  **Experimental Feature - WASM Policy Engine**

  This 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](/guides/custom-policies).
</Warning>

## Prerequisites

The Chainloop Go SDK for WASM policies is built on top of the [Extism Go PDK](https://github.com/extism/go-pdk), which provides the WebAssembly plugin interface.

**Required tools:**

<AccordionGroup>
  <Accordion title="TinyGo (0.30.0 or later)">
    TinyGo compiles Go code to WebAssembly with minimal binary size.

    ```bash theme={"dark"}
    # 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
    ```
  </Accordion>

  <Accordion title="Go (1.21 or later)">
    For dependency management and module support.

    ```bash theme={"dark"}
    # Verify installation
    go version
    ```
  </Accordion>
</AccordionGroup>

**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

```go theme={"dark"}
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

```yaml theme={"dark"}
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:

```go theme={"dark"}
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

```bash theme={"dark"}
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`:

```bash theme={"dark"}
#!/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:

```bash theme={"dark"}
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

<CardGroup cols={2}>
  <Card title="Examples" icon="code" href="/guides/wasm-policies/examples">
    See complete policy examples and common patterns
  </Card>

  <Card title="JavaScript SDK" icon="js" href="/guides/wasm-policies/javascript-sdk">
    Learn about the JavaScript SDK alternative
  </Card>
</CardGroup>
