Skip to content

Configuration reference

Complete reference for configuring muster system settings, resources, and behavior.

Overview

muster uses a file-based configuration system with YAML files organized in a structured directory hierarchy. Configuration is loaded from ~/.config/muster/ by default, or from a custom path specified with --config-path.

Configuration Philosophy

  • Simple: Direct YAML files that are easy to edit
  • Predictable: Standard directory structure with clear separation
  • Flexible: Support for both API-driven and manual file editing

Configuration Directory Structure

~/.config/muster/
├── config.yaml              # Main system configuration
├── mcpservers/              # MCP server definitions
│   ├── kubernetes.yaml
│   ├── github.yaml
│   └── prometheus.yaml
└── workflows/               # Workflow definitions
    ├── deploy-app.yaml
    └── backup-database.yaml

Main Configuration File

Location

  • Default: ~/.config/muster/config.yaml
  • Custom: Specified via --config-path flag

Structure

# ~/.config/muster/config.yaml
aggregator:
  port: 8090                    # Server port (default: 8090)
  host: "localhost"             # Bind address (default: localhost)
  transport: "streamable-http"  # MCP transport (default: streamable-http)
  enabled: true                 # Enable aggregator (default: true)

namespace: "default"            # Kubernetes namespace for CR discovery (default: default)

Configuration Fields Reference

Top-Level Configuration

Field Type Default Description
namespace string "default" Kubernetes namespace for discovering MCPServer and Workflow CRs
kubernetes bool false Enable Kubernetes CRD mode. When true, uses Kubernetes CRDs for resource storage and requires the apiserver: if the Kubernetes client cannot be created at startup (apiserver unreachable, muster CRDs not installed), muster serve retries for about half a minute and then exits with an error instead of falling back to the filesystem, so the kubelet restarts it. When false, uses filesystem YAML files. The Helm chart sets this to true by default.
aggregator AggregatorConfig see below Aggregator service configuration
auth AuthConfig see below Authentication settings for CLI
toolsetPresets map[string]Preset {} Toolset presets an agent's X-muster-Toolset header can name as preset:<name>; read-only, none and full are built in and cannot be redefined. See Toolsets.

Toolset Presets

Presets are named selections of the tool catalogue that a request's X-muster-Toolset header (or the filter_tools toolset argument) references as preset:<name>. They are evaluated against the live catalogue on every request. Each rule sets exactly one of tool, pattern, server, workflow, readOnly: true, preset (composition, include only) or label (presets only; see Toolsets).

toolsetPresets:
  infrastructure:
    description: Every infrastructure server, without deletes
    include:
      - server: mcp-kubernetes
      - server: mcp-prometheus
    exclude:
      - pattern: "*_delete"

read-only, none and full are built in; configuration that redefines one, sets a rule with no key or more than one, uses an invalid pattern, composes an unknown preset or cycles fails muster serve at startup with the preset named. The Helm chart exposes this block as muster.toolsetPresets.

Aggregator Configuration

The aggregator manages the unified MCP interface and tool aggregation.

Field Type Default Description
port int 8090 Port for the aggregator HTTP/WebSocket server
host string "localhost" Host address to bind the server to
transport string "streamable-http" MCP transport protocol
enabled bool true Whether to enable the aggregator service

Transport Options

Transport Description Use Case
streamable-http HTTP with streaming support Recommended - Most compatible
sse Server-Sent Events Real-time updates
stdio Standard I/O Command-line clients

Admin Listener

aggregator.admin starts a small web UI for sessions on a separate listener. It has no authentication of its own, so it is bound to the loopback address and reached with kubectl port-forward or from the host. See HTTP endpoints for the pages it serves.

aggregator:
  admin:
    enabled: true          # default: false
    port: 9999             # default: 9999
    bindAddress: 127.0.0.1 # default: 127.0.0.1; a wider bind exposes an unauthenticated UI

Auth Configuration

Session Duration

The sessionDuration field controls how long a user's session remains valid before re-authentication is required. This sets the server-side refresh token TTL (the muster refresh token's lifetime).

aggregator:
  oauth:
    server:
      sessionDuration: "720h"  # 30 days (default)
Value Duration Notes
720h 30 days Default, aligned with Dex's absoluteLifetime
168h 7 days More restrictive for high-security environments
2160h 90 days Longer sessions (ensure Dex absoluteLifetime matches)

Important: muster uses a rolling refresh token TTL (reset on each token rotation), while Dex's absoluteLifetime is an absolute limit measured from the original login that does not reset on rotation. If you increase sessionDuration beyond Dex's absoluteLifetime, the effective session will still be limited by Dex -- the next token refresh after the absolute lifetime expires will fail, forcing re-authentication even if muster's session estimate shows time remaining.

Always ensure sessionDuration does not exceed Dex's absoluteLifetime.

Access Token TTL

The access token TTL is not directly configurable; it defaults to 30 minutes (DefaultAccessTokenTTL), matching Dex's idTokens expiry. The mcp-oauth library's capTokenExpiry function automatically caps the effective access token lifetime to never exceed the provider's token lifetime, so even if the default were higher, the actual expiry would be capped to what Dex issues.

Access tokens are refreshed automatically using the refresh token -- users do not need to re-authenticate when they expire. The CLI's muster auth status shows the current access token expiry under "Expires" and the session duration under "Session".

For the full token lifecycle and how muster and Dex tokens interact, see the Security Configuration guide.

The CLI's muster auth status displays an approximate session estimate based on the default 30-day duration. Custom server-side values are not yet reflected in the CLI estimate.

Resource Identifier

muster issues only opaque access tokens. muster is not an identity provider: it never signs tokens, and downstream MCP servers validate identity against dex (or their own IdP), never against muster.

Field Type Default Description
resourceIdentifier string "" RFC 8707 canonical URI for this muster instance as a resource server. Access tokens carry this value in their aud claim; tokens bound to a different resource are rejected, preventing replay across resource servers sharing the same IdP. Defaults to baseUrl when empty.

DPoP and Trusted Proxies

Field Type Default Description
trustedProxyCIDRs []string [] CIDRs from which X-Forwarded-Proto and X-Forwarded-Host headers are trusted for DPoP htu URL reconstruction. Required when muster runs behind a reverse proxy that terminates TLS.

RFC 8693 Token Exchange

Field Type Default Description
trustedIssuers []TrustedIssuerConfig [] Trusted external OIDC issuers. Tokens are accepted as id_token, access_token, or jwt subject_tokens. Use allowedClaims to express Kubernetes ServiceAccount or GitHub Actions trust.

TrustedIssuerConfig fields:

Field Type Description
issuer string Expected iss claim value.
jwksUrl string JWKS endpoint. Independent of issuer.
allowedAudiences []string Accepted aud values. Empty accepts any audience.
allowedScopes []string Scope ceiling for tokens from this issuer. Nil means no restriction.
allowedClaims map[string]string Required claim name→pattern pairs. Keys are JWT claim names; values are exact strings or globs where * spans any chars including / and ? matches one char. Absent or non-string claims are rejected. Empty means no restriction.
allowPrivateIPJWKS bool Allow jwksUrl to resolve to a private or loopback address. Required for in-cluster Kubernetes SA trust where the JWKS endpoint is https://kubernetes.default.svc/openid/v1/jwks. Emits a startup warning when set. Default: false.

Brokered Token Exchange (tokenExchangeBroker)

Exposes muster's RFC 8693 token exchange to external confidential clients: a broker client POSTs a token-exchange request with an audience parameter to /oauth/token and receives a token minted by the audience's downstream Dex (instead of a muster-issued JWT). Subject tokens are validated against trustedIssuers, so at least one issuer entry covering the broker client's tokens is required.

Policy enforced by the broker path (mcp-oauth): client authentication is mandatory and only confidential clients are accepted; audiences are gated by the per-client allowlist; no refresh tokens are issued (expires_in is bounded by the downstream token's expiry — clients re-exchange); DPoP is rejected.

Field Type Default Description
clientAudiences map[string][]string {} Per-client audience allowlist: broker client ID → audiences it may request. A miss returns invalid_target.
targets map[string]BrokerTargetConfig {} Audience name (e.g. a cluster name) → downstream target: a Dex exchange (dexTokenEndpoint) or the release of the person's own grant (grantIssuer).
brokerClients map[string]BrokerClientConfig {} Confidential broker clients seeded at startup, keyed by client id: clientCredentialsSecretRef (a Kubernetes Secret with client-id/client-secret) or clientSecretFile (a file holding the secret, for deployments without a Kubernetes Secret store), plus optional informational scopes.
allowPrivateIP bool false Allow downstream token endpoints to resolve to private/loopback IPs. Reduces SSRF protection; internal deployments only.

BrokerTargetConfig fields (Dex exchange target):

Field Type Description
dexTokenEndpoint string Downstream Dex token endpoint URL (HTTPS). Required unless grantIssuer is set.
expectedIssuer string Expected iss claim of the exchanged token. Derived from dexTokenEndpoint when empty.
connectorId string Downstream Dex OIDC connector that trusts the subject token's issuer. Required.
scopes string Space-separated downstream scopes (default: openid profile email groups). Kubernetes-bound audiences must include the Dex cross-client scope for the apiserver's client, e.g. audience:server:client_id:dex-k8s-authenticator — without it the exchanged token's aud is the exchange client only, which the kube-apiserver rejects. The client-supplied RFC 8693 scope parameter is intentionally ignored.
clientCredentialsSecretRef object Kubernetes Secret with the downstream exchange client credentials: name (required), namespace (defaults to the muster namespace), clientIdKey (default client-id), clientSecretKey (default client-secret).

BrokerTargetConfig fields (grant target):

Field Type Description
grantIssuer string HTTPS issuer of an authorization server an MCPServer pins with spec.auth.authorizationServer and grantScope: subject (e.g. https://github.com/login/oauth). The exchange answers the person's own access token from that issuer -- the grant filed under the subject token's sub when the person connected the server -- refreshed first when it is due, with expires_in its remaining lifetime and never the refresh token. A person without a grant gets invalid_target. Mutually exclusive with dexTokenEndpoint; takes none of the exchange fields. See Releasing a person's grant to a trusted relying party.

Example:

aggregator:
  oauth:
    server:
      trustedIssuers:
        - issuer: https://dex.main.example.com
          jwksUrl: https://dex.main.example.com/keys
          allowedAudiences: ["portal-frontend"]
      tokenExchangeBroker:
        clientAudiences:
          portal-backend: ["cluster-a", "github"]
        targets:
          cluster-a:
            dexTokenEndpoint: https://dex.cluster-a.example.com/token
            connectorId: main-dex
            scopes: "openid profile email groups audience:server:client_id:dex-k8s-authenticator"
            clientCredentialsSecretRef:
              name: muster-token-exchange-cluster-a
          github:
            grantIssuer: https://github.com/login/oauth

Private-IP OIDC Discovery (Dex)

By default the SSRF guard rejects an OIDC issuer URL that resolves to a private or loopback address. On clusters where the public Dex hostname resolves to an RFC 1918 address (e.g. an Azure internal load balancer, or air-gapped environments), discovery fails with context deadline exceeded and the server starts in degraded mode.

Field Type Default Description
dex.allowPrivateIPOIDC bool false Allow the Dex issuer URL to resolve to a private/loopback IP during OIDC discovery. This is the discovery-path counterpart to allowPrivateIPJWKS. Emits a CWE-918 startup warning when set; only enable it when the issuer is genuinely fronted by an internal-only load balancer.

Private-IP Token Exchange Endpoints

The same guard applies to the token endpoint of a remote Dex that muster exchanges tokens with (spec.auth.tokenExchange.dexTokenEndpoint on an MCPServer). When that Dex sits behind an internal-only load balancer, the exchange fails with a DNS-rebinding error although the endpoint is reachable.

Field Type Default Description
mcpClient.tokenExchange.allowPrivateIP bool false Allow a remote Dex token endpoint to resolve to a private/loopback IP. Implied by --extra-ca-file (in-cluster TLS endpoints). Emits a startup warning when set; only enable it when the remote Dex is genuinely fronted by an internal-only load balancer. TLS verification is unchanged.

Private-IP CIMD Clients

The SSRF guard also covers Client ID Metadata Documents (CIMD): a client_id that is a URL is fetched by the OAuth server, and by default that URL must not resolve to a private, loopback or link-local address. On a cluster whose own hostnames resolve to an internal load balancer, a CIMD client hosted on the platform itself (klaus-gateway's /auth/slack/client.json, for example) is rejected with invalid_client: ... client_id metadata URL resolves to private/internal IP address. Lift the guard only there.

Field Type Default Description
allowPrivateIPClientMetadata bool false Allow a CIMD client_id URL to resolve to a private/loopback/link-local IP. Emits a startup warning when set. PKCE and redirect-URI validation are unchanged. Helm: muster.oauth.server.allowPrivateIPClientMetadata.

Private-IP Redirect URIs

A client's redirect URI is validated again when the authorization flow starts: a hostname that resolves to a private address is rejected with redirect_uri: hostname resolves to private IP address (DNS rebinding protection), even after the client itself (for example a CIMD client allowed through allowPrivateIPClientMetadata) was accepted. On a cluster whose own hostnames resolve to an internal load balancer, every platform-hosted client's callback trips this check.

Field Type Default Description
allowPrivateIPRedirectURIs bool false Allow redirect URIs that are, or resolve to, private IP addresses. Emits a startup warning when set. Exact redirect-URI matching against the client's registration is unchanged. Helm: muster.oauth.server.allowPrivateIPRedirectURIs.

Silent Re-Authentication (CLI Flag)

Silent re-authentication is controlled via CLI flags only, not configuration file.

By default, muster uses interactive authentication. If your IdP supports OIDC prompt=none (note: Dex does not), you can enable silent re-authentication with the --silent flag:

muster auth login --silent     # Attempt silent re-auth before interactive
muster agent --silent          # Enable silent auth for agent

When --silent is used:

  1. If you have a previous session, muster opens the browser with OIDC prompt=none
  2. If your IdP session is still valid, authentication completes without user interaction
  3. If the IdP session has expired, muster falls back to interactive login

Note: Silent auth is disabled by default because Dex (the default IdP) does not support prompt=none. When silent auth fails with Dex, it causes two browser tabs to open.

Security: When enabled, silent re-authentication maintains full security: - PKCE is enforced on every flow - State parameter prevents CSRF attacks - The IdP validates the session, not muster - Any failure falls back to interactive authentication

Example Configurations

Minimal Configuration

# Uses all defaults (namespace: "default", aggregator defaults)
aggregator: {}

Development Configuration

aggregator:
  port: 8091
  host: "0.0.0.0"
  transport: "streamable-http"
  enabled: true

Production Configuration (Kubernetes)

namespace: "muster-system"      # Use dedicated namespace for muster CRs
kubernetes: true                # Use Kubernetes CRDs instead of filesystem
aggregator:
  port: 80
  host: "0.0.0.0"
  transport: "streamable-http"
  enabled: true

Multi-Tenant Configuration

namespace: "team-alpha"         # Each team uses their own namespace
aggregator:
  port: 8090
  host: "localhost"
  transport: "streamable-http"

Writes-as-Caller

In Kubernetes mode, session-initiated MCPServer spec mutations (core_mcpserver_create / _update / _delete) are written against the Kubernetes API with the caller's own OIDC id_token as the bearer instead of muster's ServiceAccount: the apiserver authenticates the real user, Kubernetes RBAC authorizes the write, and the audit log records the true subject. Reads, core_mcpserver_validate, and muster's own controller writes (status reconciliation, service registration) are unaffected.

Service lifecycle actions on MCPServer-backed services are covered the same way: core_service_stop writes spec.suspended: true, core_service_start clears it (and also writes spec.restartRequestedAt when the service is down), and core_service_restart writes spec.restartRequestedAt — all as the caller, with the reconciler performing the actual start/stop/restart. Lifecycle of muster's own internal services (e.g. the aggregator) keeps the imperative path.

Workflow spec mutations (core_workflow_create / _update / _delete) go through the same gate: the write happens with the caller's identity, and the chart ships a workflow-editor Role/RoleBinding mirroring mcpserver-editor (bound to system:authenticated by default; narrow rbac.workflowEditor.subjects for admin-only workflow authoring). core_workflow_validate, reads, and workflow execution are unaffected.

Caller-identity writes are always active in Kubernetes mode (kubernetes: true); in filesystem mode there is no apiserver and mutations write through the local client. The only knob is the audience override:

writesAsCaller:
  kubernetesAudience: "dex-k8s-authenticator"  # Audience the session token must carry (default shown)

Preconditions: the login scope set must request the kubernetesAudience cross-client audience, and the apiserver must trust the OIDC issuer. Sessions whose token lacks the audience receive an actionable re-login error; callers without RBAC permission receive a permission error naming the missing verb, resource, and namespace.

MCP Server Configuration

MCP servers can be configured through YAML files or Kubernetes CRDs. Each server requires:

# Local server example
mcpservers:
  - name: filesystem-tools
    description: File system operations
    toolPrefix: fs
    type: stdio              # Server execution type
    autoStart: true
    command: ["npx", "@modelcontextprotocol/server-filesystem", "/workspace"]
    env:
      DEBUG: "1"

  # Streamable HTTP server example
  - name: api-server
    description: Remote API tools
    toolPrefix: api
    type: streamable-http
    url: "https://api.example.com/mcp"
    timeout: 30
    headers:
      Authorization: "Bearer token"

  # SSE server example
  - name: sse-server
    description: SSE-based MCP server
    toolPrefix: sse
    type: sse
    url: "https://api.example.com/sse"
    timeout: 45

MCP Server Fields

Field Type Required Default Description
name string - Unique server identifier
description string - Human-readable description
toolPrefix string - Tool name prefix
type string - Server type (stdio, streamable-http, or sse)
autoStart boolean false Auto-start server (stdio only)
command []string ✅* - Command and args (*required for stdio)
args []string - Command arguments (stdio only)
env map[string]string {} Environment variables
url string ✅* - Server URL (*required for streamable-http and sse)
timeout integer 30 Connection timeout in seconds
headers map[string]string {} HTTP headers (streamable-http and sse only)

Workflow Configuration

Location: workflows/*.yaml

apiVersion: muster.giantswarm.io/v1alpha1
kind: Workflow
metadata:
  name: deploy-application
  namespace: default
spec:
  description: "Deploy application with health checks"
  args:                          # Workflow arguments
    appName:
      type: string
      required: true
      description: "Application name to deploy"
    environment:
      type: string
      default: "staging"
      description: "Target environment"
  steps:                         # Workflow steps
    - id: "build"
      tool: "build_application"
      args:
        name: "{{ .input.appName }}"
        env: "{{ .input.environment }}"
      store: true
      description: "Build the application"

    - id: "deploy"
      tool: "deploy_application"
      args:
        name: "{{ .input.appName }}"
        image: "{{ .results.build.image }}"
        env: "{{ .input.environment }}"
      condition:
        fromStep: "build"
        expect:
          success: true
      description: "Deploy to target environment"

    - id: "health-check"
      tool: "health_check"
      args:
        url: "{{ .results.deploy.url }}"
      allowFailure: false
      description: "Verify deployment health"

Workflow Fields

Field Type Required Description
description string Human-readable description
args map[string]ArgDefinition Workflow argument schema
steps []WorkflowStep Sequence of execution steps

Workflow Step Fields

Field Type Required Description
id string Unique step identifier
tool string Tool name to execute
args map[string]any Tool arguments (supports templating)
condition WorkflowCondition Execution condition
store bool Store result for later steps
allowFailure bool Continue on failure
outputs map[string]any Output mappings
description string Step documentation

Configuration Loading

Loading Order

  1. Defaults: Built-in default values
  2. Main Config: config.yaml overrides defaults
  3. Resource Files: Individual resource definitions

Custom Configuration Path

Use --config-path to specify a custom configuration directory:

muster serve --config-path /etc/muster
muster list mcpserver --config-path ./project-config

Environment-Specific Configuration

Create different configuration directories for different environments:

# Development
muster serve --config-path ~/.config/muster-dev

# Staging
muster serve --config-path ~/.config/muster-staging

# Production
muster serve --config-path /etc/muster-prod

Configuration Validation

Automatic Validation

muster validates configuration on startup and when resources are created:

  • Syntax: YAML syntax validation
  • Schema: Field types and required values
  • References: Tool availability and dependencies

Manual Validation

Check resource availability:

# Check specific resources
muster check mcpserver kubernetes
muster check workflow deploy-app

Templating

Template Syntax

Use Go template syntax for dynamic values. Workflow inputs are under .input, stored step results under .results (the engine renders with missingkey=error, so a bare {{ .environment }} errors at runtime):

args:
  url: "https://{{ .input.environment }}.example.com"
  replicas: "{{ .input.replicas }}"
  config: "{{ .input.baseConfig }}/{{ .input.serviceName }}"

Available Variables

In workflow templates, these context roots are available:

Context Reference Description
Workflow inputs .input.<arg> Values passed to the workflow
Step results .results.<step-id> Output of a previous store: true step (.context.<step-id> is an alias)
Loop/user variables .vars.<name> forEach item/index and other bound variables

CLI Commands

Available Commands

Command Description
muster serve Start the muster aggregator server
muster agent MCP client for the aggregator server
muster create Create resources (service, workflow)
muster get Get detailed information about resources
muster list List resources
muster start Start services or execute workflows
muster stop Stop services
muster check Check resource availability
muster test Execute test scenarios

Resource Types

Resource Type Create Get List Check Start
service
mcpserver
workflow
workflow-execution

See Also