Repository: bacalhau-project/bacalhau (language: python)

Issue:
how to set default publisher
Not sure what `params` are used for
```
→ bacalhau config list | grep publisher
11:14:07.493 | INF ProtocolLabs/workspace/bacalhau/cmd/cli/config/list.go:44 > Config loaded from: [], and with data-dir /Users/walid/.bacalhau
 jobdefaults.batch.task.publisher.config.params    map[]                        No description available
 jobdefaults.batch.task.publisher.config.type                                   No description available
 jobdefaults.ops.task.publisher.config.params      map[]                        No description available
 jobdefaults.ops.task.publisher.config.type                                     No description available
```


Discussion / hints:
The default publisher is a spec config - so params could contain things like the S3 bucket and key, the ipfs endpoint, etc.

Fix patch:
diff --git a/cmd/util/flags/configflags/publishing.go b/cmd/util/flags/configflags/publishing.go
index 186d8cebf6..f52b5f97f3 100644
--- a/cmd/util/flags/configflags/publishing.go
+++ b/cmd/util/flags/configflags/publishing.go
@@ -15,7 +15,7 @@ var PublishingFlags = []Definition{
 		Deprecated:   true,
 		DeprecatedMessage: fmt.Sprintf("Use one or more of the following options, all are accepted %s, %s",
 			makeConfigFlagDeprecationCommand(types.JobDefaultsBatchTaskPublisherTypeKey),
-			makeConfigFlagDeprecationCommand(types.JobDefaultsOpsTaskPublisherParamsKey),
+			makeConfigFlagDeprecationCommand(types.JobDefaultsOpsTaskPublisherTypeKey),
 		),
 	},
 }
diff --git a/pkg/config/types/bacalhau.go b/pkg/config/types/bacalhau.go
index aa21c19150..51e4b25f0f 100644
--- a/pkg/config/types/bacalhau.go
+++ b/pkg/config/types/bacalhau.go
@@ -11,6 +11,7 @@ import (
 // NB: Developers, after making changes (comments included) to this struct or any of its children, run go generate.
 
 //go:generate go run gen/generate.go ./
+//go:generate go fmt ./generated_constants.go ./generated_descriptions.go
 type Bacalhau struct {
 	API API `yaml:"API,omitempty" json:"API,omitempty"`
 	// NameProvider specifies the method used to generate names for the node. One of: hostname, aws, gcp, uuid, puuid.
diff --git a/pkg/config/types/gen/generate.go b/pkg/config/types/gen/generate.go
index 5eec5baca2..ef00c27352 100644
--- a/pkg/config/types/gen/generate.go
+++ b/pkg/config/types/gen/generate.go
@@ -5,57 +5,65 @@ import (
 	"go/ast"
 	"go/parser"
 	"go/token"
+	"io"
+	"log"
 	"os"
 	"path/filepath"
 	"reflect"
+	"sort"
 	"strings"
-
-	"github.com/bacalhau-project/bacalhau/pkg/config/types"
 )
 
-// processStructFields processes the fields of a struct type using the provided handler function.
-func processStructFields(t reflect.Type, prefix string, file *os.File, handleLeafNode func(field reflect.StructField, newPrefix string)) {
-	for i := 0; i < t.NumField(); i++ {
-		field := t.Field(i)
-		tag := field.Tag.Get("config")
-		newPrefix := prefix
+func main() {
+	if len(os.Args) != 2 {
+		fmt.Println("Please provide the path to the config directory.")
+		os.Exit(1)
+	}
+	pkgPath := os.Args[1]
 
-		if tag != "" {
-			if newPrefix != "" {
-				newPrefix += "."
-			}
-			newPrefix += tag
-		} else if !field.Anonymous {
-			if newPrefix != "" {
-				newPrefix += "."
-			}
-			newPrefix += field.Name
-		}
+	fieldInfos := ConfigFieldMap(pkgPath)
+	// Generate constants file
+	constantsFile, err := os.Create(filepath.Join(pkgPath, "generated_constants.go"))
+	if err != nil {
+		panic(err)
+	}
+	defer constantsFile.Close()
 
-		if field.Type.Kind() == reflect.Struct {
-			// Recursively process nested structs
-			processStructFields(field.Type, newPrefix, file, handleLeafNode)
-		} else {
-			// Handle leaf node
-			handleLeafNode(field, newPrefix)
-		}
+	if err := WriteConstants(fieldInfos, constantsFile); err != nil {
+		panic(err)
 	}
-}
 
-// generateConstants generates constant definitions for each leaf field in the given struct type.
-func generateConstants(t reflect.Type, prefix string, file *os.File) {
-	handleLeafNode := func(field reflect.StructField, newPrefix string) {
-		constantName := strings.ReplaceAll(newPrefix, ".", "")
-		constantValue := strings.ToLower(newPrefix)
-		fmt.Fprintf(file, "const %sKey = \"%s\"\n", constantName, constantValue)
+	commentsFile, err := os.Create(filepath.Join(pkgPath, "generated_descriptions.go"))
+	if err != nil {
+		panic(err)
+	}
+	defer commentsFile.Close()
+
+	if err := WriteComments(fieldInfos, commentsFile); err != nil {
+		panic(err)
 	}
-	processStructFields(t, prefix, file, handleLeafNode)
 }
 
-// generateDescriptions generates the map of configuration descriptions.
-func generateDescriptions(t reflect.Type, prefix string, file *os.File) {
-	handleLeafNode := func(field reflect.StructField, newPrefix string) {
-		description := commentsMap[field.Name]
+func WriteComments(fieldInfos map[string]FieldInfo, w io.Writer) error {
+	var builder strings.Builder
+	// write method declaration
+	builder.WriteString("// CODE GENERATED BY pkg/config/types/gen/generate.go DO NOT EDIT\n\n")
+	builder.WriteString("package types\n\n")
+	builder.WriteString("// ConfigDescriptions maps configuration paths to their descriptions\n")
+	builder.WriteString("var ConfigDescriptions = map[string]string{\n")
+
+	// Collect keys and sort them
+	var keys []string
+	for k := range fieldInfos {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	// Generate constant declarations
+	for _, key := range keys {
+		info := fieldInfos[key]
+		constName := generateConstantName(info.CapitalizedPath)
+		description := info.Comment
 		if description == "" {
 			description = "No description available"
 		} else {
@@ -65,91 +73,234 @@ func generateDescriptions(t reflect.Type, prefix string, file *os.File) {
 			description = strings.TrimSpace(description)
 			description = escapeString(description)
 		}
-
-		constantName := strings.ReplaceAll(newPrefix, ".", "")
-		fmt.Fprintf(file, "\t%sKey: \"%s\",\n", constantName, description)
+		builder.WriteString(fmt.Sprintf("\t%s: \"%s\",\n", constName, description))
 	}
-	processStructFields(t, prefix, file, handleLeafNode)
+	builder.WriteString("}\n")
+	// Write the content to the specified file
+	if _, err := io.WriteString(w, builder.String()); err != nil {
+		return err
+	}
+
+	return nil
 }
 
-var commentsMap = make(map[string]string)
+func WriteConstants(fieldInfos map[string]FieldInfo, w io.Writer) error {
+	var builder strings.Builder
+	// write method declaration
+	builder.WriteString("// CODE GENERATED BY pkg/config/types/gen/generate.go DO NOT EDIT\n\n")
+	builder.WriteString("package types\n\n")
 
-// extractComments extracts comments from the source files in the provided directory.
-func extractComments(dir string) error {
+	// Collect keys and sort them
+	var keys []string
+	for k := range fieldInfos {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+
+	// Generate constant declarations
+	for _, key := range keys {
+		info := fieldInfos[key]
+		constName := generateConstantName(info.CapitalizedPath)
+		builder.WriteString(fmt.Sprintf("const %s = \"%s\"\n", constName, key))
+	}
+	// Write the content to the specified file
+	if _, err := io.WriteString(w, builder.String()); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func ConfigFieldMap(dir string) map[string]FieldInfo {
+	// Parse the package directory
 	fset := token.NewFileSet()
-	packages, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
+	pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments)
 	if err != nil {
-		return fmt.Errorf("error parsing directory %s: %w", dir, err)
+		log.Fatal(err)
 	}
 
-	for _, pkg := range packages {
+	// Map from lowercased field path to FieldInfo
+	fieldInfos := make(map[string]FieldInfo)
+	typeMap := make(map[string]*ast.StructType)
+
+	// Build a map of type names to *ast.StructType
+	for _, pkg := range pkgs {
 		for _, file := range pkg.Files {
 			ast.Inspect(file, func(n ast.Node) bool {
-				switch x := n.(type) {
-				case *ast.TypeSpec:
-					if x.Doc != nil {
-						commentsMap[x.Name.Name] = strings.TrimSpace(x.Doc.Text())
+				// Look for type declarations
+				genDecl, ok := n.(*ast.GenDecl)
+				if !ok || genDecl.Tok != token.TYPE {
+					return true
+				}
+				for _, spec := range genDecl.Specs {
+					typeSpec, ok := spec.(*ast.TypeSpec)
+					if !ok {
+						continue
 					}
-				case *ast.Field:
-					if x.Doc != nil && len(x.Names) > 0 {
-						commentsMap[x.Names[0].Name] = strings.TrimSpace(x.Doc.Text())
+					structType, ok := typeSpec.Type.(*ast.StructType)
+					if !ok {
+						continue
 					}
+					structName := typeSpec.Name.Name
+					typeMap[structName] = structType
 				}
-				return true
+				return false
 			})
 		}
 	}
-	return nil
+
+	// Start processing from the Bacalhau struct
+	if bacalhauStruct, ok := typeMap["Bacalhau"]; ok {
+		processStruct("", "", bacalhauStruct, fieldInfos, typeMap)
+	} else {
+		log.Fatal("Could not find Bacalhau struct")
+	}
+
+	return fieldInfos
 }
 
-// escapeString escapes double quotes in the string for safe inclusion in Go source code.
-func escapeString(s string) string {
-	return strings.ReplaceAll(s, `"`, `\"`)
+// FieldInfo stores information about a field
+type FieldInfo struct {
+	Comment         string
+	CapitalizedPath string
 }
 
-func main() {
-	if len(os.Args) != 2 {
-		fmt.Println("Please provide the path to the config directory.")
-		os.Exit(1)
-	}
-	inputDir := os.Args[1]
+func processStruct(prefix string, capPrefix string, structType *ast.StructType, fieldInfos map[string]FieldInfo, typeMap map[string]*ast.StructType) {
+	for _, field := range structType.Fields.List {
+		// Get field names
+		var fieldNames []string
+		var fieldNamesOriginal []string
+		if len(field.Names) > 0 {
+			for _, name := range field.Names {
+				fieldNames = append(fieldNames, name.Name)
+				fieldNamesOriginal = append(fieldNamesOriginal, name.Name)
+			}
+		} else {
+			// Embedded field
+			switch t := field.Type.(type) {
+			case *ast.Ident:
+				fieldNames = append(fieldNames, t.Name)
+				fieldNamesOriginal = append(fieldNamesOriginal, t.Name)
+			case *ast.StarExpr:
+				if ident, ok := t.X.(*ast.Ident); ok {
+					fieldNames = append(fieldNames, ident.Name)
+					fieldNamesOriginal = append(fieldNamesOriginal, ident.Name)
+				}
+			}
+		}
 
-	// Extract comments from all source files in the directory
-	err := extractComments(inputDir)
-	if err != nil {
-		panic(err)
-	}
+		// Extract comment
+		comment := ""
+		if field.Doc != nil {
+			comment = strings.TrimSpace(field.Doc.Text())
+		} else if field.Comment != nil {
+			comment = strings.TrimSpace(field.Comment.Text())
+		}
 
-	// Generate constants file
-	constantsFile, err := os.Create(filepath.Join(inputDir, "generated_constants.go"))
-	if err != nil {
-		panic(err)
-	}
-	defer constantsFile.Close()
+		// Extract YAML tag
+		tag := ""
+		if field.Tag != nil {
+			tagValue := strings.Trim(field.Tag.Value, "`")
+			structTag := reflect.StructTag(tagValue)
+			yamlTag := structTag.Get("yaml")
+			if yamlTag != "" {
+				tag = strings.Split(yamlTag, ",")[0]
+			}
+		}
 
-	// Write the package declaration
-	fmt.Fprintf(constantsFile, "// CODE GENERATED BY pkg/config/types/gen_paths DO NOT EDIT\n\n")
-	fmt.Fprintf(constantsFile, "package types\n\n")
+		for idx, name := range fieldNames {
+			// Use YAML tag or field name
+			tagOrName := tag
+			if tagOrName == "" {
+				tagOrName = name
+			}
 
-	generateConstants(reflect.TypeOf(types.Bacalhau{}), "", constantsFile)
+			// Original capitalization for the constant name
+			origName := fieldNamesOriginal[idx]
+			if tag != "" {
+				origName = tag
+			}
 
-	// Generate descriptions file
-	descriptionsFile, err := os.Create(filepath.Join(inputDir, "generated_descriptions.go"))
-	if err != nil {
-		panic(err)
-	}
-	defer descriptionsFile.Close()
+			// Build field path
+			var fieldPath string
+			var capFieldPath string
+			if prefix != "" {
+				fieldPath = prefix + "." + tagOrName
+				capFieldPath = capPrefix + "." + origName
+			} else {
+				fieldPath = tagOrName
+				capFieldPath = origName
+			}
 
-	// Write the package declaration
-	fmt.Fprintf(descriptionsFile, "// Code generated by go generate; DO NOT EDIT.\n\n")
-	fmt.Fprintf(descriptionsFile, "package types\n\n")
+			// Determine if the field is a leaf (non-struct) field
+			isLeaf := true
+			switch ft := field.Type.(type) {
+			case *ast.Ident:
+				if _, ok := typeMap[ft.Name]; ok {
+					// Field is a named struct type
+					isLeaf = false
+				}
+			case *ast.StarExpr:
+				if ident, ok := ft.X.(*ast.Ident); ok {
+					if _, ok := typeMap[ident.Name]; ok {
+						// Field is a pointer to a named struct type
+						isLeaf = false
+					}
+				}
+			case *ast.StructType:
+				// Field is an anonymous struct
+				isLeaf = false
+			}
 
-	fmt.Fprintf(descriptionsFile, "// ConfigDescriptions maps configuration paths to their descriptions\n")
-	fmt.Fprintf(descriptionsFile, "var ConfigDescriptions = map[string]string{\n")
+			// If it's a leaf field, store the comment and capitalized path
+			if isLeaf {
+				fieldInfos[fieldPath] = FieldInfo{
+					Comment:         comment,
+					CapitalizedPath: capFieldPath,
+				}
+			} else {
+				// Recursively process nested structs
+				switch ft := field.Type.(type) {
+				case *ast.Ident:
+					if nestedStruct, ok := typeMap[ft.Name]; ok {
+						processStruct(fieldPath, capFieldPath, nestedStruct, fieldInfos, typeMap)
+					}
+				case *ast.StarExpr:
+					if ident, ok := ft.X.(*ast.Ident); ok {
+						if nestedStruct, ok := typeMap[ident.Name]; ok {
+							processStruct(fieldPath, capFieldPath, nestedStruct, fieldInfos, typeMap)
+						}
+					}
+				case *ast.StructType:
+					processStruct(fieldPath, capFieldPath, ft, fieldInfos, typeMap)
+				}
+			}
+		}
+	}
+}
 
-	generateDescriptions(reflect.TypeOf(types.Bacalhau{}), "", descriptionsFile)
+// generateConstantName converts a field path to a constant name
+func generateConstantName(fieldPath string) string {
+	// Split the field path by "."
+	parts := strings.Split(fieldPath, ".")
+	// Capitalize each part
+	for i, part := range parts {
+		parts[i] = capitalize(part)
+	}
+	// Join the parts and append "Key"
+	constName := strings.Join(parts, "") + "Key"
+	return constName
+}
 
-	fmt.Fprintf(descriptionsFile, "}\n")
+// capitalize capitalizes the first letter of a string
+func capitalize(s string) string {
+	if s == "" {
+		return s
+	}
+	return strings.ToUpper(s[:1]) + s[1:]
+}
 
-	fmt.Printf("Generated files written to %s\n", inputDir)
+// escapeString escapes double quotes in the string for safe inclusion in Go source code.
+func escapeString(s string) string {
+	return strings.ReplaceAll(s, `"`, `\"`)
 }
diff --git a/pkg/config/types/generated_constants.go b/pkg/config/types/generated_constants.go
index b3b189c3f2..c805277289 100644
--- a/pkg/config/types/generated_constants.go
+++ b/pkg/config/types/generated_constants.go
@@ -1,108 +1,108 @@
-// CODE GENERATED BY pkg/config/types/gen_paths DO NOT EDIT
+// CODE GENERATED BY pkg/config/types/gen/generate.go DO NOT EDIT
 
 package types
 
-const APIHostKey = "api.host"
-const APIPortKey = "api.port"
-const APITLSCertFileKey = "api.tls.certfile"
-const APITLSKeyFileKey = "api.tls.keyfile"
-const APITLSCAFileKey = "api.tls.cafile"
-const APITLSUseTLSKey = "api.tls.usetls"
-const APITLSInsecureKey = "api.tls.insecure"
-const APITLSSelfSignedKey = "api.tls.selfsigned"
-const APITLSAutoCertKey = "api.tls.autocert"
-const APITLSAutoCertCachePathKey = "api.tls.autocertcachepath"
-const APIAuthMethodsKey = "api.auth.methods"
-const APIAuthAccessPolicyPathKey = "api.auth.accesspolicypath"
-const NameProviderKey = "nameprovider"
-const DataDirKey = "datadir"
-const StrictVersionMatchKey = "strictversionmatch"
-const OrchestratorEnabledKey = "orchestrator.enabled"
-const OrchestratorHostKey = "orchestrator.host"
-const OrchestratorPortKey = "orchestrator.port"
-const OrchestratorAdvertiseKey = "orchestrator.advertise"
-const OrchestratorAuthTokenKey = "orchestrator.auth.token"
-const OrchestratorClusterNameKey = "orchestrator.cluster.name"
-const OrchestratorClusterHostKey = "orchestrator.cluster.host"
-const OrchestratorClusterPortKey = "orchestrator.cluster.port"
-const OrchestratorClusterAdvertiseKey = "orchestrator.cluster.advertise"
-const OrchestratorClusterPeersKey = "orchestrator.cluster.peers"
-const OrchestratorNodeManagerDisconnectTimeoutKey = "orchestrator.nodemanager.disconnecttimeout"
-const OrchestratorNodeManagerManualApprovalKey = "orchestrator.nodemanager.manualapproval"
-const OrchestratorSchedulerWorkerCountKey = "orchestrator.scheduler.workercount"
-const OrchestratorSchedulerQueueBackoffKey = "orchestrator.scheduler.queuebackoff"
-const OrchestratorSchedulerHousekeepingIntervalKey = "orchestrator.scheduler.housekeepinginterval"
-const OrchestratorSchedulerHousekeepingTimeoutKey = "orchestrator.scheduler.housekeepingtimeout"
-const OrchestratorEvaluationBrokerVisibilityTimeoutKey = "orchestrator.evaluationbroker.visibilitytimeout"
-const OrchestratorEvaluationBrokerMaxRetryCountKey = "orchestrator.evaluationbroker.maxretrycount"
-const ComputeEnabledKey = "compute.enabled"
-const ComputeOrchestratorsKey = "compute.orchestrators"
-const ComputeAuthTokenKey = "compute.auth.token"
-const ComputeHeartbeatInfoUpdateIntervalKey = "compute.heartbeat.infoupdateinterval"
-const ComputeHeartbeatResourceUpdateIntervalKey = "compute.heartbeat.resourceupdateinterval"
-const ComputeHeartbeatIntervalKey = "compute.heartbeat.interval"
-const ComputeAllocatedCapacityCPUKey = "compute.allocatedcapacity.cpu"
-const ComputeAllocatedCapacityMemoryKey = "compute.allocatedcapacity.memory"
-const ComputeAllocatedCapacityDiskKey = "compute.allocatedcapacity.disk"
-const ComputeAllocatedCapacityGPUKey = "compute.allocatedcapacity.gpu"
-const ComputeAllowListedLocalPathsKey = "compute.allowlistedlocalpaths"
-const LabelsKey = "labels"
-const WebUIEnabledKey = "webui.enabled"
-const WebUIListenKey = "webui.listen"
-const WebUIBackendKey = "webui.backend"
-const InputSourcesDisabledKey = "inputsources.disabled"
-const InputSourcesReadTimeoutKey = "inputsources.readtimeout"
-const InputSourcesMaxRetryCountKey = "inputsources.maxretrycount"
-const InputSourcesTypesIPFSEndpointKey = "inputsources.types.ipfs.endpoint"
-const PublishersDisabledKey = "publishers.disabled"
-const PublishersTypesIPFSEndpointKey = "publishers.types.ipfs.endpoint"
-const PublishersTypesS3PreSignedURLDisabledKey = "publishers.types.s3.presignedurldisabled"
-const PublishersTypesS3PreSignedURLExpirationKey = "publishers.types.s3.presignedurlexpiration"
-const PublishersTypesLocalAddressKey = "publishers.types.local.address"
-const PublishersTypesLocalPortKey = "publishers.types.local.port"
-const EnginesDisabledKey = "engines.disabled"
-const EnginesTypesDockerManifestCacheSizeKey = "engines.types.docker.manifestcache.size"
-const EnginesTypesDockerManifestCacheTTLKey = "engines.types.docker.manifestcache.ttl"
-const EnginesTypesDockerManifestCacheRefreshKey = "engines.types.docker.manifestcache.refresh"
-const ResultDownloadersDisabledKey = "resultdownloaders.disabled"
-const ResultDownloadersTimeoutKey = "resultdownloaders.timeout"
-const ResultDownloadersTypesIPFSEndpointKey = "resultdownloaders.types.ipfs.endpoint"
-const JobDefaultsBatchPriorityKey = "jobdefaults.batch.priority"
-const JobDefaultsBatchTaskResourcesCPUKey = "jobdefaults.batch.task.resources.cpu"
-const JobDefaultsBatchTaskResourcesMemoryKey = "jobdefaults.batch.task.resources.memory"
-const JobDefaultsBatchTaskResourcesDiskKey = "jobdefaults.batch.task.resources.disk"
-const JobDefaultsBatchTaskResourcesGPUKey = "jobdefaults.batch.task.resources.gpu"
-const JobDefaultsBatchTaskPublisherTypeKey = "jobdefaults.batch.task.publisher.type"
-const JobDefaultsBatchTaskPublisherParamsKey = "jobdefaults.batch.task.publisher.params"
-const JobDefaultsBatchTaskTimeoutsTotalTimeoutKey = "jobdefaults.batch.task.timeouts.totaltimeout"
-const JobDefaultsBatchTaskTimeoutsExecutionTimeoutKey = "jobdefaults.batch.task.timeouts.executiontimeout"
-const JobDefaultsOpsPriorityKey = "jobdefaults.ops.priority"
-const JobDefaultsOpsTaskResourcesCPUKey = "jobdefaults.ops.task.resources.cpu"
-const JobDefaultsOpsTaskResourcesMemoryKey = "jobdefaults.ops.task.resources.memory"
-const JobDefaultsOpsTaskResourcesDiskKey = "jobdefaults.ops.task.resources.disk"
-const JobDefaultsOpsTaskResourcesGPUKey = "jobdefaults.ops.task.resources.gpu"
-const JobDefaultsOpsTaskPublisherTypeKey = "jobdefaults.ops.task.publisher.type"
-const JobDefaultsOpsTaskPublisherParamsKey = "jobdefaults.ops.task.publisher.params"
-const JobDefaultsOpsTaskTimeoutsTotalTimeoutKey = "jobdefaults.ops.task.timeouts.totaltimeout"
-const JobDefaultsOpsTaskTimeoutsExecutionTimeoutKey = "jobdefaults.ops.task.timeouts.executiontimeout"
-const JobDefaultsDaemonPriorityKey = "jobdefaults.daemon.priority"
-const JobDefaultsDaemonTaskResourcesCPUKey = "jobdefaults.daemon.task.resources.cpu"
-const JobDefaultsDaemonTaskResourcesMemoryKey = "jobdefaults.daemon.task.resources.memory"
-const JobDefaultsDaemonTaskResourcesDiskKey = "jobdefaults.daemon.task.resources.disk"
-const JobDefaultsDaemonTaskResourcesGPUKey = "jobdefaults.daemon.task.resources.gpu"
-const JobDefaultsServicePriorityKey = "jobdefaults.service.priority"
-const JobDefaultsServiceTaskResourcesCPUKey = "jobdefaults.service.task.resources.cpu"
-const JobDefaultsServiceTaskResourcesMemoryKey = "jobdefaults.service.task.resources.memory"
-const JobDefaultsServiceTaskResourcesDiskKey = "jobdefaults.service.task.resources.disk"
-const JobDefaultsServiceTaskResourcesGPUKey = "jobdefaults.service.task.resources.gpu"
-const JobAdmissionControlLocalityKey = "jobadmissioncontrol.locality"
-const JobAdmissionControlRejectStatelessJobsKey = "jobadmissioncontrol.rejectstatelessjobs"
-const JobAdmissionControlAcceptNetworkedJobsKey = "jobadmissioncontrol.acceptnetworkedjobs"
-const JobAdmissionControlProbeHTTPKey = "jobadmissioncontrol.probehttp"
-const JobAdmissionControlProbeExecKey = "jobadmissioncontrol.probeexec"
-const LoggingLevelKey = "logging.level"
-const LoggingModeKey = "logging.mode"
-const LoggingLogDebugInfoIntervalKey = "logging.logdebuginfointerval"
-const UpdateConfigIntervalKey = "updateconfig.interval"
-const FeatureFlagsExecTranslationKey = "featureflags.exectranslation"
-const DisableAnalyticsKey = "disableanalytics"
+const APIAuthAccessPolicyPathKey = "API.Auth.AccessPolicyPath"
+const APIAuthMethodsKey = "API.Auth.Methods"
+const APIHostKey = "API.Host"
+const APIPortKey = "API.Port"
+const APITLSAutoCertKey = "API.TLS.AutoCert"
+const APITLSAutoCertCachePathKey = "API.TLS.AutoCertCachePath"
+const APITLSCAFileKey = "API.TLS.CAFile"
+const APITLSCertFileKey = "API.TLS.CertFile"
+const APITLSInsecureKey = "API.TLS.Insecure"
+const APITLSKeyFileKey = "API.TLS.KeyFile"
+const APITLSSelfSignedKey = "API.TLS.SelfSigned"
+const APITLSUseTLSKey = "API.TLS.UseTLS"
+const ComputeAllocatedCapacityCPUKey = "Compute.AllocatedCapacity.CPU"
+const ComputeAllocatedCapacityDiskKey = "Compute.AllocatedCapacity.Disk"
+const ComputeAllocatedCapacityGPUKey = "Compute.AllocatedCapacity.GPU"
+const ComputeAllocatedCapacityMemoryKey = "Compute.AllocatedCapacity.Memory"
+const ComputeAllowListedLocalPathsKey = "Compute.AllowListedLocalPaths"
+const ComputeAuthTokenKey = "Compute.Auth.Token"
+const ComputeEnabledKey = "Compute.Enabled"
+const ComputeHeartbeatInfoUpdateIntervalKey = "Compute.Heartbeat.InfoUpdateInterval"
+const ComputeHeartbeatIntervalKey = "Compute.Heartbeat.Interval"
+const ComputeHeartbeatResourceUpdateIntervalKey = "Compute.Heartbeat.ResourceUpdateInterval"
+const ComputeOrchestratorsKey = "Compute.Orchestrators"
+const DataDirKey = "DataDir"
+const DisableAnalyticsKey = "DisableAnalytics"
+const EnginesDisabledKey = "Engines.Disabled"
+const EnginesTypesDockerManifestCacheRefreshKey = "Engines.Types.Docker.ManifestCache.Refresh"
+const EnginesTypesDockerManifestCacheSizeKey = "Engines.Types.Docker.ManifestCache.Size"
+const EnginesTypesDockerManifestCacheTTLKey = "Engines.Types.Docker.ManifestCache.TTL"
+const FeatureFlagsExecTranslationKey = "FeatureFlags.ExecTranslation"
+const InputSourcesDisabledKey = "InputSources.Disabled"
+const InputSourcesMaxRetryCountKey = "InputSources.MaxRetryCount"
+const InputSourcesReadTimeoutKey = "InputSources.ReadTimeout"
+const InputSourcesTypesIPFSEndpointKey = "InputSources.Types.IPFS.Endpoint"
+const JobAdmissionControlAcceptNetworkedJobsKey = "JobAdmissionControl.AcceptNetworkedJobs"
+const JobAdmissionControlLocalityKey = "JobAdmissionControl.Locality"
+const JobAdmissionControlProbeExecKey = "JobAdmissionControl.ProbeExec"
+const JobAdmissionControlProbeHTTPKey = "JobAdmissionControl.ProbeHTTP"
+const JobAdmissionControlRejectStatelessJobsKey = "JobAdmissionControl.RejectStatelessJobs"
+const JobDefaultsBatchPriorityKey = "JobDefaults.Batch.Priority"
+const JobDefaultsBatchTaskPublisherParamsKey = "JobDefaults.Batch.Task.Publisher.Params"
+const JobDefaultsBatchTaskPublisherTypeKey = "JobDefaults.Batch.Task.Publisher.Type"
+const JobDefaultsBatchTaskResourcesCPUKey = "JobDefaults.Batch.Task.Resources.CPU"
+const JobDefaultsBatchTaskResourcesDiskKey = "JobDefaults.Batch.Task.Resources.Disk"
+const JobDefaultsBatchTaskResourcesGPUKey = "JobDefaults.Batch.Task.Resources.GPU"
+const JobDefaultsBatchTaskResourcesMemoryKey = "JobDefaults.Batch.Task.Resources.Memory"
+const JobDefaultsBatchTaskTimeoutsExecutionTimeoutKey = "JobDefaults.Batch.Task.Timeouts.ExecutionTimeout"
+const JobDefaultsBatchTaskTimeoutsTotalTimeoutKey = "JobDefaults.Batch.Task.Timeouts.TotalTimeout"
+const JobDefaultsDaemonPriorityKey = "JobDefaults.Daemon.Priority"
+const JobDefaultsDaemonTaskResourcesCPUKey = "JobDefaults.Daemon.Task.Resources.CPU"
+const JobDefaultsDaemonTaskResourcesDiskKey = "JobDefaults.Daemon.Task.Resources.Disk"
+const JobDefaultsDaemonTaskResourcesGPUKey = "JobDefaults.Daemon.Task.Resources.GPU"
+const JobDefaultsDaemonTaskResourcesMemoryKey = "JobDefaults.Daemon.Task.Resources.Memory"
+const JobDefaultsOpsPriorityKey = "JobDefaults.Ops.Priority"
+const JobDefaultsOpsTaskPublisherParamsKey = "JobDefaults.Ops.Task.Publisher.Params"
+const JobDefaultsOpsTaskPublisherTypeKey = "JobDefaults.Ops.Task.Publisher.Type"
+const JobDefaultsOpsTaskResourcesCPUKey = "JobDefaults.Ops.Task.Resources.CPU"
+const JobDefaultsOpsTaskResourcesDiskKey = "JobDefaults.Ops.Task.Resources.Disk"
+const JobDefaultsOpsTaskResourcesGPUKey = "JobDefaults.Ops.Task.Resources.GPU"
+const JobDefaultsOpsTaskResourcesMemoryKey = "JobDefaults.Ops.Task.Resources.Memory"
+const JobDefaultsOpsTaskTimeoutsExecutionTimeoutKey = "JobDefaults.Ops.Task.Timeouts.ExecutionTimeout"
+const JobDefaultsOpsTaskTimeoutsTotalTimeoutKey = "JobDefaults.Ops.Task.Timeouts.TotalTimeout"
+const JobDefaultsServicePriorityKey = "JobDefaults.Service.Priority"
+const JobDefaultsServiceTaskResourcesCPUKey = "JobDefaults.Service.Task.Resources.CPU"
+const JobDefaultsServiceTaskResourcesDiskKey = "JobDefaults.Service.Task.Resources.Disk"
+const JobDefaultsServiceTaskResourcesGPUKey = "JobDefaults.Service.Task.Resources.GPU"
+const JobDefaultsServiceTaskResourcesMemoryKey = "JobDefaults.Service.Task.Resources.Memory"
+const LabelsKey = "Labels"
+const LoggingLevelKey = "Logging.Level"
+const LoggingLogDebugInfoIntervalKey = "Logging.LogDebugInfoInterval"
+const LoggingModeKey = "Logging.Mode"
+const NameProviderKey = "NameProvider"
+const OrchestratorAdvertiseKey = "Orchestrator.Advertise"
+const OrchestratorAuthTokenKey = "Orchestrator.Auth.Token"
+const OrchestratorClusterAdvertiseKey = "Orchestrator.Cluster.Advertise"
+const OrchestratorClusterHostKey = "Orchestrator.Cluster.Host"
+const OrchestratorClusterNameKey = "Orchestrator.Cluster.Name"
+const OrchestratorClusterPeersKey = "Orchestrator.Cluster.Peers"
+const OrchestratorClusterPortKey = "Orchestrator.Cluster.Port"
+const OrchestratorEnabledKey = "Orchestrator.Enabled"
+const OrchestratorEvaluationBrokerMaxRetryCountKey = "Orchestrator.EvaluationBroker.MaxRetryCount"
+const OrchestratorEvaluationBrokerVisibilityTimeoutKey = "Orchestrator.EvaluationBroker.VisibilityTimeout"
+const OrchestratorHostKey = "Orchestrator.Host"
+const OrchestratorNodeManagerDisconnectTimeoutKey = "Orchestrator.NodeManager.DisconnectTimeout"
+const OrchestratorNodeManagerManualApprovalKey = "Orchestrator.NodeManager.ManualApproval"
+const OrchestratorPortKey = "Orchestrator.Port"
+const OrchestratorSchedulerHousekeepingIntervalKey = "Orchestrator.Scheduler.HousekeepingInterval"
+const OrchestratorSchedulerHousekeepingTimeoutKey = "Orchestrator.Scheduler.HousekeepingTimeout"
+const OrchestratorSchedulerQueueBackoffKey = "Orchestrator.Scheduler.QueueBackoff"
+const OrchestratorSchedulerWorkerCountKey = "Orchestrator.Scheduler.WorkerCount"
+const PublishersDisabledKey = "Publishers.Disabled"
+const PublishersTypesIPFSEndpointKey = "Publishers.Types.IPFS.Endpoint"
+const PublishersTypesLocalAddressKey = "Publishers.Types.Local.Address"
+const PublishersTypesLocalPortKey = "Publishers.Types.Local.Port"
+const PublishersTypesS3PreSignedURLDisabledKey = "Publishers.Types.S3.PreSignedURLDisabled"
+const PublishersTypesS3PreSignedURLExpirationKey = "Publishers.Types.S3.PreSignedURLExpiration"
+const ResultDownloadersDisabledKey = "ResultDownloaders.Disabled"
+const ResultDownloadersTimeoutKey = "ResultDownloaders.Timeout"
+const ResultDownloadersTypesIPFSEndpointKey = "ResultDownloaders.Types.IPFS.Endpoint"
+const StrictVersionMatchKey = "StrictVersionMatch"
+const UpdateConfigIntervalKey = "UpdateConfig.Interval"
+const WebUIBackendKey = "WebUI.Backend"
+const WebUIEnabledKey = "WebUI.Enabled"
+const WebUIListenKey = "WebUI.Listen"
diff --git a/pkg/config/types/generated_descriptions.go b/pkg/config/types/generated_descriptions.go
index 219c891843..897bb42dc2 100644
--- a/pkg/config/types/generated_descriptions.go
+++ b/pkg/config/types/generated_descriptions.go
@@ -1,111 +1,111 @@
-// Code generated by go generate; DO NOT EDIT.
+// CODE GENERATED BY pkg/config/types/gen/generate.go DO NOT EDIT
 
 package types
 
 // ConfigDescriptions maps configuration paths to their descriptions
 var ConfigDescriptions = map[string]string{
+	APIAuthAccessPolicyPathKey:                       "AccessPolicyPath is the path to a file or directory that will be loaded as the policy to apply to all inbound API requests. If unspecified, a policy that permits access to all API endpoints to both authenticated and unauthenticated users (the default as of v1.2.0) will be used.",
+	APIAuthMethodsKey:                                "Methods maps \"method names\" to authenticator implementations. A method name is a human-readable string chosen by the person configuring the system that is shown to users to help them pick the authentication method they want to use. There can be multiple usages of the same Authenticator *type* but with different configs and parameters, each identified with a unique method name.  For example, if an implementation wants to allow users to log in with Github or Bitbucket, they might both use an authenticator implementation of type \"oidc\", and each would appear once on this provider with key / method name \"github\" and \"bitbucket\".  By default, only a single authentication method that accepts authentication via client keys will be enabled.",
 	APIHostKey:                                       "Host specifies the hostname or IP address on which the API server listens or the client connects.",
-	APIPortKey:                                       "Port specifies the port the publisher serves on.",
-	APITLSCertFileKey:                                "CertFile specifies the path to the TLS certificate file.",
-	APITLSKeyFileKey:                                 "KeyFile specifies the path to the TLS private key file.",
+	APIPortKey:                                       "Port specifies the port number on which the API server listens or the client connects.",
+	APITLSAutoCertKey:                                "AutoCert specifies the domain for automatic certificate generation.",
+	APITLSAutoCertCachePathKey:                       "AutoCertCachePath specifies the directory to cache auto-generated certificates.",
 	APITLSCAFileKey:                                  "CAFile specifies the path to the Certificate Authority file.",
-	APITLSUseTLSKey:                                  "UseTLS indicates whether to use TLS for client connections.",
+	APITLSCertFileKey:                                "CertFile specifies the path to the TLS certificate file.",
 	APITLSInsecureKey:                                "Insecure allows insecure TLS connections (e.g., self-signed certificates).",
+	APITLSKeyFileKey:                                 "KeyFile specifies the path to the TLS private key file.",
 	APITLSSelfSignedKey:                              "SelfSigned indicates whether to use a self-signed certificate.",
-	APITLSAutoCertKey:                                "AutoCert specifies the domain for automatic certificate generation.",
-	APITLSAutoCertCachePathKey:                       "AutoCertCachePath specifies the directory to cache auto-generated certificates.",
-	APIAuthMethodsKey:                                "Methods maps \"method names\" to authenticator implementations. A method name is a human-readable string chosen by the person configuring the system that is shown to users to help them pick the authentication method they want to use. There can be multiple usages of the same Authenticator *type* but with different configs and parameters, each identified with a unique method name.  For example, if an implementation wants to allow users to log in with Github or Bitbucket, they might both use an authenticator implementation of type \"oidc\", and each would appear once on this provider with key / method name \"github\" and \"bitbucket\".  By default, only a single authentication method that accepts authentication via client keys will be enabled.",
-	APIAuthAccessPolicyPathKey:                       "AccessPolicyPath is the path to a file or directory that will be loaded as the policy to apply to all inbound API requests. If unspecified, a policy that permits access to all API endpoints to both authenticated and unauthenticated users (the default as of v1.2.0) will be used.",
-	NameProviderKey:                                  "NameProvider specifies the method used to generate names for the node. One of: hostname, aws, gcp, uuid, puuid.",
-	DataDirKey:                                       "DataDir specifies a location on disk where the bacalhau node will maintain state.",
-	StrictVersionMatchKey:                            "StrictVersionMatch indicates whether to enforce strict version matching.",
-	OrchestratorEnabledKey:                           "Enabled indicates whether the compute node is active and available for job execution.",
-	OrchestratorHostKey:                              "Host specifies the hostname or IP address on which the API server listens or the client connects.",
-	OrchestratorPortKey:                              "Port specifies the port the publisher serves on.",
-	OrchestratorAdvertiseKey:                         "Advertise specifies the address to advertise to other cluster members.",
-	OrchestratorAuthTokenKey:                         "Token specifies the key for compute nodes to be able to access the orchestrator.",
-	OrchestratorClusterNameKey:                       "Name specifies the unique identifier for this orchestrator cluster.",
-	OrchestratorClusterHostKey:                       "Host specifies the hostname or IP address on which the API server listens or the client connects.",
-	OrchestratorClusterPortKey:                       "Port specifies the port the publisher serves on.",
-	OrchestratorClusterAdvertiseKey:                  "Advertise specifies the address to advertise to other cluster members.",
-	OrchestratorClusterPeersKey:                      "Peers is a list of other cluster members to connect to on startup.",
-	OrchestratorNodeManagerDisconnectTimeoutKey:      "DisconnectTimeout specifies how long to wait before considering a node disconnected.",
-	OrchestratorNodeManagerManualApprovalKey:         "ManualApproval, if true, requires manual approval for new compute nodes joining the cluster.",
-	OrchestratorSchedulerWorkerCountKey:              "WorkerCount specifies the number of concurrent workers for job scheduling.",
-	OrchestratorSchedulerQueueBackoffKey:             "QueueBackoff specifies the time to wait before retrying a failed job.",
-	OrchestratorSchedulerHousekeepingIntervalKey:     "HousekeepingInterval specifies how often to run housekeeping tasks.",
-	OrchestratorSchedulerHousekeepingTimeoutKey:      "HousekeepingTimeout specifies the maximum time allowed for a single housekeeping run.",
-	OrchestratorEvaluationBrokerVisibilityTimeoutKey: "VisibilityTimeout specifies how long an evaluation can be claimed before it's returned to the queue.",
-	OrchestratorEvaluationBrokerMaxRetryCountKey:     "MaxRetryCount specifies the maximum number of times an evaluation can be retried before being marked as failed.",
-	ComputeEnabledKey:                                "Enabled indicates whether the compute node is active and available for job execution.",
-	ComputeOrchestratorsKey:                          "Orchestrators specifies a list of orchestrator endpoints that this compute node connects to.",
+	APITLSUseTLSKey:                                  "UseTLS indicates whether to use TLS for client connections.",
+	ComputeAllocatedCapacityCPUKey:                   "CPU specifies the amount of CPU a compute node allocates for running jobs. It can be expressed as a percentage (e.g., \"85%\") or a Kubernetes resource string (e.g., \"100m\").",
+	ComputeAllocatedCapacityDiskKey:                  "Disk specifies the amount of Disk space a compute node allocates for running jobs. It can be expressed as a percentage (e.g., \"85%\") or a Kubernetes resource string (e.g., \"10Gi\").",
+	ComputeAllocatedCapacityGPUKey:                   "GPU specifies the amount of GPU a compute node allocates for running jobs. It can be expressed as a percentage (e.g., \"85%\") or a Kubernetes resource string (e.g., \"1\"). Note: When using percentages, the result is always rounded up to the nearest whole GPU.",
+	ComputeAllocatedCapacityMemoryKey:                "Memory specifies the amount of Memory a compute node allocates for running jobs. It can be expressed as a percentage (e.g., \"85%\") or a Kubernetes resource string (e.g., \"1Gi\").",
+	ComputeAllowListedLocalPathsKey:                  "AllowListedLocalPaths specifies a list of local file system paths that the compute node is allowed to access.",
 	ComputeAuthTokenKey:                              "Token specifies the key for compute nodes to be able to access the orchestrator.",
+	ComputeEnabledKey:                                "Enabled indicates whether the compute node is active and available for job execution.",
 	ComputeHeartbeatInfoUpdateIntervalKey:            "InfoUpdateInterval specifies the time between updates of non-resource information to the orchestrator.",
-	ComputeHeartbeatResourceUpdateIntervalKey:        "ResourceUpdateInterval specifies the time between updates of resource information to the orchestrator.",
 	ComputeHeartbeatIntervalKey:                      "Interval specifies the time between heartbeat signals sent to the orchestrator.",
-	ComputeAllocatedCapacityCPUKey:                   "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
-	ComputeAllocatedCapacityMemoryKey:                "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
-	ComputeAllocatedCapacityDiskKey:                  "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
-	ComputeAllocatedCapacityGPUKey:                   "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
-	ComputeAllowListedLocalPathsKey:                  "AllowListedLocalPaths specifies a list of local file system paths that the compute node is allowed to access.",
-	LabelsKey:                                        "Labels are key-value pairs used to describe and categorize the nodes.",
-	WebUIEnabledKey:                                  "Enabled indicates whether the compute node is active and available for job execution.",
-	WebUIListenKey:                                   "Listen specifies the address and port on which the Web UI listens.",
-	WebUIBackendKey:                                  "Backend specifies the address and port of the backend API server. If empty, the Web UI will use the same address and port as the API server.",
-	InputSourcesDisabledKey:                          "Disabled specifies a list of publishers that are disabled.",
-	InputSourcesReadTimeoutKey:                       "ReadTimeout specifies the maximum time allowed for reading from a storage.",
-	InputSourcesMaxRetryCountKey:                     "MaxRetryCount specifies the maximum number of times an evaluation can be retried before being marked as failed.",
-	InputSourcesTypesIPFSEndpointKey:                 "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
-	PublishersDisabledKey:                            "Disabled specifies a list of publishers that are disabled.",
-	PublishersTypesIPFSEndpointKey:                   "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
-	PublishersTypesS3PreSignedURLDisabledKey:         "PreSignedURLDisabled specifies whether pre-signed URLs are enabled for the S3 provider.",
-	PublishersTypesS3PreSignedURLExpirationKey:       "PreSignedURLExpiration specifies the duration before a pre-signed URL expires.",
-	PublishersTypesLocalAddressKey:                   "Address specifies the endpoint the publisher serves on.",
-	PublishersTypesLocalPortKey:                      "Port specifies the port the publisher serves on.",
-	EnginesDisabledKey:                               "Disabled specifies a list of publishers that are disabled.",
+	ComputeHeartbeatResourceUpdateIntervalKey:        "ResourceUpdateInterval specifies the time between updates of resource information to the orchestrator.",
+	ComputeOrchestratorsKey:                          "Orchestrators specifies a list of orchestrator endpoints that this compute node connects to.",
+	DataDirKey:                                       "DataDir specifies a location on disk where the bacalhau node will maintain state.",
+	DisableAnalyticsKey:                              "No description available",
+	EnginesDisabledKey:                               "Disabled specifies a list of engines that are disabled.",
+	EnginesTypesDockerManifestCacheRefreshKey:        "Refresh specifies the refresh interval for cache entries.",
 	EnginesTypesDockerManifestCacheSizeKey:           "Size specifies the size of the Docker manifest cache.",
 	EnginesTypesDockerManifestCacheTTLKey:            "TTL specifies the time-to-live duration for cache entries.",
-	EnginesTypesDockerManifestCacheRefreshKey:        "Refresh specifies the refresh interval for cache entries.",
-	ResultDownloadersDisabledKey:                     "Disabled specifies a list of publishers that are disabled.",
-	ResultDownloadersTimeoutKey:                      "Timeout specifies the maximum time allowed for a download operation.",
-	ResultDownloadersTypesIPFSEndpointKey:            "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
-	JobDefaultsBatchPriorityKey:                      "Priority specifies the default priority allocated to a service or daemon job. This value is used when the job hasn't explicitly set its priority requirement.",
+	FeatureFlagsExecTranslationKey:                   "ExecTranslation enables the execution translation feature.",
+	InputSourcesDisabledKey:                          "Disabled specifies a list of storages that are disabled.",
+	InputSourcesMaxRetryCountKey:                     "ReadTimeout specifies the maximum number of attempts for reading from a storage.",
+	InputSourcesReadTimeoutKey:                       "ReadTimeout specifies the maximum time allowed for reading from a storage.",
+	InputSourcesTypesIPFSEndpointKey:                 "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
+	JobAdmissionControlAcceptNetworkedJobsKey:        "AcceptNetworkedJobs indicates whether to accept jobs that require network access.",
+	JobAdmissionControlLocalityKey:                   "Locality specifies the locality of the job input data.",
+	JobAdmissionControlProbeExecKey:                  "ProbeExec specifies the command to execute for probing job submission.",
+	JobAdmissionControlProbeHTTPKey:                  "ProbeHTTP specifies the HTTP endpoint for probing job submission.",
+	JobAdmissionControlRejectStatelessJobsKey:        "RejectStatelessJobs indicates whether to reject stateless jobs, i.e. jobs without inputs.",
+	JobDefaultsBatchPriorityKey:                      "Priority specifies the default priority allocated to a batch or ops job. This value is used when the job hasn't explicitly set its priority requirement.",
+	JobDefaultsBatchTaskPublisherParamsKey:           "Params specifies the publisher configuration data.",
+	JobDefaultsBatchTaskPublisherTypeKey:             "Type specifies the publisher type. e.g. \"s3\", \"local\", \"ipfs\", etc.",
 	JobDefaultsBatchTaskResourcesCPUKey:              "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
-	JobDefaultsBatchTaskResourcesMemoryKey:           "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
 	JobDefaultsBatchTaskResourcesDiskKey:             "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
 	JobDefaultsBatchTaskResourcesGPUKey:              "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
-	JobDefaultsBatchTaskPublisherTypeKey:             "No description available",
-	JobDefaultsBatchTaskPublisherParamsKey:           "No description available",
-	JobDefaultsBatchTaskTimeoutsTotalTimeoutKey:      "TotalTimeout is the maximum total time allowed for a task",
+	JobDefaultsBatchTaskResourcesMemoryKey:           "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
 	JobDefaultsBatchTaskTimeoutsExecutionTimeoutKey:  "ExecutionTimeout is the maximum time allowed for task execution",
-	JobDefaultsOpsPriorityKey:                        "Priority specifies the default priority allocated to a service or daemon job. This value is used when the job hasn't explicitly set its priority requirement.",
-	JobDefaultsOpsTaskResourcesCPUKey:                "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
-	JobDefaultsOpsTaskResourcesMemoryKey:             "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
-	JobDefaultsOpsTaskResourcesDiskKey:               "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
-	JobDefaultsOpsTaskResourcesGPUKey:                "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
-	JobDefaultsOpsTaskPublisherTypeKey:               "No description available",
-	JobDefaultsOpsTaskPublisherParamsKey:             "No description available",
-	JobDefaultsOpsTaskTimeoutsTotalTimeoutKey:        "TotalTimeout is the maximum total time allowed for a task",
-	JobDefaultsOpsTaskTimeoutsExecutionTimeoutKey:    "ExecutionTimeout is the maximum time allowed for task execution",
+	JobDefaultsBatchTaskTimeoutsTotalTimeoutKey:      "TotalTimeout is the maximum total time allowed for a task",
 	JobDefaultsDaemonPriorityKey:                     "Priority specifies the default priority allocated to a service or daemon job. This value is used when the job hasn't explicitly set its priority requirement.",
 	JobDefaultsDaemonTaskResourcesCPUKey:             "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
-	JobDefaultsDaemonTaskResourcesMemoryKey:          "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
 	JobDefaultsDaemonTaskResourcesDiskKey:            "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
 	JobDefaultsDaemonTaskResourcesGPUKey:             "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
+	JobDefaultsDaemonTaskResourcesMemoryKey:          "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
+	JobDefaultsOpsPriorityKey:                        "Priority specifies the default priority allocated to a batch or ops job. This value is used when the job hasn't explicitly set its priority requirement.",
+	JobDefaultsOpsTaskPublisherParamsKey:             "Params specifies the publisher configuration data.",
+	JobDefaultsOpsTaskPublisherTypeKey:               "Type specifies the publisher type. e.g. \"s3\", \"local\", \"ipfs\", etc.",
+	JobDefaultsOpsTaskResourcesCPUKey:                "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
+	JobDefaultsOpsTaskResourcesDiskKey:               "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
+	JobDefaultsOpsTaskResourcesGPUKey:                "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
+	JobDefaultsOpsTaskResourcesMemoryKey:             "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
+	JobDefaultsOpsTaskTimeoutsExecutionTimeoutKey:    "ExecutionTimeout is the maximum time allowed for task execution",
+	JobDefaultsOpsTaskTimeoutsTotalTimeoutKey:        "TotalTimeout is the maximum total time allowed for a task",
 	JobDefaultsServicePriorityKey:                    "Priority specifies the default priority allocated to a service or daemon job. This value is used when the job hasn't explicitly set its priority requirement.",
 	JobDefaultsServiceTaskResourcesCPUKey:            "CPU specifies the default amount of CPU allocated to a task. It uses Kubernetes resource string format (e.g., \"100m\" for 0.1 CPU cores). This value is used when the task hasn't explicitly set its CPU requirement.",
-	JobDefaultsServiceTaskResourcesMemoryKey:         "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
 	JobDefaultsServiceTaskResourcesDiskKey:           "Disk specifies the default amount of disk space allocated to a task. It uses Kubernetes resource string format (e.g., \"1Gi\" for 1 gibibyte). This value is used when the task hasn't explicitly set its disk space requirement.",
 	JobDefaultsServiceTaskResourcesGPUKey:            "GPU specifies the default number of GPUs allocated to a task. It uses Kubernetes resource string format (e.g., \"1\" for 1 GPU). This value is used when the task hasn't explicitly set its GPU requirement.",
-	JobAdmissionControlLocalityKey:                   "Locality specifies the locality of the job input data.",
-	JobAdmissionControlRejectStatelessJobsKey:        "RejectStatelessJobs indicates whether to reject stateless jobs, i.e. jobs without inputs.",
-	JobAdmissionControlAcceptNetworkedJobsKey:        "AcceptNetworkedJobs indicates whether to accept jobs that require network access.",
-	JobAdmissionControlProbeHTTPKey:                  "ProbeHTTP specifies the HTTP endpoint for probing job submission.",
-	JobAdmissionControlProbeExecKey:                  "ProbeExec specifies the command to execute for probing job submission.",
+	JobDefaultsServiceTaskResourcesMemoryKey:         "Memory specifies the default amount of memory allocated to a task. It uses Kubernetes resource string format (e.g., \"256Mi\" for 256 mebibytes). This value is used when the task hasn't explicitly set its memory requirement.",
+	LabelsKey:                                        "Labels are key-value pairs used to describe and categorize the nodes.",
 	LoggingLevelKey:                                  "Level sets the logging level. One of: trace, debug, info, warn, error, fatal, panic.",
-	LoggingModeKey:                                   "Mode specifies the logging mode. One of: default, json.",
 	LoggingLogDebugInfoIntervalKey:                   "LogDebugInfoInterval specifies the interval for logging debug information.",
-	UpdateConfigIntervalKey:                          "Interval specifies the time between heartbeat signals sent to the orchestrator.",
-	FeatureFlagsExecTranslationKey:                   "ExecTranslation enables the execution translation feature.",
-	DisableAnalyticsKey:                              "No description available",
+	LoggingModeKey:                                   "Mode specifies the logging mode. One of: default, json.",
+	NameProviderKey:                                  "NameProvider specifies the method used to generate names for the node. One of: hostname, aws, gcp, uuid, puuid.",
+	OrchestratorAdvertiseKey:                         "Advertise specifies URL to advertise to other servers.",
+	OrchestratorAuthTokenKey:                         "Token specifies the key for compute nodes to be able to access the orchestrator",
+	OrchestratorClusterAdvertiseKey:                  "Advertise specifies the address to advertise to other cluster members.",
+	OrchestratorClusterHostKey:                       "Host specifies the hostname or IP address for cluster communication.",
+	OrchestratorClusterNameKey:                       "Name specifies the unique identifier for this orchestrator cluster.",
+	OrchestratorClusterPeersKey:                      "Peers is a list of other cluster members to connect to on startup.",
+	OrchestratorClusterPortKey:                       "Port specifies the port number for cluster communication.",
+	OrchestratorEnabledKey:                           "Enabled indicates whether the orchestrator node is active and available for job submission.",
+	OrchestratorEvaluationBrokerMaxRetryCountKey:     "MaxRetryCount specifies the maximum number of times an evaluation can be retried before being marked as failed.",
+	OrchestratorEvaluationBrokerVisibilityTimeoutKey: "VisibilityTimeout specifies how long an evaluation can be claimed before it's returned to the queue.",
+	OrchestratorHostKey:                              "Host specifies the hostname or IP address on which the Orchestrator server listens for compute node connections.",
+	OrchestratorNodeManagerDisconnectTimeoutKey:      "DisconnectTimeout specifies how long to wait before considering a node disconnected.",
+	OrchestratorNodeManagerManualApprovalKey:         "ManualApproval, if true, requires manual approval for new compute nodes joining the cluster.",
+	OrchestratorPortKey:                              "Host specifies the port number on which the Orchestrator server listens for compute node connections.",
+	OrchestratorSchedulerHousekeepingIntervalKey:     "HousekeepingInterval specifies how often to run housekeeping tasks.",
+	OrchestratorSchedulerHousekeepingTimeoutKey:      "HousekeepingTimeout specifies the maximum time allowed for a single housekeeping run.",
+	OrchestratorSchedulerQueueBackoffKey:             "QueueBackoff specifies the time to wait before retrying a failed job.",
+	OrchestratorSchedulerWorkerCountKey:              "WorkerCount specifies the number of concurrent workers for job scheduling.",
+	PublishersDisabledKey:                            "Disabled specifies a list of publishers that are disabled.",
+	PublishersTypesIPFSEndpointKey:                   "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
+	PublishersTypesLocalAddressKey:                   "Address specifies the endpoint the publisher serves on.",
+	PublishersTypesLocalPortKey:                      "Port specifies the port the publisher serves on.",
+	PublishersTypesS3PreSignedURLDisabledKey:         "PreSignedURLDisabled specifies whether pre-signed URLs are enabled for the S3 provider.",
+	PublishersTypesS3PreSignedURLExpirationKey:       "PreSignedURLExpiration specifies the duration before a pre-signed URL expires.",
+	ResultDownloadersDisabledKey:                     "Disabled is a list of downloaders that are disabled.",
+	ResultDownloadersTimeoutKey:                      "Timeout specifies the maximum time allowed for a download operation.",
+	ResultDownloadersTypesIPFSEndpointKey:            "Endpoint specifies the multi-address to connect to for IPFS. e.g /ip4/127.0.0.1/tcp/5001",
+	StrictVersionMatchKey:                            "StrictVersionMatch indicates whether to enforce strict version matching.",
+	UpdateConfigIntervalKey:                          "Interval specifies the time between update checks, when set to 0 update checks are not performed.",
+	WebUIBackendKey:                                  "Backend specifies the address and port of the backend API server. If empty, the Web UI will use the same address and port as the API server.",
+	WebUIEnabledKey:                                  "Enabled indicates whether the Web UI is enabled.",
+	WebUIListenKey:                                   "Listen specifies the address and port on which the Web UI listens.",
 }
diff --git a/pkg/config/types/jobdefaults.go b/pkg/config/types/jobdefaults.go
index ac2df558bb..ce241c0543 100644
--- a/pkg/config/types/jobdefaults.go
+++ b/pkg/config/types/jobdefaults.go
@@ -19,9 +19,9 @@ type BatchJobDefaultsConfig struct {
 }
 
 type BatchTaskDefaultConfig struct {
-	Resources ResourcesConfig   `yaml:"Resources,omitempty" json:"Resources,omitempty"`
-	Publisher models.SpecConfig `yaml:"Publisher,omitempty"`
-	Timeouts  TaskTimeoutConfig `yaml:"Timeouts,omitempty" json:"Timeouts,omitempty"`
+	Resources ResourcesConfig        `yaml:"Resources,omitempty" json:"Resources,omitempty"`
+	Publisher DefaultPublisherConfig `yaml:"Publisher,omitempty" json:"Publisher,omitempty"`
+	Timeouts  TaskTimeoutConfig      `yaml:"Timeouts,omitempty" json:"Timeouts,omitempty"`
 }
 
 type ResourcesConfig struct {
@@ -56,6 +56,28 @@ func FromModelsResourceConfig(r models.ResourcesConfig) ResourcesConfig {
 	}
 }
 
+type DefaultPublisherConfig struct {
+	// Type specifies the publisher type. e.g. "s3", "local", "ipfs", etc.
+	Type string `json:"Type,omitempty" yaml:"Type,omitempty"`
+
+	// Params specifies the publisher configuration data.
+	Params map[string]string `json:"Params,omitempty" yaml:"Params,omitempty"`
+}
+
+func (d DefaultPublisherConfig) ToSpecConfig() models.SpecConfig {
+	sc := models.SpecConfig{
+		Type:   d.Type,
+		Params: make(map[string]interface{}),
+	}
+
+	for k, v := range d.Params {
+		sc.Params[k] = v
+	}
+
+	sc.Normalize()
+	return sc
+}
+
 type TaskTimeoutConfig struct {
 	// TotalTimeout is the maximum total time allowed for a task
 	TotalTimeout Duration `yaml:"TotalTimeout,omitempty" json:"TotalTimeout,omitempty"`
diff --git a/pkg/devstack/option.go b/pkg/devstack/option.go
index b4611b926e..3b0f238b2a 100644
--- a/pkg/devstack/option.go
+++ b/pkg/devstack/option.go
@@ -8,7 +8,6 @@ import (
 
 	"github.com/bacalhau-project/bacalhau/pkg/config"
 	"github.com/bacalhau-project/bacalhau/pkg/config/types"
-	"github.com/bacalhau-project/bacalhau/pkg/models"
 	"github.com/bacalhau-project/bacalhau/pkg/node"
 )
 
@@ -175,7 +174,7 @@ func WithAllowListedLocalPaths(paths []string) ConfigOption {
 	}
 }
 
-func WithDefaultPublisher(publisher models.SpecConfig) ConfigOption {
+func WithDefaultPublisher(publisher types.DefaultPublisherConfig) ConfigOption {
 	return func(cfg *DevStackConfig) {
 		cfg.BacalhauConfig.JobDefaults.Batch.Task.Publisher = publisher
 		cfg.BacalhauConfig.JobDefaults.Ops.Task.Publisher = publisher
diff --git a/pkg/orchestrator/transformer/job.go b/pkg/orchestrator/transformer/job.go
index eda2016863..60b62f5482 100644
--- a/pkg/orchestrator/transformer/job.go
+++ b/pkg/orchestrator/transformer/job.go
@@ -69,8 +69,9 @@ func applyBatchTaskDefaults(defaults types.BatchTaskDefaultConfig, task *models.
 		task.ResourcesConfig.GPU = defaults.Resources.GPU
 	}
 	// if the user didn't provide a publisher, and a default transformer is set - use it.
-	if task.Publisher.IsEmpty() && !defaults.Publisher.IsEmpty() {
-		task.Publisher = &defaults.Publisher
+	specConfig := defaults.Publisher.ToSpecConfig()
+	if task.Publisher.IsEmpty() && !specConfig.IsEmpty() {
+		task.Publisher = &specConfig
 	}
 	if task.Timeouts.ExecutionTimeout <= 0 {
 		task.Timeouts.ExecutionTimeout = int64(time.Duration(defaults.Timeouts.ExecutionTimeout).Seconds())


Test patch:
diff --git a/pkg/test/devstack/default_publisher_test.go b/pkg/test/devstack/default_publisher_test.go
index 67bc88dede..503e64ed43 100644
--- a/pkg/test/devstack/default_publisher_test.go
+++ b/pkg/test/devstack/default_publisher_test.go
@@ -7,11 +7,11 @@ import (
 	"os"
 	"testing"
 
+	"github.com/bacalhau-project/bacalhau/pkg/config/types"
 	"github.com/bacalhau-project/bacalhau/pkg/devstack"
 	wasmmodels "github.com/bacalhau-project/bacalhau/pkg/executor/wasm/models"
 	_ "github.com/bacalhau-project/bacalhau/pkg/logger"
 	"github.com/bacalhau-project/bacalhau/pkg/models"
-	"github.com/bacalhau-project/bacalhau/pkg/publisher/local"
 	"github.com/bacalhau-project/bacalhau/pkg/test/scenario"
 	"github.com/bacalhau-project/bacalhau/testdata/wasm/cat"
 
@@ -75,7 +75,10 @@ func (s *DefaultPublisherSuite) TestDefaultPublisher() {
 		},
 		Stack: &scenario.StackConfig{
 			DevStackOptions: []devstack.ConfigOption{
-				devstack.WithDefaultPublisher(*local.NewSpecConfig()),
+				devstack.WithDefaultPublisher(types.DefaultPublisherConfig{
+					Type:   models.PublisherLocal,
+					Params: make(map[string]string),
+				}),
 			},
 		},
 		ResultsChecker: expectResultsSome,