Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/client/workflow_registry_v2_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ type RegisterWorkflowV2Parameters struct {

BinaryURL string // required: URL location for the workflow binary WASM file
ConfigURL string // optional: URL location for the workflow configuration file (default empty string)
Attributes []byte // optional: 1 to pause workflow after registration, 0 to activate it (default is 0)
Attributes []byte // optional: JSON-encoded workflow attributes (e.g. confidential flag, enclave type)
KeepAlive bool // optional: whether to keep the other workflows of the same name and owner active after the new deploy (default is false)
}

Expand Down
16 changes: 16 additions & 0 deletions cmd/workflow/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ type Inputs struct {

OwnerLabel string `validate:"omitempty"`
SkipConfirmation bool

Enclave string // enclave type (e.g. "nitro"); empty means non-confidential
}

func (i *Inputs) ResolveConfigURL(fallbackURL string) string {
Expand Down Expand Up @@ -104,6 +106,7 @@ func New(runtimeContext *runtime.Context) *cobra.Command {
settings.AddSkipConfirmation(deployCmd)
deployCmd.Flags().StringP("output", "o", defaultOutputPath, "The output file for the compiled WASM binary encoded in base64")
deployCmd.Flags().StringP("owner-label", "l", "", "Label for the workflow owner (used during auto-link if owner is not already linked)")
deployCmd.Flags().String("enclave", "", "Enclave type for confidential execution (e.g. \"nitro\"); prefer setting confidential.enclave in workflow.yaml")

return deployCmd
}
Expand Down Expand Up @@ -171,10 +174,20 @@ func (h *handler) ResolveInputs(v *viper.Viper) (Inputs, error) {
WorkflowRegistryContractAddress: h.environmentSet.WorkflowRegistryAddress,
OwnerLabel: v.GetString("owner-label"),
SkipConfirmation: v.GetBool(settings.Flags.SkipConfirmation.Name),
Enclave: h.resolveEnclave(v),
}, nil
}

// resolveEnclave returns the enclave type from workflow.yaml, overridden by CLI flag.
func (h *handler) resolveEnclave(v *viper.Viper) string {
if cli := v.GetString("enclave"); cli != "" {
return cli
}
return h.settings.Workflow.ConfidentialSettings.Enclave
}

func (h *handler) ValidateInputs() error {

validate, err := validation.NewValidator()
if err != nil {
return fmt.Errorf("failed to initialize validator: %w", err)
Expand Down Expand Up @@ -299,5 +312,8 @@ func (h *handler) displayWorkflowDetails() {
ui.Title(fmt.Sprintf("Deploying Workflow: %s", h.inputs.WorkflowName))
ui.Dim(fmt.Sprintf("Target: %s", h.settings.User.TargetName))
ui.Dim(fmt.Sprintf("Owner Address: %s", h.settings.Workflow.UserWorkflowSettings.WorkflowOwnerAddress))
if h.inputs.Enclave != "" {
ui.Dim(fmt.Sprintf("Enclave: %s", h.inputs.Enclave))
}
ui.Line()
}
30 changes: 29 additions & 1 deletion cmd/workflow/deploy/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package deploy

import (
"encoding/hex"
"encoding/json"
"fmt"
"time"

Expand Down Expand Up @@ -43,6 +44,11 @@ func (h *handler) prepareUpsertParams() (client.RegisterWorkflowV2Parameters, er
status = *h.existingWorkflowStatus
}

attrs, err := h.buildAttributes()
if err != nil {
return client.RegisterWorkflowV2Parameters{}, err
}

ui.Dim(fmt.Sprintf("Preparing transaction for workflowID: %s", workflowID))
return client.RegisterWorkflowV2Parameters{
WorkflowName: workflowName,
Expand All @@ -52,7 +58,7 @@ func (h *handler) prepareUpsertParams() (client.RegisterWorkflowV2Parameters, er
DonFamily: h.inputs.DonFamily,
BinaryURL: binaryURL,
ConfigURL: configURL,
Attributes: []byte{}, // optional
Attributes: attrs,
KeepAlive: h.inputs.KeepAlive,
}, nil
}
Expand Down Expand Up @@ -145,3 +151,25 @@ func (h *handler) handleUpsert(params client.RegisterWorkflowV2Parameters) error
}
return nil
}

func (h *handler) buildAttributes() ([]byte, error) {
if h.inputs.Enclave == "" {
return []byte{}, nil
}

attrs := workflowAttributes{
Confidential: true,
Enclave: h.inputs.Enclave,
}

data, err := json.Marshal(attrs)
if err != nil {
return nil, fmt.Errorf("marshalling workflow attributes: %w", err)
}
return data, nil
}

type workflowAttributes struct {
Confidential bool `json:"confidential"`
Enclave string `json:"enclave,omitempty"`
}
25 changes: 25 additions & 0 deletions cmd/workflow/deploy/register_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package deploy

import (
"encoding/json"
"path/filepath"
"testing"

Expand Down Expand Up @@ -171,3 +172,27 @@ func TestPrepareUpsertParams_StatusPreservation(t *testing.T) {
assert.Equal(t, uint8(0), params.Status, "updating active workflow should preserve active status (0)")
})
}

func TestBuildAttributes(t *testing.T) {
t.Parallel()

t.Run("non-confidential workflow returns empty attributes", func(t *testing.T) {
t.Parallel()
h := &handler{inputs: Inputs{Enclave: ""}}
attrs, err := h.buildAttributes()
require.NoError(t, err)
assert.Equal(t, []byte{}, attrs)
})

t.Run("nitro enclave sets confidential and enclave type", func(t *testing.T) {
t.Parallel()
h := &handler{inputs: Inputs{Enclave: "nitro"}}
attrs, err := h.buildAttributes()
require.NoError(t, err)

var parsed workflowAttributes
require.NoError(t, json.Unmarshal(attrs, &parsed))
assert.True(t, parsed.Confidential)
assert.Equal(t, "nitro", parsed.Enclave)
})
}
1 change: 1 addition & 0 deletions internal/settings/settings_load.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
ConfigPathSettingName = "workflow-artifacts.config-path"
SecretsPathSettingName = "workflow-artifacts.secrets-path"
SethConfigPathSettingName = "logging.seth-config-path"
ConfidentialEnclaveSettingName = "confidential.enclave"
RegistriesSettingName = "contracts.registries"
KeystoneSettingName = "contracts.keystone"
RpcsSettingName = "rpcs"
Expand Down
2 changes: 2 additions & 0 deletions internal/settings/template/workflow.yaml.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
# workflow-path: "./main.ts" # Path to workflow entry point
# config-path: "./config.yaml" # Path to config file
# secrets-path: "../secrets.yaml" # Path to secrets file (project root by default)
# confidential: # Optional: run workflow in a TEE
# enclave: "nitro" # Enclave type (e.g. "nitro")

# ==========================================================================
staging-settings:
Expand Down
9 changes: 9 additions & 0 deletions internal/settings/workflow_settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,20 @@ type WorkflowSettings struct {
ConfigPath string `mapstructure:"config-path" yaml:"config-path"`
SecretsPath string `mapstructure:"secrets-path" yaml:"secrets-path"`
} `mapstructure:"workflow-artifacts" yaml:"workflow-artifacts"`
ConfidentialSettings struct {
Enclave string `mapstructure:"enclave" yaml:"enclave"`
} `mapstructure:"confidential" yaml:"confidential"`
LoggingSettings struct {
SethConfigPath string `mapstructure:"seth-config-path" yaml:"seth-config-path"`
} `mapstructure:"logging" yaml:"logging"`
RPCs []RpcEndpoint `mapstructure:"rpcs" yaml:"rpcs"`
}

// IsConfidential returns true if the workflow has a non-empty enclave setting.
func (w *WorkflowSettings) IsConfidential() bool {
return w.ConfidentialSettings.Enclave != ""
}

func loadWorkflowSettings(logger *zerolog.Logger, v *viper.Viper, cmd *cobra.Command, registryChainName string) (WorkflowSettings, error) {
target, err := GetTarget(v)
if err != nil {
Expand Down Expand Up @@ -131,6 +139,7 @@ func loadWorkflowSettings(logger *zerolog.Logger, v *viper.Viper, cmd *cobra.Com
workflowSettings.WorkflowArtifactSettings.ConfigPath = getSetting(ConfigPathSettingName)
workflowSettings.WorkflowArtifactSettings.SecretsPath = getSetting(SecretsPathSettingName)
workflowSettings.LoggingSettings.SethConfigPath = getSetting(SethConfigPathSettingName)
workflowSettings.ConfidentialSettings.Enclave = getSetting(ConfidentialEnclaveSettingName)
fullRPCsKey := fmt.Sprintf("%s.%s", target, RpcsSettingName)
if v.IsSet(fullRPCsKey) {
if err := v.UnmarshalKey(fullRPCsKey, &workflowSettings.RPCs); err != nil {
Expand Down
Loading