Build with the Component SDK
Create collectors, tools, utilities, and remote adapters.
Prerequisites
- Go 1.21+
- epack CLI installed
Create a component
epack sdk new collector myservice
This creates a ready-to-build component project:
epack-collector-myservice/ ├── main.go # Implementation ├── go.mod ├── README.md ├── .gitignore ├── docs/ # Registry documentation │ ├── overview.md │ ├── configuration.md │ └── examples.md ├── .slsa-goreleaser/ # Multi-platform build configs │ └── *.yml └── .github/workflows/ └── release.yaml # SLSA Level 3 + Sigstore signing
Component types
epack sdk new collector myservice # Gathers evidence from APIs epack sdk new tool analyzer # Processes packs epack sdk new utility viewer # Standalone CLI tool epack sdk new remote mycloud # Push/pull adapter
Develop with watch mode
Run your component with automatic rebuild on file changes:
cd epack-collector-myservice epack sdk run --watch .
Pass arguments after --:
epack sdk run --watch . -- --capabilities
Generate test data
Create sample inputs for testing:
# Generate test fixtures epack sdk mock tool # Creates sample-evidence.epack epack sdk mock collector # Creates sample config and env vars epack sdk mock tool -o testdata # Output to testdata/
Run conformance tests
Verify your component implements the protocol correctly:
# Build and test epack sdk test . # Test a binary directly epack sdk test ./epack-collector-myservice # Verbose output epack sdk test --verbose .
Implement a collector
Edit main.go to fetch from your API:
main.go
func run(ctx componentsdk.CollectorContext) error { // Get config from epack.yaml org := ctx.Config()["organization"].(string) // Get secrets from environment token := ctx.Secret("MYSERVICE_API_TOKEN") // Call your API data, err := fetchFromAPI(org, token) if err != nil { return componentsdk.NewNetworkError("API call failed: %v", err) } // Emit collected artifacts return ctx.Emit([]componentsdk.CollectedArtifact{{ Data: map[string]any{ "collected_at": time.Now().UTC().Format(time.RFC3339), "organization": org, "mfa_enabled": data.MFAEnabled, "user_count": data.UserCount, }, }}) }
Error types
componentsdk.NewConfigError("missing field: %s", field) # Exit 2 componentsdk.NewAuthError("invalid token") # Exit 3 componentsdk.NewNetworkError("request failed: %v", err) # Exit 4
Implement a tool
main.go
func run(ctx componentsdk.ToolContext) error { // Access the pack pack := ctx.Pack() // Read an artifact data, err := pack.ReadArtifact("artifacts/github-repos.json") if err != nil { return err } // Analyze result := analyze(data) // Write output return ctx.WriteOutput("analysis.json", result) }
Pack access
pack.Manifest() # Get manifest pack.ReadArtifact("artifacts/report.json") # Read artifact ctx.Warn("CODE", "message", "path") # Add warning ctx.Error("CODE", "message", "path") # Add error
Implement a utility
main.go
func run(args []string) error { if len(args) == 0 { return fmt.Errorf("no pack file specified") } pack, err := componentsdk.OpenPack(args[0]) if err != nil { return err } defer pack.Close() fmt.Printf("Stream: %s\n", pack.Manifest().Stream) return nil }
Implement a remote adapter
main.go
type handler struct{} func (h *handler) PushPrepare(req componentsdk.PushPrepareRequest) (*componentsdk.PushPrepareResponse, error) { url, token := generateUploadURL(req.Target.Workspace, req.Pack.Digest) return &componentsdk.PushPrepareResponse{ Upload: componentsdk.UploadInfo{Method: "PUT", URL: url}, FinalizeToken: token, }, nil } func (h *handler) PushFinalize(req componentsdk.PushFinalizeRequest) (*componentsdk.PushFinalizeResponse, error) { release := createRelease(req.FinalizeToken) return &componentsdk.PushFinalizeResponse{ Release: componentsdk.ReleaseResult{ReleaseID: release.ID, PackDigest: release.Digest}, }, nil } func (h *handler) PullPrepare(req componentsdk.PullPrepareRequest) (*componentsdk.PullPrepareResponse, error) { url, digest, token := getDownloadURL(req.Target.Workspace, req.Ref) return &componentsdk.PullPrepareResponse{ Download: componentsdk.DownloadInfo{URL: url}, Pack: componentsdk.PackResult{Digest: digest}, FinalizeToken: token, }, nil } func (h *handler) PullFinalize(req componentsdk.PullFinalizeRequest) (*componentsdk.PullFinalizeResponse, error) { return &componentsdk.PullFinalizeResponse{Confirmed: true}, nil }
Release
Tag a release to trigger the scaffolded GitHub workflow:
git tag v0.1.29 git push origin v0.1.29
The workflow builds for linux/amd64, linux/arm64, darwin/amd64, darwin/arm64 with SLSA Level 3 provenance and Sigstore signing.
Configure in epack.yaml
Collectors and tools
epack.yaml
collectors: myservice: source: ghcr.io/yourorg/epack-collector-myservice@v1 config: organization: acme secrets: - MYSERVICE_API_TOKEN tools: analyzer: source: ghcr.io/yourorg/epack-tool-analyzer@v1
Remotes
epack.yaml
remotes: mycloud: source: ghcr.io/yourorg/epack-remote-mycloud@v1 target: workspace: acme auth: mode: api_key
Utilities
Utilities are standalone CLI tools invoked directly:
epack utility viewer evidence.epack
Related
- SDK Reference — Full API documentation
- Component Protocol — Wire format
- Run collectors — Using collectors