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-pathflag
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).
| 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
absoluteLifetimeis an absolute limit measured from the original login that does not reset on rotation. If you increasesessionDurationbeyond Dex'sabsoluteLifetime, 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
sessionDurationdoes not exceed Dex'sabsoluteLifetime.
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:
- If you have a previous session, muster opens the browser with OIDC
prompt=none - If your IdP session is still valid, authentication completes without user interaction
- 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¶
Development Configuration¶
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¶
- Defaults: Built-in default values
- Main Config:
config.yamloverrides defaults - Resource Files: Individual resource definitions
Custom Configuration Path¶
Use --config-path to specify a custom configuration directory:
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:
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¶
- CLI Reference - Command-line interface documentation
- CRDs Reference - Kubernetes resource specifications
- MCP Tools Reference - Available tools and usage
- API Reference - HTTP and MCP API documentation