diff --git a/harnesses/codex/config.yaml b/harnesses/codex/config.yaml
index 8892097e6..b21a153b6 100644
--- a/harnesses/codex/config.yaml
+++ b/harnesses/codex/config.yaml
@@ -71,6 +71,12 @@ capabilities:
sse: { support: "partial", reason: "Codex maps SSE to its single HTTP server type (url + http_headers)" }
streamable_http: { support: "yes" }
project_scope: { support: "no", reason: "Project-scoped MCP (.codex/mcp_servers.json) is not implemented yet; project entries are demoted to global" }
+no_auth:
+ behavior: drop-to-shell
+ message: |
+ This agent started without credentials.
+ Run your Codex authentication setup.
+ Then run: python3 /home/scion/.scion/harness/capture_auth.py
auth:
default_type: api-key
types:
@@ -89,9 +95,3 @@ auth:
OPENAI_API_KEY: api-key
files:
CODEX_AUTH: auth-file
-no_auth:
- behavior: drop-to-shell
- message: |
- This agent started without credentials.
- Set CODEX_API_KEY or OPENAI_API_KEY, or authenticate interactively.
- Then capture credentials: python3 ~/.scion/harness/capture_auth.py
diff --git a/harnesses/opencode/config.yaml b/harnesses/opencode/config.yaml
index 388caf64d..011054e0c 100644
--- a/harnesses/opencode/config.yaml
+++ b/harnesses/opencode/config.yaml
@@ -67,6 +67,12 @@ capabilities:
sse: { support: "yes" }
streamable_http: { support: "yes" }
project_scope: { support: "no", reason: "OpenCode does not distinguish project-scoped MCP" }
+no_auth:
+ behavior: drop-to-shell
+ message: |
+ This agent started without credentials.
+ Run your OpenCode authentication setup.
+ Then run: python3 /home/scion/.scion/harness/capture_auth.py
auth:
default_type: api-key
types:
@@ -85,9 +91,3 @@ auth:
OPENAI_API_KEY: api-key
files:
OPENCODE_AUTH: auth-file
-no_auth:
- behavior: drop-to-shell
- message: |
- This agent started without credentials.
- Set ANTHROPIC_API_KEY or OPENAI_API_KEY, or authenticate interactively.
- Then capture credentials: python3 ~/.scion/harness/capture_auth.py
diff --git a/pkg/agent/run.go b/pkg/agent/run.go
index a83087934..ced5bdbc8 100644
--- a/pkg/agent/run.go
+++ b/pkg/agent/run.go
@@ -341,8 +341,7 @@ func (m *AgentManager) Start(ctx context.Context, opts api.StartOptions) (*api.A
var h api.Harness
var harnessConfigRevision string
var resolvedImpl string
- var noAuthMessage string
- var resolvedAuthMeta *config.HarnessAuthMetadata
+ var noAuthConfig *config.HarnessNoAuthConfig
if harnessConfigName != "" {
var resolveTemplatePaths []string
if opts.Template != "" {
@@ -369,14 +368,11 @@ func (m *AgentManager) Start(ctx context.Context, opts api.StartOptions) (*api.A
} else {
h = resolved.Harness
resolvedImpl = resolved.Implementation
+ noAuthConfig = resolved.Config.NoAuthConfig
if resolved.ConfigDir != nil {
harnessConfigRevision = config.ComputeHarnessConfigRevision(resolved.ConfigDir.Path)
}
util.Debugf("harness resolution: implementation=%s harness=%q", resolved.Implementation, resolved.Config.Harness)
- if opts.NoAuth && resolved.Config.NoAuth != nil {
- noAuthMessage = resolved.Config.NoAuth.Message
- }
- resolvedAuthMeta = resolved.Config.Auth
}
} else {
h = harness.New(harnessName)
@@ -431,11 +427,7 @@ func (m *AgentManager) Start(ctx context.Context, opts api.StartOptions) (*api.A
if !opts.NoAuth {
auth = harness.GatherAuthWithEnv(authEnvOverlay, !opts.BrokerMode)
if opts.BrokerMode {
- if resolvedAuthMeta != nil {
- harness.OverlayFileSecretsFromConfig(&auth, opts.ResolvedSecrets, resolvedAuthMeta)
- } else {
- harness.OverlayFileSecrets(&auth, opts.ResolvedSecrets)
- }
+ harness.OverlayFileSecrets(&auth, opts.ResolvedSecrets)
}
util.Debugf("auth: gathered credentials — selectedType=%q, hasGeminiKey=%t, hasGoogleKey=%t, hasOAuth=%t, hasADC=%t, hasAnthropicKey=%t, hasClaudeOAuthToken=%t, hasClaudeAuthFile=%t, cloudProject=%q, gcpMetadataMode=%q, brokerMode=%t",
auth.SelectedType,
@@ -893,11 +885,16 @@ func (m *AgentManager) Start(ctx context.Context, opts api.StartOptions) (*api.A
}
return nil
}(),
- GitClone: opts.GitClone,
- SharedDirs: effectiveSharedDirs,
- BrokerMode: opts.BrokerMode,
- NoAuth: opts.NoAuth,
- NoAuthMessage: noAuthMessage,
+ GitClone: opts.GitClone,
+ SharedDirs: effectiveSharedDirs,
+ BrokerMode: opts.BrokerMode,
+ NoAuth: opts.NoAuth && noAuthConfig != nil && noAuthConfig.Behavior == "drop-to-shell",
+ NoAuthMessage: func() string {
+ if opts.NoAuth && noAuthConfig != nil && noAuthConfig.Behavior == "drop-to-shell" {
+ return noAuthConfig.Message
+ }
+ return ""
+ }(),
Debug: util.DebugEnabled(),
Resume: opts.Resume,
MetadataInterception: hasMetadataInterception(agentEnv),
diff --git a/pkg/config/schemas/settings-v1.schema.json b/pkg/config/schemas/settings-v1.schema.json
index 10c11a643..af7e542ee 100644
--- a/pkg/config/schemas/settings-v1.schema.json
+++ b/pkg/config/schemas/settings-v1.schema.json
@@ -269,8 +269,8 @@
},
"auth_selected_type": {
"type": "string",
- "enum": ["api-key", "oauth-token", "auth-file", "vertex-ai"],
- "description": "Authentication mechanism to use (e.g., api-key, oauth-token, vertex-ai, auth-file)."
+ "enum": ["api-key", "oauth-token", "auth-file", "vertex-ai", "none"],
+ "description": "Authentication mechanism to use (e.g., api-key, oauth-token, vertex-ai, auth-file, none)."
},
"secrets": {
"type": "array",
@@ -328,14 +328,14 @@
"$ref": "#/$defs/harnessAuthMetadata",
"description": "Declarative auth preflight metadata for broker and hosted dispatch."
},
- "no_auth": {
- "$ref": "#/$defs/harnessNoAuthConfig",
- "description": "Defines behavior when an agent starts with --no-auth (no credentials)."
- },
"mcp": {
"$ref": "#/$defs/harnessMCPMapping",
"description": "Declarative mapping for translating universal mcp_servers into the harness's native MCP config (used by scion_harness.apply_mcp_servers_simple). Harnesses with bespoke MCP formats (e.g. OpenCode) leave this empty and translate themselves in provision.py."
},
+ "no_auth": {
+ "$ref": "#/$defs/harnessNoAuthConfig",
+ "description": "Behavior when an agent starts without credentials (NoAuth mode)."
+ },
"dialect": {
"type": "object",
"description": "Optional hook dialect metadata.",
@@ -350,11 +350,11 @@
"behavior": {
"type": "string",
"enum": ["drop-to-shell", "show-setup-instructions", "run-setup-wizard"],
- "description": "How the container starts when no credentials are present."
+ "description": "What the harness should do when no credentials are provided."
},
"message": {
"type": "string",
- "description": "Message displayed to the user when the agent starts without credentials."
+ "description": "Message to display to the user in no-auth mode."
}
},
"additionalProperties": false
@@ -547,13 +547,13 @@
"type": { "type": "string" },
"description": { "type": "string" },
"target_suffix": { "type": "string" },
- "field": { "type": "string" },
"alternative_env_keys": {
"type": "array",
"items": { "type": "string" }
},
"skipped_when_gcp_service_account_assigned": { "type": "boolean" },
- "required": { "type": "boolean" }
+ "required": { "type": "boolean" },
+ "field": { "type": "string" }
},
"additionalProperties": false
},
diff --git a/pkg/config/settings_v1.go b/pkg/config/settings_v1.go
index 15d4dcdc8..7d1f7aed6 100644
--- a/pkg/config/settings_v1.go
+++ b/pkg/config/settings_v1.go
@@ -331,9 +331,14 @@ type V1PluginEntry struct {
// SelfManaged indicates the plugin manages its own process lifecycle.
// The Hub connects to the plugin's RPC server rather than starting it.
SelfManaged bool `json:"self_managed,omitempty" yaml:"self_managed,omitempty" koanf:"self_managed"`
- // Address is the RPC address for self-managed plugins (e.g. "localhost:9090").
- // Required when SelfManaged is true.
+ // Address is the network address for self-managed or gRPC plugins.
+ // Required when SelfManaged is true or Mode is "grpc".
Address string `json:"address,omitempty" yaml:"address,omitempty" koanf:"address"`
+ // Mode selects the plugin communication mode: "" or "plugin" (default go-plugin
+ // subprocess), "grpc" (standalone gRPC broker), "self-managed" (go-plugin RPC to
+ // an externally-managed process). When empty, falls back to SelfManaged for
+ // backward compatibility.
+ Mode string `json:"mode,omitempty" yaml:"mode,omitempty" koanf:"mode"`
}
// V1ServerHubConfig holds the Hub API server settings (when running scion-server).
@@ -715,7 +720,7 @@ type HarnessConfigEntry struct {
EnvTemplate map[string]string `json:"env_template,omitempty" yaml:"env_template,omitempty" koanf:"env_template"`
Capabilities *api.HarnessAdvancedCapabilities `json:"capabilities,omitempty" yaml:"capabilities,omitempty" koanf:"capabilities"`
Auth *HarnessAuthMetadata `json:"auth,omitempty" yaml:"auth,omitempty" koanf:"auth"`
- NoAuth *HarnessNoAuthConfig `json:"no_auth,omitempty" yaml:"no_auth,omitempty" koanf:"no_auth"`
+ NoAuthConfig *HarnessNoAuthConfig `json:"no_auth,omitempty" yaml:"no_auth,omitempty" koanf:"no_auth"`
MCP *HarnessMCPConfig `json:"mcp,omitempty" yaml:"mcp,omitempty" koanf:"mcp"`
Dialect map[string]interface{} `json:"dialect,omitempty" yaml:"dialect,omitempty" koanf:"dialect"`
}
@@ -793,18 +798,10 @@ type HarnessAuthAutodetect struct {
Files map[string]string `json:"files,omitempty" yaml:"files,omitempty" koanf:"files"`
}
-// HarnessNoAuthConfig defines what happens when an agent starts with
-// --no-auth (no credentials injected). The behavior field determines
-// how the container starts.
+// HarnessNoAuthConfig defines harness behavior when an agent starts without credentials.
type HarnessNoAuthConfig struct {
- // Behavior controls the container startup when no credentials are present.
- // Supported values:
- // "drop-to-shell" — start the container but don't launch the harness CLI
- // "show-setup-instructions" — launch normally but display setup instructions
- // "run-setup-wizard" — execute a setup script before the main process
Behavior string `json:"behavior,omitempty" yaml:"behavior,omitempty" koanf:"behavior"`
- // Message is displayed to the user when the agent starts without credentials.
- Message string `json:"message,omitempty" yaml:"message,omitempty" koanf:"message"`
+ Message string `json:"message,omitempty" yaml:"message,omitempty" koanf:"message"`
}
// HarnessMCPConfig is the declarative mapping that lets a harness's
diff --git a/pkg/harness/claude/embeds/config.yaml b/pkg/harness/claude/embeds/config.yaml
index 37a358bb5..205cddc4f 100644
--- a/pkg/harness/claude/embeds/config.yaml
+++ b/pkg/harness/claude/embeds/config.yaml
@@ -52,6 +52,12 @@ capabilities:
auth_file: { support: "yes" }
oauth_token: { support: "yes" }
vertex_ai: { support: "yes" }
+no_auth:
+ behavior: drop-to-shell
+ message: |
+ This agent started without credentials.
+ Run: claude login
+ Then run: python3 /home/scion/.scion/harness/capture_auth.py
auth:
default_type: api-key
types:
@@ -88,9 +94,3 @@ auth:
files:
CLAUDE_AUTH: auth-file
gcloud-adc: vertex-ai
-no_auth:
- behavior: drop-to-shell
- message: |
- This agent started without credentials.
- To authenticate, run: claude login
- Then capture credentials: python3 ~/.scion/harness/capture_auth.py
diff --git a/pkg/harness/gemini/embeds/config.yaml b/pkg/harness/gemini/embeds/config.yaml
index f94e99a6d..1ee301529 100644
--- a/pkg/harness/gemini/embeds/config.yaml
+++ b/pkg/harness/gemini/embeds/config.yaml
@@ -51,6 +51,12 @@ capabilities:
auth_file: { support: "yes" }
oauth_token: { support: "no" }
vertex_ai: { support: "yes" }
+no_auth:
+ behavior: drop-to-shell
+ message: |
+ This agent started without credentials.
+ Run your Gemini authentication setup.
+ Then run: python3 /home/scion/.scion/harness/capture_auth.py
auth:
default_type: api-key
types:
@@ -85,9 +91,3 @@ auth:
files:
GEMINI_OAUTH_CREDS: auth-file
gcloud-adc: vertex-ai
-no_auth:
- behavior: drop-to-shell
- message: |
- This agent started without credentials.
- To authenticate, run: gcloud auth application-default login
- Then capture credentials: python3 ~/.scion/harness/capture_auth.py
diff --git a/pkg/hub/admin_maintenance.go b/pkg/hub/admin_maintenance.go
index dafc17b5e..c484b8583 100644
--- a/pkg/hub/admin_maintenance.go
+++ b/pkg/hub/admin_maintenance.go
@@ -293,16 +293,6 @@ func (s *Server) resolveMaintenanceExecutor(key string) (MaintenanceExecutor, er
return &RebuildContainerBinariesExecutor{
repoPath: mc.RepoPath,
}, nil
- case "build-harness-config-image":
- log.Debug("Resolved build-harness-config-image executor",
- "runtime_bin", mc.RuntimeBin, "registry", mc.ImageRegistry, "tag", mc.ImageTag)
- return &BuildHarnessConfigImageExecutor{
- store: s.store,
- storage: s.GetStorage(),
- runtimeBin: mc.RuntimeBin,
- registry: mc.ImageRegistry,
- tag: mc.ImageTag,
- }, nil
default:
return nil, fmt.Errorf("no executor registered for operation %q", key)
}
diff --git a/pkg/hub/handlers.go b/pkg/hub/handlers.go
index e977c036e..200c499db 100644
--- a/pkg/hub/handlers.go
+++ b/pkg/hub/handlers.go
@@ -9251,6 +9251,10 @@ func (s *Server) buildAppliedConfig(req CreateAgentRequest, harnessConfig string
ac.InlineConfig = req.Config
}
+ if ac.HarnessAuth == "none" {
+ ac.NoAuth = true
+ }
+
return ac
}
diff --git a/pkg/hub/maintenance_executors.go b/pkg/hub/maintenance_executors.go
index e7fd7fc05..cb303e3f4 100644
--- a/pkg/hub/maintenance_executors.go
+++ b/pkg/hub/maintenance_executors.go
@@ -27,7 +27,6 @@ import (
scionruntime "github.com/GoogleCloudPlatform/scion/pkg/runtime"
"github.com/GoogleCloudPlatform/scion/pkg/secret"
- "github.com/GoogleCloudPlatform/scion/pkg/storage"
"github.com/GoogleCloudPlatform/scion/pkg/store"
"github.com/GoogleCloudPlatform/scion/pkg/util/logging"
)
@@ -429,162 +428,6 @@ func (e *RebuildContainerBinariesExecutor) Run(ctx context.Context, logger io.Wr
return nil
}
-// BuildHarnessConfigImageExecutor builds a container image from a harness-config's Dockerfile.
-type BuildHarnessConfigImageExecutor struct {
- store store.Store
- storage storage.Storage
- runtimeBin string
- registry string
- tag string
-}
-
-func (e *BuildHarnessConfigImageExecutor) Run(ctx context.Context, logger io.Writer, params map[string]string) error {
- log := logging.Subsystem("hub.maintenance.build-harness-config-image")
-
- harnessConfigID := params["harness_config_id"]
- if harnessConfigID == "" {
- return fmt.Errorf("missing required parameter: harness_config_id")
- }
-
- tag := e.tag
- if tag == "" {
- tag = "latest"
- }
- if v := params["tag"]; v != "" {
- tag = v
- }
-
- registry := e.registry
- if v := params["registry"]; v != "" {
- registry = v
- }
- registry = strings.TrimSuffix(registry, "/")
-
- hc, err := e.store.GetHarnessConfig(ctx, harnessConfigID)
- if err != nil {
- return fmt.Errorf("failed to load harness-config %q: %w", harnessConfigID, err)
- }
-
- hasDockerfile := false
- for _, f := range hc.Files {
- if f.Path == "Dockerfile" {
- hasDockerfile = true
- break
- }
- }
- if !hasDockerfile {
- return fmt.Errorf("harness-config %q does not contain a Dockerfile", hc.Name)
- }
-
- if e.storage == nil {
- return fmt.Errorf("storage not configured")
- }
-
- tmpDir, err := os.MkdirTemp("", "scion-build-*")
- if err != nil {
- return fmt.Errorf("failed to create temp directory: %w", err)
- }
- defer os.RemoveAll(tmpDir)
-
- fmt.Fprintf(logger, "Materializing %d file(s) from harness-config %q...\n", len(hc.Files), hc.Name)
- for _, f := range hc.Files {
- objectPath := hc.StoragePath + "/" + f.Path
- reader, _, err := e.storage.Download(ctx, objectPath)
- if err != nil {
- return fmt.Errorf("failed to download %q from storage: %w", f.Path, err)
- }
-
- destPath := filepath.Join(tmpDir, f.Path)
- if !strings.HasPrefix(destPath, tmpDir+string(os.PathSeparator)) {
- _ = reader.Close()
- return fmt.Errorf("invalid file path %q: escapes build directory", f.Path)
- }
- if dir := filepath.Dir(destPath); dir != tmpDir {
- if err := os.MkdirAll(dir, 0o755); err != nil {
- _ = reader.Close()
- return fmt.Errorf("failed to create directory for %q: %w", f.Path, err)
- }
- }
-
- outFile, err := os.Create(destPath)
- if err != nil {
- _ = reader.Close()
- return fmt.Errorf("failed to create file %q: %w", f.Path, err)
- }
- _, err = io.Copy(outFile, reader)
- _ = reader.Close()
- _ = outFile.Close()
- if err != nil {
- return fmt.Errorf("failed to write file %q: %w", f.Path, err)
- }
-
- if f.Mode != "" {
- mode := os.FileMode(0o644)
- if _, err := fmt.Sscanf(f.Mode, "%o", &mode); err == nil {
- _ = os.Chmod(destPath, mode)
- }
- }
- }
-
- baseImage := "scion-base:" + tag
- if registry != "" {
- baseImage = registry + "/scion-base:" + tag
- }
- fmt.Fprintf(logger, "Base image: %s\n", baseImage)
-
- runtimeBin := e.runtimeBin
- if runtimeBin == "" {
- runtimeBin = scionruntime.DetectContainerRuntime()
- }
- if runtimeBin == "" {
- return fmt.Errorf("no container runtime found (tried docker, podman)")
- }
-
- imageName := hc.Slug
- if imageName == "" {
- imageName = hc.Name
- }
- outputImage := imageName + ":" + tag
- fmt.Fprintf(logger, "Building %s from harness-config %q...\n", outputImage, hc.Name)
- log.Debug("Starting container build",
- "image", outputImage, "base_image", baseImage,
- "runtime", runtimeBin, "harness_config", hc.Name)
-
- cmd := exec.CommandContext(ctx, runtimeBin, "build",
- "--build-arg", "BASE_IMAGE="+baseImage,
- "-t", outputImage,
- tmpDir)
- cmd.Stdout = logger
- cmd.Stderr = logger
- if err := cmd.Run(); err != nil {
- return fmt.Errorf("build failed: %w", err)
- }
-
- if params["push"] == "true" && registry != "" {
- pushImage := registry + "/" + outputImage
- fmt.Fprintf(logger, "Tagging %s as %s...\n", outputImage, pushImage)
- tagCmd := exec.CommandContext(ctx, runtimeBin, "tag", outputImage, pushImage)
- tagCmd.Stdout = logger
- tagCmd.Stderr = logger
- if err := tagCmd.Run(); err != nil {
- return fmt.Errorf("tag failed: %w", err)
- }
-
- fmt.Fprintf(logger, "Pushing %s...\n", pushImage)
- pushCmd := exec.CommandContext(ctx, runtimeBin, "push", pushImage)
- pushCmd.Stdout = logger
- pushCmd.Stderr = logger
- if err := pushCmd.Run(); err != nil {
- return fmt.Errorf("push failed: %w", err)
- }
- outputImage = pushImage
- }
-
- fmt.Fprintf(logger, "\nBuild complete: %s\n", outputImage)
- log.Info("Build complete", "image", outputImage, "harness_config", hc.Name)
- return nil
-}
-
// UpdateCheckResult contains the result of a check-for-updates operation.
type UpdateCheckResult struct {
UpdateAvailable bool `json:"update_available"`
diff --git a/pkg/hub/server.go b/pkg/hub/server.go
index f02a8d1f8..21faeca77 100644
--- a/pkg/hub/server.go
+++ b/pkg/hub/server.go
@@ -390,6 +390,8 @@ type RemoteCreateAgentRequest struct {
// CreatorName is the human-readable identity of who created this agent.
// Injected as the SCION_CREATOR environment variable in the agent container.
CreatorName string `json:"creatorName,omitempty"`
+ // NoAuth indicates the agent should start without any injected credentials.
+ NoAuth bool `json:"noAuth,omitempty"`
// Attach indicates the agent should start in interactive attach mode (not detached).
Attach bool `json:"attach,omitempty"`
// ProvisionOnly indicates the agent should be provisioned (dirs, worktree, templates)
@@ -433,10 +435,6 @@ type RemoteCreateAgentRequest struct {
// (e.g. "shared", "per-agent", "worktree-per-agent"). Threaded from the
// Hub so the broker can branch dispatch without re-deriving from labels.
WorkspaceMode string `json:"workspaceMode,omitempty"`
-
- // NoAuth indicates the agent should start with zero injected credentials.
- // When true, the broker skips credential injection.
- NoAuth bool `json:"noAuth,omitempty"`
}
// ResolvedSecret represents a secret resolved by the Hub for projection into an agent container.
diff --git a/pkg/runtime/common.go b/pkg/runtime/common.go
index e07f7424b..047500979 100644
--- a/pkg/runtime/common.go
+++ b/pkg/runtime/common.go
@@ -428,21 +428,16 @@ func buildCommonRunArgs(config RunConfig) ([]string, error) {
// Get command from harness
var harnessArgs []string
- if config.Harness != nil {
- harnessArgs = config.Harness.GetCommand(config.Task, config.Resume, config.CommandArgs)
- } else {
- return nil, fmt.Errorf("no harness provided")
- }
-
- // When NoAuth is set, drop to a shell instead of launching the harness CLI.
- // TODO: Only drop-to-shell is currently implemented. show-setup-instructions
- // and run-setup-wizard are defined in config but not yet handled.
if config.NoAuth {
if config.NoAuthMessage != "" {
- harnessArgs = []string{"sh", "-c", fmt.Sprintf("echo %s; exec bash", shellQuote(config.NoAuthMessage))}
+ harnessArgs = []string{"sh", "-c", fmt.Sprintf("printf '%%s\\n' %s; exec bash", shellQuote(config.NoAuthMessage))}
} else {
harnessArgs = []string{"bash"}
}
+ } else if config.Harness != nil {
+ harnessArgs = config.Harness.GetCommand(config.Task, config.Resume, config.CommandArgs)
+ } else {
+ return nil, fmt.Errorf("no harness provided")
}
// Build tmux-wrapped command — use POSIX single-quote escaping so that
diff --git a/pkg/runtime/k8s_runtime.go b/pkg/runtime/k8s_runtime.go
index 12bf60ea9..b856d5efe 100644
--- a/pkg/runtime/k8s_runtime.go
+++ b/pkg/runtime/k8s_runtime.go
@@ -881,7 +881,13 @@ func (r *KubernetesRuntime) buildPod(namespace string, config RunConfig) (*corev
// Command Resolution
var cmd []string
var harnessArgs []string
- if config.Harness != nil {
+ if config.NoAuth {
+ if config.NoAuthMessage != "" {
+ harnessArgs = []string{"sh", "-c", fmt.Sprintf("printf '%%s\\n' %s; exec bash", shellQuote(config.NoAuthMessage))}
+ } else {
+ harnessArgs = []string{"bash"}
+ }
+ } else if config.Harness != nil {
harnessArgs = config.Harness.GetCommand(config.Task, config.Resume, config.CommandArgs)
} else {
// Fallback if no harness (though RunConfig implies there should be one or defaults)
diff --git a/pkg/runtimebroker/handlers.go b/pkg/runtimebroker/handlers.go
index 55c1921c2..ef72eee70 100644
--- a/pkg/runtimebroker/handlers.go
+++ b/pkg/runtimebroker/handlers.go
@@ -592,8 +592,8 @@ func (s *Server) createAgent(w http.ResponseWriter, r *http.Request) {
CreatorName: req.CreatorName,
ResolvedEnv: req.ResolvedEnv,
ResolvedSecrets: req.ResolvedSecrets,
- Attach: req.Attach,
NoAuth: req.NoAuth,
+ Attach: req.Attach,
WorkspaceMode: req.WorkspaceMode,
HTTPRequest: r,
})
@@ -2326,6 +2326,7 @@ func (s *Server) finalizeEnv(w http.ResponseWriter, r *http.Request, id string)
CreatorName: origReq.CreatorName,
ResolvedEnv: pending.MergedEnv,
ResolvedSecrets: origReq.ResolvedSecrets,
+ NoAuth: origReq.NoAuth,
Attach: origReq.Attach,
HTTPRequest: r,
})
diff --git a/pkg/runtimebroker/start_context.go b/pkg/runtimebroker/start_context.go
index cd951acfa..e93f53b0f 100644
--- a/pkg/runtimebroker/start_context.go
+++ b/pkg/runtimebroker/start_context.go
@@ -76,8 +76,8 @@ type startContextInputs struct {
ResolvedSecrets []api.ResolvedSecret
// Behavior
- Attach bool
NoAuth bool
+ Attach bool
// WorkspaceMode is the resolved workspace sharing mode for the project
// (e.g. "worktree-per-agent"). Threaded from CreateAgentRequest so the
diff --git a/pkg/runtimebroker/types.go b/pkg/runtimebroker/types.go
index 46e477d02..7c6f0a2e6 100644
--- a/pkg/runtimebroker/types.go
+++ b/pkg/runtimebroker/types.go
@@ -286,6 +286,8 @@ type CreateAgentRequest struct {
// CreatorName is the human-readable identity of who created this agent.
// Injected as the SCION_CREATOR environment variable in the agent container.
CreatorName string `json:"creatorName,omitempty"`
+ // NoAuth indicates the agent should start without any injected credentials.
+ NoAuth bool `json:"noAuth,omitempty"`
// Attach indicates the agent should start in interactive attach mode (not detached).
Attach bool `json:"attach,omitempty"`
// ProvisionOnly indicates the agent should be provisioned (dirs, worktree, templates)
@@ -326,10 +328,6 @@ type CreateAgentRequest struct {
// (e.g. "shared", "per-agent", "worktree-per-agent"). Threaded from the
// Hub so the broker can branch dispatch without re-deriving from labels.
WorkspaceMode string `json:"workspaceMode,omitempty"`
-
- // NoAuth indicates the agent should start with zero injected credentials.
- // When true, the broker skips credential injection.
- NoAuth bool `json:"noAuth,omitempty"`
}
// UnmarshalJSON implements custom unmarshaling to support legacy grove fields.
diff --git a/pkg/sciontool/telemetry/pipeline_health_test.go b/pkg/sciontool/telemetry/pipeline_health_test.go
index b37755650..9ea158716 100644
--- a/pkg/sciontool/telemetry/pipeline_health_test.go
+++ b/pkg/sciontool/telemetry/pipeline_health_test.go
@@ -7,7 +7,6 @@ package telemetry
import (
"context"
"errors"
- "os"
"testing"
"time"
@@ -16,10 +15,10 @@ import (
func TestPipeline_HealthGauge_Registers(t *testing.T) {
clearTelemetryEnv()
- os.Setenv(EnvEnabled, "true")
- os.Setenv(EnvCloudEnabled, "false")
- os.Setenv(EnvGRPCPort, "54401")
- os.Setenv(EnvHTTPPort, "54402")
+ t.Setenv(EnvEnabled, "true")
+ t.Setenv(EnvCloudEnabled, "false")
+ t.Setenv(EnvGRPCPort, "54401")
+ t.Setenv(EnvHTTPPort, "54402")
defer clearTelemetryEnv()
cfg := &Config{
@@ -54,10 +53,10 @@ func TestPipeline_HealthGauge_Registers(t *testing.T) {
func TestPipeline_HealthGauge_StopsOnStop(t *testing.T) {
clearTelemetryEnv()
- os.Setenv(EnvEnabled, "true")
- os.Setenv(EnvCloudEnabled, "false")
- os.Setenv(EnvGRPCPort, "54403")
- os.Setenv(EnvHTTPPort, "54404")
+ t.Setenv(EnvEnabled, "true")
+ t.Setenv(EnvCloudEnabled, "false")
+ t.Setenv(EnvGRPCPort, "54403")
+ t.Setenv(EnvHTTPPort, "54404")
defer clearTelemetryEnv()
cfg := &Config{
diff --git a/pkg/store/entadapter/maintenance_store.go b/pkg/store/entadapter/maintenance_store.go
index 65268e81b..7172e90d3 100644
--- a/pkg/store/entadapter/maintenance_store.go
+++ b/pkg/store/entadapter/maintenance_store.go
@@ -74,12 +74,6 @@ var defaultSeedOperations = []store.MaintenanceOperation{
Description: "Rebuilds scion and sciontool binaries for Linux containers (make container-binaries). Only available when SCION_DEV_BINARIES is set. Binaries are written to .build/container/ in the source checkout.",
Category: store.MaintenanceCategoryOperation,
},
- {
- Key: "build-harness-config-image",
- Title: "Build Harness Config Image",
- Description: "Builds a container image from a harness-config's bundled Dockerfile. The base image is resolved from the configured image registry.",
- Category: store.MaintenanceCategoryOperation,
- },
}
// ============================================================================
diff --git a/web/src/components/pages/agent-configure.ts b/web/src/components/pages/agent-configure.ts
index 7ea2bd46e..1e47871d7 100644
--- a/web/src/components/pages/agent-configure.ts
+++ b/web/src/components/pages/agent-configure.ts
@@ -836,6 +836,7 @@ export class ScionPageAgentConfigure extends LitElement {
${hc.description}
` : ''} `; } - // ── Build Image ── - - private openBuildDialog(): void { - this.buildTag = 'latest'; - this.buildPush = false; - this.buildError = ''; - this.buildDialogOpen = true; - } - - private renderBuildDialog() { - return html` -${this.buildError}
` : nothing} -${this.buildLog}
-