config

package
v0.1.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 10, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExpandStackVarsWithEnv

func ExpandStackVarsWithEnv(s *Stack)

ExpandStackVarsWithEnv expands environment variable references in stack fields.

func ExpandString

func ExpandString(s string, resolve Resolver) (expanded string, unresolvedVault []string, emptyEnvVars []string)

ExpandString expands variable references in a string using the given resolver. All patterns are matched in a single pass to prevent double-expansion of values that contain dollar signs.

Returns the expanded string, any unresolved vault references, and env vars that resolved to empty.

func ExpandStringRefs

func ExpandStringRefs(s string, resolve Resolver) (expanded string, storeRefs []string, unresolvedVault []string, emptyEnvVars []string)

ExpandStringRefs is ExpandString plus storeRefs: the variable-store keys (the KEY in ${var:KEY} / ${vault:KEY}) referenced by s, in first-seen order.

storeRefs is captured from the parsed grammar *before* resolution, so it records what s references regardless of whether each key resolves — this is the basis for usage tracing and is why the index can never drift from what expansion actually recognizes. Bare $VAR / ${VAR} env-style references are NOT store references and are deliberately excluded.

func SupportedLinkClientsForTest

func SupportedLinkClientsForTest() []string

SupportedLinkClientsForTest exposes the copied slug list to the external parity test (links_parity_test.go), mirroring NormalizedClientModelKeyForTest. Not for production use.

func Validate

func Validate(s *Stack) error

Validate checks the stack configuration for errors.

func ValidateStackFile

func ValidateStackFile(path string) (*Stack, *ValidationResult, error)

ValidateStackFile loads a stack file and validates it without deploying. Returns the parsed stack (for further use) and the validation result.

Types

type AuthConfig

type AuthConfig struct {
	// Type is the auth mechanism: "bearer" or "api_key".
	Type string `yaml:"type"`
	// Token is the expected token value (supports env var references via $VAR or ${VAR}).
	Token string `yaml:"token"`
	// Header is the header name for api_key auth (default: "Authorization").
	Header string `yaml:"header,omitempty"`
}

AuthConfig configures gateway authentication. When configured, all requests (except /health and /ready) must include a valid token.

type AutoscaleConfig

type AutoscaleConfig struct {
	// Min is the minimum number of healthy replicas to maintain.
	// >= 0. Must be >= 1 when IdleToZero is false.
	Min int `yaml:"min" json:"min"`
	// Max is the upper bound on replica count. >= 1, >= Min, <= 32.
	Max int `yaml:"max" json:"max"`
	// TargetInFlight is the per-replica in-flight request count the scaler
	// tries to hold the median at or below. >= 1.
	TargetInFlight int `yaml:"target_in_flight" json:"target_in_flight"`
	// ScaleUpAfter is how long the window median must exceed the target
	// before spawning a replica. Default 30s. Minimum 10s.
	ScaleUpAfter string `yaml:"scale_up_after,omitempty" json:"scale_up_after,omitempty"`
	// ScaleDownAfter is how long the window median must be below the target
	// before reaping a replica. Default 5m. Minimum 1m.
	ScaleDownAfter string `yaml:"scale_down_after,omitempty" json:"scale_down_after,omitempty"`
	// WarmPool keeps this many extra idle-ready replicas above the load-derived
	// target at all times. Default 0. Must satisfy Min + WarmPool <= Max.
	WarmPool int `yaml:"warm_pool,omitempty" json:"warm_pool,omitempty"`
	// IdleToZero allows the scaler to reap every replica after a sustained
	// idle. Min may be 0 only when IdleToZero is true. Default false.
	IdleToZero bool `yaml:"idle_to_zero,omitempty" json:"idle_to_zero,omitempty"`
}

AutoscaleConfig controls reactive autoscaling of a ReplicaSet. All fields are optional at the YAML layer only in the sense that SetDefaults fills missing timings; Min, Max, and TargetInFlight are required for the block to validate.

func (*AutoscaleConfig) ResolvedScaleDownAfter

func (a *AutoscaleConfig) ResolvedScaleDownAfter() time.Duration

ResolvedScaleDownAfter parses ScaleDownAfter; returns 5m when unset or invalid.

func (*AutoscaleConfig) ResolvedScaleUpAfter

func (a *AutoscaleConfig) ResolvedScaleUpAfter() time.Duration

ResolvedScaleUpAfter parses ScaleUpAfter; returns 30s when unset or invalid.

type ClientProfile

type ClientProfile struct {
	// Aliases are raw clientInfo.name values that should resolve to this
	// profile, for reconciling a wire identity that differs from the profile
	// key without relying on the built-in normalization heuristic.
	Aliases []string `yaml:"aliases,omitempty"`
	// Servers is an allow-list of MCP server names. Empty means all servers.
	Servers []string `yaml:"servers,omitempty"`
	// Tools is an allow-list of prefixed tool names. Empty means all tools
	// within the allowed servers.
	Tools []string `yaml:"tools,omitempty"`
}

ClientProfile is one client's tool access allow-list. Servers and Tools are both allow-lists; an empty Servers list means "all servers" and an empty Tools list means "all tools within the allowed servers". Tools are matched against the router's prefixed names (e.g. "github__search-repos").

type ClientsConfig

type ClientsConfig struct {
	// Default is the policy for clients that match no profile: "deny" (the
	// default when empty) or "allow".
	Default string `yaml:"default,omitempty"`
	// Profiles maps a stable client identifier to its access allow-list.
	Profiles map[string]ClientProfile `yaml:"profiles,omitempty"`
}

ClientsConfig is the optional top-level per-client access scoping block. Its presence opts a stack into NetworkPolicy semantics:

  • Omitting the entire `clients:` block preserves legacy behavior — every connecting client sees every tool (Article IX back-compat).
  • With the block present, a connecting client that matches a profile is restricted to that profile's allow-list; a client matching no profile is governed by Default ("deny" unless set to "allow").

The map key in Profiles is the stable client identifier assigned at `gridctl link` time, which is also the identifier shown in the UI and carried on the wire (the `client` query parameter / X-Gridctl-Client-Id header). It is reconciled with the connecting client's normalized identity, so the same string keys configuration, enforcement, and the Stack view.

Scope coverage for v1 is tools only: skills (served as MCP prompts) and resources remain globally visible. This is an explicit, documented decision; extending scope to prompts/resources is deferred.

type Consumer

type Consumer struct {
	Kind  ReferenceKind `json:"kind"`
	Name  string        `json:"name,omitempty"`
	Field string        `json:"field"`

	// Target names the workload a scoped secrets.sets entry injects into, and
	// TargetKind says whether that is a server or a resource. Both are set only
	// on RefKindSecretsSet consumers built from a scoped set: one such consumer
	// is synthesized per receiving workload, so the UI can name and navigate to
	// each one. An unscoped set fans out to everything and produces a single
	// consumer with both fields empty.
	//
	// Name keeps holding the set name in every case. Callers that ask "is this
	// variable's own set actively injected" compare against Name, so widening
	// that field to mean the workload would silently break them.
	Target     string        `json:"target,omitempty"`
	TargetKind ReferenceKind `json:"targetKind,omitempty"`
}

Consumer is a single site that references a variable: the kind of stack element, its name (server/resource/network name; empty for stack- and gateway-level sites), and the field where the reference appears.

Field mirrors the YAML key path the user actually wrote (e.g. "env.GITHUB_TOKEN", "image", "command[2]", "ssh.identityFile", "openapi.baseUrl") so it can be used verbatim to locate the reference in the stack file. Casing therefore tracks the schema's own YAML tags, which mix camelCase (identityFile, baseUrl) and snake_case (build_args, ssh_key_path).

type DependencyStatus

type DependencyStatus struct {
	Status  string   `json:"status"` // "resolved", "missing"
	Missing []string `json:"missing,omitempty"`
}

DependencyStatus summarizes skill dependency resolution.

type DiffAction

type DiffAction string

DiffAction describes what changed.

const (
	DiffAdd    DiffAction = "add"
	DiffRemove DiffAction = "remove"
	DiffChange DiffAction = "change"
)

type DiffItem

type DiffItem struct {
	Action  DiffAction `json:"action"`
	Kind    string     `json:"kind"` // "mcp-server", "agent", "resource", "a2a-agent", "gateway", "network"
	Name    string     `json:"name"`
	Details []string   `json:"details,omitempty"` // human-readable change descriptions
}

DiffItem represents a single change in the plan.

type DriftStatus

type DriftStatus struct {
	Status  string   `json:"status"` // "in-sync", "drifted", "unknown"
	Added   []string `json:"added,omitempty"`
	Removed []string `json:"removed,omitempty"`
	Changed []string `json:"changed,omitempty"`
}

DriftStatus summarizes drift between spec and running state.

type GatewayConfig

type GatewayConfig struct {
	// Name overrides the identity the gateway announces to MCP clients in the
	// initialize response (serverInfo.name). Some clients (VS Code / GitHub
	// Copilot) display this value rather than the entry key from their own
	// config file, so distinct gateways need distinct names to be told apart.
	// Empty keeps the default "gridctl-gateway".
	Name string `yaml:"name,omitempty" json:"name,omitempty"`

	// AllowedOrigins lists origins for CORS.
	// When not set, defaults to ["*"] (allow all) for backward compatibility.
	// Set explicit origins to restrict cross-origin access.
	AllowedOrigins []string `yaml:"allowed_origins,omitempty"`

	// Bind is the address the gateway's HTTP listener binds. When not set,
	// defaults to 127.0.0.1 (loopback only), so the API, web UI, and gateway
	// are unreachable from other hosts. Set "0.0.0.0" to listen on every
	// interface; the --bind and --bind-all flags override this field.
	// Widening the bind without setting auth logs a warning at startup.
	Bind string `yaml:"bind,omitempty"`

	// InsecureAllowUnauthenticated permits a non-loopback bind with no auth
	// configured. Without it gridctl refuses to start in that combination.
	// Exists alongside the --insecure-allow-unauthenticated flag because a
	// flag can be dropped by whatever wraps the process; a config field
	// survives that.
	InsecureAllowUnauthenticated bool `yaml:"insecure_allow_unauthenticated,omitempty"`

	// AllowedHosts lists additional Host header values accepted on the MCP
	// endpoint when a request arrives over loopback. Loopback hosts
	// (localhost, 127.0.0.0/8, ::1) are always accepted, so the default of
	// unset means loopback-only and needs no configuration. Set this only
	// when a reverse proxy or container hostname fronts the gateway.
	AllowedHosts []string    `yaml:"allowed_hosts,omitempty"`
	Auth         *AuthConfig `yaml:"auth,omitempty"`

	// CodeMode controls whether the gateway replaces individual tool definitions
	// with two meta-tools (search + execute). Values: "off" (default), "on".
	CodeMode string `yaml:"code_mode,omitempty"`
	// CodeModeTimeout is the execution timeout in seconds (default: 30).
	CodeModeTimeout int `yaml:"code_mode_timeout,omitempty"`

	// OutputFormat sets the default output format for tool call results.
	// Values: "json" (default), "toon", "csv", "text".
	// Per-server output_format overrides this value.
	OutputFormat string `yaml:"output_format,omitempty"`

	// MaxToolResultBytes sets the maximum size of a tool result in bytes before truncation.
	// Results exceeding this limit are truncated with a suffix indicating the original size.
	// Default: 65536 (64KB). Set to 0 to use the default.
	MaxToolResultBytes int `yaml:"maxToolResultBytes,omitempty" json:"maxToolResultBytes,omitempty"`

	// Tracing configures distributed tracing. When nil, tracing is enabled with defaults.
	Tracing *TracingConfig `yaml:"tracing,omitempty" json:"tracing,omitempty"`

	// Security configures security features such as schema pinning. When nil, defaults apply.
	Security *GatewaySecurityConfig `yaml:"security,omitempty" json:"security,omitempty"`

	// Tokenizer selects the token counting strategy.
	// Values: "embedded" (default) uses the cl100k_base BPE vocabulary (pure Go, no network).
	// "api" uses Anthropic's count_tokens endpoint for exact counts — Anthropic-specific,
	// requires network access and an API key, wrong for non-Anthropic model routing.
	Tokenizer string `yaml:"tokenizer,omitempty"`
	// TokenizerAPIKey overrides ANTHROPIC_API_KEY for the api tokenizer mode.
	// When unset, the api tokenizer falls back to the ANTHROPIC_API_KEY environment variable.
	TokenizerAPIKey string `yaml:"tokenizer_api_key,omitempty"`
}

GatewayConfig holds optional gateway-level configuration.

type GatewaySecurityConfig

type GatewaySecurityConfig struct {
	// SchemaPinning configures TOFU schema pinning for MCP tool definitions.
	SchemaPinning *SchemaPinningConfig `yaml:"schema_pinning,omitempty" json:"schema_pinning,omitempty"`
}

GatewaySecurityConfig holds gateway-level security settings.

type GroupConfig

type GroupConfig struct {
	Description string `yaml:"description,omitempty" json:"description,omitempty"`
	// Servers includes every tool of the named stack servers.
	Servers []string `yaml:"servers,omitempty" json:"servers,omitempty"`
	// Tools includes specific prefixed tool names ("github__create_issue").
	Tools []string `yaml:"tools,omitempty" json:"tools,omitempty"`
	// Exclude subtracts prefixed tool names, applied after inclusion.
	Exclude []string `yaml:"exclude,omitempty" json:"exclude,omitempty"`
	// Overrides customizes individual member tools, keyed by canonical
	// prefixed name. Renames and rewrites exist only at this group's
	// exposure boundary; dispatch, scoping, limits, pins, and telemetry
	// always operate on canonical names.
	Overrides map[string]GroupOverride `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}

GroupConfig is one entry of the optional top-level `groups:` block: a named cross-server tool bundle served at its own MCP endpoint (/groups/{name}/mcp). Groups are the curation axis; per-client scoping (`clients:`) remains the access axis and still applies on group sessions. Membership resolves as: all tools of Servers, plus Tools, minus Exclude (exclusion always last). Omitting the whole block preserves legacy behavior (Article IX): no group endpoints exist and /mcp is unchanged.

type GroupOverride

type GroupOverride struct {
	// Name renames the tool at the exposure boundary (a flat alias, no
	// "__"). The canonical name still routes and is still accepted on call.
	Name string `yaml:"name,omitempty" json:"name,omitempty"`
	// Description replaces the tool's description verbatim. Empty keeps
	// the original.
	Description     string `yaml:"description,omitempty" json:"description,omitempty"`
	ReadOnlyHint    *bool  `yaml:"read_only_hint,omitempty" json:"read_only_hint,omitempty"`
	DestructiveHint *bool  `yaml:"destructive_hint,omitempty" json:"destructive_hint,omitempty"`
	IdempotentHint  *bool  `yaml:"idempotent_hint,omitempty" json:"idempotent_hint,omitempty"`
	OpenWorldHint   *bool  `yaml:"open_world_hint,omitempty" json:"open_world_hint,omitempty"`
}

GroupOverride customizes one member tool of a group. Hint fields are pointers: nil passes the downstream server's own annotation through, a set value overrides it. An operator-set hint is the operator vouching for the tool's behavior to clients that consume annotations.

type IssueSeverity

type IssueSeverity string

IssueSeverity represents the severity level of a validation issue.

const (
	SeverityError   IssueSeverity = "error"
	SeverityWarning IssueSeverity = "warning"
	SeverityInfo    IssueSeverity = "info"
)

type LimitsConfig

type LimitsConfig struct {
	RateLimits []RateLimit `yaml:"rate_limits,omitempty" json:"rate_limits,omitempty"`
}

LimitsConfig is the optional top-level `limits:` block: declarative rate limits enforced on the tool-call dispatch path. Omitting the block preserves legacy behavior — nothing is ever limited (Article IX).

Entries scope to exactly one of client, server, or tool. The client key is the stable client identifier used by clients.profiles; server is the stack server name; tool is the router's prefixed name ("github__search_code").

type LinkEntry

type LinkEntry struct {
	Client   string `yaml:"client" json:"client"`
	Group    string `yaml:"group,omitempty" json:"group,omitempty"`
	ClientID string `yaml:"client_id,omitempty" json:"client_id,omitempty"`
	Name     string `yaml:"name,omitempty" json:"name,omitempty"`
}

LinkEntry is one declared client connection in the optional top-level `link:` block. The block lists LLM clients that `gridctl apply` should link to this stack's gateway once it is healthy. Reconciliation is additive and idempotent: declared clients are linked if installed, already-linked clients are a no-op, and removing an entry never unlinks anything (removal stays explicit via `gridctl unlink` or `gridctl destroy --unlink`). Omitting the block preserves legacy behavior: nothing is auto-linked.

An entry is either a bare client slug ("- claude-code") or a mapping:

link:
  - claude
  - client: cursor
    group: dev        # link the group endpoint; entry name defaults to gridctl-dev
    client_id: cursor # stable identifier for per-client access scoping
    name: gridctl     # server entry name override in the client config

func (LinkEntry) EffectiveName

func (e LinkEntry) EffectiveName() string

EffectiveName resolves the server entry name this link writes into the client config: an explicit name wins, a group link defaults to "gridctl-<group>" (matching `gridctl link --group`), everything else uses "gridctl".

func (LinkEntry) IsShorthand

func (e LinkEntry) IsShorthand() bool

IsShorthand reports whether the entry carries nothing beyond the client slug, so YAML emitters can round-trip it back to the scalar form.

func (*LinkEntry) UnmarshalYAML

func (e *LinkEntry) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts the scalar shorthand ("- cursor") or the mapping form. Any other node kind is rejected so a stray nested sequence fails loudly instead of decoding to an empty entry.

type LoadOption

type LoadOption func(*loadConfig)

LoadOption configures LoadStack behavior.

func WithVault

func WithVault(v VaultLookup) LoadOption

WithVault enables ${vault:KEY} resolution during stack loading.

func WithVaultSets

func WithVaultSets(v VaultSetLookup) LoadOption

WithVaultSets enables secrets.sets injection during stack loading.

type LoggingConfig

type LoggingConfig struct {
	// File is the path to the log file. When set, logs are written to both the
	// in-memory ring buffer (web UI) and this file simultaneously.
	File string `yaml:"file,omitempty" json:"file,omitempty"`
	// MaxSizeMB is the maximum log file size in megabytes before rotation (default: 100).
	MaxSizeMB int `yaml:"maxSizeMB,omitempty" json:"maxSizeMB,omitempty"`
	// MaxAgeDays is the maximum number of days to retain old log files (default: 7).
	MaxAgeDays int `yaml:"maxAgeDays,omitempty" json:"maxAgeDays,omitempty"`
	// MaxBackups is the maximum number of compressed old log files to keep (default: 3).
	MaxBackups int `yaml:"maxBackups,omitempty" json:"maxBackups,omitempty"`
}

LoggingConfig configures log file output with automatic rotation.

type MCPServer

type MCPServer struct {
	Name         string            `yaml:"name"`
	Image        string            `yaml:"image,omitempty"`
	Source       *Source           `yaml:"source,omitempty"`
	URL          string            `yaml:"url,omitempty"`       // External server URL (no container)
	Port         int               `yaml:"port,omitempty"`      // For HTTP transport (container-based)
	Transport    string            `yaml:"transport,omitempty"` // "http" (default), "stdio", or "sse"
	Command      []string          `yaml:"command,omitempty"`   // Override container command or remote command for SSH
	Env          map[string]string `yaml:"env,omitempty"`
	BuildArgs    map[string]string `yaml:"build_args,omitempty"`
	Network      string            `yaml:"network,omitempty"`       // Network to join (for multi-network mode)
	SSH          *SSHConfig        `yaml:"ssh,omitempty"`           // SSH connection config for remote servers
	OpenAPI      *OpenAPIConfig    `yaml:"openapi,omitempty"`       // OpenAPI spec config for API-backed servers
	Tools        []string          `yaml:"tools,omitempty"`         // Tool whitelist (empty = all tools exposed)
	OutputFormat string            `yaml:"output_format,omitempty"` // Output format override: "json", "toon", "csv", "text"
	PinSchemas   *bool             `yaml:"pin_schemas,omitempty"`   // Override gateway schema pinning for this server (nil = inherit)
	// ReadyTimeout overrides the HTTP/SSE readiness wait for container-based servers.
	// Accepts any time.Duration string (e.g. "60s", "2m"). Empty/"0" inherits the gateway default (30s).
	// Ignored for stdio, local process, SSH, OpenAPI, and external transports.
	ReadyTimeout string `yaml:"ready_timeout,omitempty"`

	// PingTimeout overrides the per-ping deadline used by the gateway health monitor.
	// Accepts any time.Duration string (e.g. "10s"). Empty/"0" inherits DefaultPingTimeout (5s).
	// Tune this for slow upstreams (e.g. HTTP servers with many tools) where the
	// 5s default can flake under autoscale spawn load.
	PingTimeout string `yaml:"ping_timeout,omitempty"`

	// ProtocolGeneration overrides MCP protocol-generation resolution for
	// this server: "auto" (default, same as empty) probes server/discover
	// and falls back to the initialize handshake; "handshake" and
	// "stateless" skip the probe and force one generation. An escape
	// hatch for peers the probe misclassifies; absent means today's
	// auto-negotiation exactly.
	ProtocolGeneration string `yaml:"protocol_generation,omitempty"`

	// Replicas is the number of independent processes to spawn for this server.
	// Defaults to 1. Values >1 load-balance JSON-RPC tool calls across replicas
	// using ReplicaPolicy. Not supported for external URL or OpenAPI transports.
	Replicas int `yaml:"replicas,omitempty" json:"replicas,omitempty"`

	// ReplicaPolicy selects the dispatch policy when Replicas > 1.
	// Valid values: "round-robin" (default), "least-connections".
	ReplicaPolicy string `yaml:"replica_policy,omitempty" json:"replica_policy,omitempty"`

	// Autoscale, when set, replaces the static Replicas count with reactive
	// autoscaling bounded by Min and Max. Mutually exclusive with Replicas.
	// Not supported on external URL or OpenAPI transports.
	Autoscale *AutoscaleConfig `yaml:"autoscale,omitempty" json:"autoscale,omitempty"`

	// Telemetry, when set, overrides stack-global telemetry persistence for
	// this server. nil fields inherit; *bool fields explicitly opt in or out.
	Telemetry *MCPServerTelemetry `yaml:"telemetry,omitempty" json:"telemetry,omitempty"`

	// Auth configures downstream authentication for external URL servers:
	// a static bearer token, a static custom header, or OAuth 2.1 brokering
	// handled by the gateway. nil (the default) preserves the existing
	// unauthenticated behavior. Only valid on external URL servers.
	Auth *ServerAuth `yaml:"auth,omitempty" json:"auth,omitempty"`
}

MCPServer defines an MCP server (container-based or external).

func (*MCPServer) IsContainerBased

func (s *MCPServer) IsContainerBased() bool

IsContainerBased returns true if this MCP server requires a container runtime.

func (*MCPServer) IsExternal

func (s *MCPServer) IsExternal() bool

IsExternal returns true if this is an external MCP server (URL-only, no container).

func (*MCPServer) IsLocalProcess

func (s *MCPServer) IsLocalProcess() bool

IsLocalProcess returns true if this is a local process MCP server (command-only, no container).

func (*MCPServer) IsOpenAPI

func (s *MCPServer) IsOpenAPI() bool

IsOpenAPI returns true if this is an OpenAPI-based MCP server.

func (*MCPServer) IsSSH

func (s *MCPServer) IsSSH() bool

IsSSH returns true if this is an SSH-based MCP server (ssh config with command).

func (*MCPServer) PersistLogs

func (s *MCPServer) PersistLogs(stack *Stack) bool

PersistLogs reports whether log persistence is effectively enabled for this server. An explicit per-server *bool override wins; otherwise the stack- global default is returned. Returns false when both stack and server are nil.

func (*MCPServer) PersistMetrics

func (s *MCPServer) PersistMetrics(stack *Stack) bool

PersistMetrics — see PersistLogs for inheritance semantics.

func (*MCPServer) PersistTraces

func (s *MCPServer) PersistTraces(stack *Stack) bool

PersistTraces — see PersistLogs for inheritance semantics.

func (*MCPServer) ResolvedPingTimeout

func (s *MCPServer) ResolvedPingTimeout() time.Duration

ResolvedPingTimeout parses PingTimeout; returns 0 when unset or invalid so the gateway falls back to DefaultPingTimeout (5s).

func (*MCPServer) ResolvedReadyTimeout

func (s *MCPServer) ResolvedReadyTimeout() time.Duration

ResolvedReadyTimeout parses ReadyTimeout; returns 0 when unset or invalid so the gateway falls back to its default.

type MCPServerPersistence

type MCPServerPersistence struct {
	Logs    *bool `yaml:"logs,omitempty" json:"logs,omitempty"`
	Metrics *bool `yaml:"metrics,omitempty" json:"metrics,omitempty"`
	Traces  *bool `yaml:"traces,omitempty" json:"traces,omitempty"`
}

MCPServerPersistence is the *bool tri-state mirror of TelemetryPersistence.

type MCPServerTelemetry

type MCPServerTelemetry struct {
	Persist MCPServerPersistence `yaml:"persist,omitempty" json:"persist,omitempty"`
}

MCPServerTelemetry holds per-server telemetry persistence overrides. Each *bool field uses tri-state semantics: nil = inherit stack-global, &true = explicitly persist, &false = explicitly do not persist (overrides stack global). Never default these to &false in SetDefaults — that would collapse inherit and explicit-off into the same value.

type ModelPreferenceScope

type ModelPreferenceScope struct {
	// Rewrite opts the scope into projection rewrite: projected files
	// carry the resolved preference, and affected skill projections are
	// forced to copy channel (status names the reason). Default false =
	// pure pass-through.
	Rewrite bool `yaml:"rewrite,omitempty" json:"rewrite,omitempty"`
	// Default applies where the author declared nothing.
	Default string `yaml:"default,omitempty" json:"default,omitempty"`
	// Overrides maps exact registry names (case-sensitive, no globs) to
	// the preference applied regardless of the author's declaration.
	// Unknown names warn at validate time but never error: the skill may
	// arrive later via pack.
	Overrides map[string]string `yaml:"overrides,omitempty" json:"overrides,omitempty"`
}

ModelPreferenceScope is one kind's model preference policy.

Resolution order: Overrides beats the author's declaration beats Default. Overrides apply in either direction (raising to a stronger model or lowering to a cheaper one). With Rewrite false (the default), the scope is surfacing-only: nothing on disk changes.

func (*ModelPreferenceScope) Compile

Compile converts one model_preferences scope into the policy form the projection engines consume. A nil scope compiles to a non-nil empty policy: on a loaded stack, an absent scope is a KNOWN absence (the documented off switch, reconciling rewritten projections back to pass-through), never the unknown-policy preserve state, which is reserved for call sites with no stack at all.

type ModelPreferencesConfig

type ModelPreferencesConfig struct {
	// Skills is the policy for skill projections (pkg/skillsync).
	Skills *ModelPreferenceScope `yaml:"skills,omitempty" json:"skills,omitempty"`
	// Agents is the policy for agent projections (pkg/agentsync).
	Agents *ModelPreferenceScope `yaml:"agents,omitempty" json:"agents,omitempty"`
}

ModelPreferencesConfig is the optional top-level `model_preferences:` block. Each scope carries a projection-time model preference policy for its kind. A preference is a durable default the client may still override (env vars and per-invocation parameters outrank projected frontmatter); nothing here enforces, measures, or costs anything.

type Network

type Network struct {
	Name   string `yaml:"name"`
	Driver string `yaml:"driver"`
}

Network defines the Docker network configuration.

type OpenAPIAuth

type OpenAPIAuth struct {
	Type     string `yaml:"type"`               // "bearer", "header", "query", "oauth2", or "basic"
	TokenEnv string `yaml:"tokenEnv,omitempty"` // Env var name containing bearer token (for type: bearer)
	Header   string `yaml:"header,omitempty"`   // Header name (for type: header, e.g., "X-API-Key")
	ValueEnv string `yaml:"valueEnv,omitempty"` // Env var name containing header value (for type: header or query)

	// Query param auth (type: query)
	ParamName string `yaml:"paramName,omitempty"` // Query parameter name (for type: query)

	// OAuth2 client credentials (type: oauth2)
	ClientIdEnv     string   `yaml:"clientIdEnv,omitempty"`     // Env var name containing OAuth2 client ID
	ClientSecretEnv string   `yaml:"clientSecretEnv,omitempty"` // Env var name containing OAuth2 client secret
	TokenUrl        string   `yaml:"tokenUrl,omitempty"`        // OAuth2 token endpoint URL
	Scopes          []string `yaml:"scopes,omitempty"`          // OAuth2 scopes to request

	// Basic auth (type: basic)
	UsernameEnv string `yaml:"usernameEnv,omitempty"` // Env var name containing username
	PasswordEnv string `yaml:"passwordEnv,omitempty"` // Env var name containing password
}

OpenAPIAuth defines authentication for OpenAPI HTTP requests.

type OpenAPIConfig

type OpenAPIConfig struct {
	Spec       string            `yaml:"spec"`                 // URL or local file path to OpenAPI spec (JSON or YAML)
	BaseURL    string            `yaml:"baseUrl,omitempty"`    // Override the server URL from the spec
	Auth       *OpenAPIAuth      `yaml:"auth,omitempty"`       // Authentication configuration
	TLS        *OpenAPITLS       `yaml:"tls,omitempty"`        // TLS/mTLS configuration (transport-layer)
	Operations *OperationsFilter `yaml:"operations,omitempty"` // Filter which operations become tools
}

OpenAPIConfig defines an MCP server backed by an OpenAPI specification. The spec is parsed and each operation becomes an MCP tool.

type OpenAPITLS

type OpenAPITLS struct {
	CertFile           string `yaml:"certFile,omitempty"`           // Client certificate file path (required for mTLS)
	KeyFile            string `yaml:"keyFile,omitempty"`            // Client private key file path (required for mTLS)
	CaFile             string `yaml:"caFile,omitempty"`             // Custom CA certificate file path
	InsecureSkipVerify bool   `yaml:"insecureSkipVerify,omitempty"` // Skip server certificate verification (dangerous)
}

OpenAPITLS defines TLS/mTLS configuration for OpenAPI HTTP connections. This is transport-layer config and can be combined with any auth type.

type OperationsFilter

type OperationsFilter struct {
	Include []string `yaml:"include,omitempty"` // Operation IDs to include (whitelist)
	Exclude []string `yaml:"exclude,omitempty"` // Operation IDs to exclude (blacklist)
}

OperationsFilter defines which OpenAPI operations to include or exclude. Only one of Include or Exclude should be specified.

type PlanDiff

type PlanDiff struct {
	HasChanges bool       `json:"hasChanges"`
	Items      []DiffItem `json:"items"`
	Summary    string     `json:"summary"`
}

PlanDiff is the complete diff between two stack specs.

func ComputePlan

func ComputePlan(proposed, current *Stack) *PlanDiff

ComputePlan compares a new spec against the currently running spec and returns a structured diff.

type RateLimit

type RateLimit struct {
	Client string `yaml:"client,omitempty" json:"client,omitempty"`
	Server string `yaml:"server,omitempty" json:"server,omitempty"`
	Tool   string `yaml:"tool,omitempty" json:"tool,omitempty"`
	// CallsPerMinute is the sustained rate. Must be positive.
	CallsPerMinute int `yaml:"calls_per_minute" json:"calls_per_minute"`
	// Burst is the bucket capacity; 0 selects the default.
	Burst int `yaml:"burst,omitempty" json:"burst,omitempty"`
}

RateLimit is a token-bucket call rate for one scope. Burst is the bucket capacity: how many calls may land at once before the sustained rate applies. Zero means a default of max(5, calls_per_minute/6).

func (RateLimit) ScopeKey

func (r RateLimit) ScopeKey() (kind, key string, ok bool)

ScopeKey returns the rate limit's scope kind and key; ok=false when the entry does not set exactly one of client/server/tool.

type ReferenceIndex

type ReferenceIndex map[string][]Consumer

ReferenceIndex maps a variable-store key to the consumers that reference it.

It is built by expandStackVars from the same grammar used for expansion (ExpandStringRefs), so it can never drift from what gridctl actually recognizes as a ${var:KEY}/${vault:KEY} reference. It carries only keys and reference-site metadata — never variable values — so it is safe to expose even while the vault is locked.

Scope:

  • One-hop only: a variable referenced inside another variable's *value* is not followed. This is intentional — values live in the vault, outside the static stack, so resolving transitive references would require reading secrets at index time. v1 indexes only references written in the stack.
  • Known gap: secrets injected via secrets.sets (see injectSetSecrets) are added to server env *after* expansion without ${var:KEY} syntax, so they are not recorded here. The usage API compensates by synthesizing RefKindSecretsSet consumers from vault set membership; the index itself stays explicit-references-only.

type ReferenceKind

type ReferenceKind string

ReferenceKind identifies what kind of stack element references a variable.

const (
	RefKindMCPServer ReferenceKind = "mcp-server"
	RefKindResource  ReferenceKind = "resource"
	RefKindGateway   ReferenceKind = "gateway"
	RefKindNetwork   ReferenceKind = "network"
	RefKindStack     ReferenceKind = "stack"
	// RefKindSecretsSet marks a synthetic consumer for a variable injected in
	// bulk through the stack's secrets.sets block (see injectSetSecrets). These
	// never appear in Stack.References — the API layer synthesizes them from
	// vault set membership so usage reporting covers injected keys too.
	RefKindSecretsSet ReferenceKind = "secrets-set"
)

type ReplicaHealth

type ReplicaHealth struct {
	ReplicaID        int    `json:"replicaId"`
	State            string `json:"state"` // "healthy" | "unhealthy" | "restarting"
	InFlight         int64  `json:"inFlight"`
	UptimeSeconds    int64  `json:"uptimeSeconds,omitempty"`
	LastError        string `json:"lastError,omitempty"`
	NextRetrySeconds int64  `json:"nextRetrySeconds,omitempty"`
	RestartAttempts  uint32 `json:"restartAttempts,omitempty"`
	PID              int    `json:"pid,omitempty"`
	ContainerID      string `json:"containerId,omitempty"`
}

ReplicaHealth describes the live state of one replica in a server's ReplicaSet. Durations use seconds so the JSON representation does not depend on Go's time formatting.

type Resolver

type Resolver func(name string) (string, bool)

Resolver looks up a variable by name. Returns value and whether it exists.

func EnvResolver

func EnvResolver() Resolver

EnvResolver returns a resolver that checks os.LookupEnv.

func VaultResolver

func VaultResolver(vault VaultLookup) Resolver

VaultResolver returns a resolver that checks vault first, then env.

type Resource

type Resource struct {
	Name    string            `yaml:"name"`
	Image   string            `yaml:"image"`
	Env     map[string]string `yaml:"env,omitempty"`
	Ports   []string          `yaml:"ports,omitempty"`
	Volumes []string          `yaml:"volumes,omitempty"`
	Network string            `yaml:"network,omitempty"` // Network to join (for multi-network mode)
}

Resource defines a supporting container (database, cache, etc).

type RetentionConfig

type RetentionConfig struct {
	MaxSizeMB  int `yaml:"max_size_mb,omitempty" json:"max_size_mb,omitempty"`
	MaxBackups int `yaml:"max_backups,omitempty" json:"max_backups,omitempty"`
	MaxAgeDays int `yaml:"max_age_days,omitempty" json:"max_age_days,omitempty"`
}

RetentionConfig controls lumberjack rotation for persisted telemetry files. One block per stack — per-signal retention is intentionally out of scope at MVP. Defaults: 100MB / 5 backups / 7d. YAML tags use snake_case to match the AutoscaleConfig precedent for control-plane structs (LoggingConfig uses camelCase, but is closer to a runtime-rotation knob than a control-plane resource).

type SSHConfig

type SSHConfig struct {
	Host           string `yaml:"host"`                     // Required: hostname or IP address
	User           string `yaml:"user"`                     // Required: SSH username
	Port           int    `yaml:"port,omitempty"`           // Optional: SSH port (default 22)
	IdentityFile   string `yaml:"identityFile,omitempty"`   // Optional: path to SSH private key
	KnownHostsFile string `yaml:"knownHostsFile,omitempty"` // Optional: path to known_hosts file; enables StrictHostKeyChecking=yes
	JumpHost       string `yaml:"jumpHost,omitempty"`       // Optional: bastion/jump host ([user@]host[:port])
}

SSHConfig defines SSH connection parameters for remote MCP servers.

type SchemaPinningConfig

type SchemaPinningConfig struct {
	// Enabled controls whether schema pinning is active. Default: true.
	// A pointer so an omitted `enabled:` inherits the default-on behavior
	// rather than YAML's zero value (false); set it explicitly to false to
	// disable pinning for the whole stack.
	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
	// Action is the response when drift is detected: "warn" (default) or "block".
	// warn: log a structured diff and continue serving.
	// block: reject all tool calls from the drifted server until approved.
	Action string `yaml:"action,omitempty" json:"action,omitempty"`
	// Scan controls the poisoning heuristics run over tool definitions at
	// pin and drift time. Default: true. Findings are always advisory; this
	// toggle never affects hashing, drift detection, or the approve flow.
	Scan *bool `yaml:"scan,omitempty" json:"scan,omitempty"`
	// ScanIgnore suppresses scan findings by code (e.g. ["P004"]). Useful
	// for silencing a heuristic that false-positives on a legitimate stack.
	ScanIgnore []string `yaml:"scan_ignore,omitempty" json:"scan_ignore,omitempty"`
}

SchemaPinningConfig controls the schema pinning feature.

type SecretSetRef

type SecretSetRef struct {
	Name      string   `yaml:"name" json:"name"`
	Servers   []string `yaml:"servers,omitempty" json:"servers,omitempty"`
	Resources []string `yaml:"resources,omitempty" json:"resources,omitempty"`
	// contains filtered or unexported fields
}

SecretSetRef is one entry in the `secrets.sets` block: a variable set whose members are injected into container environments at load time (see injectSetSecrets).

An entry is either a bare set name or a mapping that narrows which workloads receive the set:

secrets:
  sets:
    - shared              # unscoped: injected into every server and resource
    - name: github-creds  # scoped: only the listed workloads receive it
      servers: [github]
    - name: db
      resources: [postgres]

Scoping is opt-in and per entry (Article IX). An entry with neither servers nor resources keeps the historic fan-out, so a stack written before scoping existed behaves identically. Naming either list makes the entry scoped, and a scoped entry reaches only what it names: `servers: [github]` injects into the github server and into no resources at all. That is the least-privilege reading, and it keeps one rule ("scoped entries reach exactly what they name") rather than two axes with different defaults.

func (SecretSetRef) InjectsIntoResource

func (r SecretSetRef) InjectsIntoResource(name string) bool

InjectsIntoResource reports whether this entry's secrets reach the named resource. Unscoped entries reach every resource.

func (SecretSetRef) InjectsIntoServer

func (r SecretSetRef) InjectsIntoServer(name string) bool

InjectsIntoServer reports whether this entry's secrets reach the named MCP server. Unscoped entries reach every server.

func (SecretSetRef) IsShorthand

func (r SecretSetRef) IsShorthand() bool

IsShorthand reports whether the entry carries nothing beyond the set name, so YAML emitters can round-trip it back to the scalar form.

func (SecretSetRef) MarshalYAML

func (r SecretSetRef) MarshalYAML() (any, error)

MarshalYAML emits the scalar shorthand for unscoped entries so a stack that was written as `- shared` survives a load/save cycle unchanged. Scoped entries marshal as mappings, keeping an explicitly empty scope explicit.

func (SecretSetRef) Scoped

func (r SecretSetRef) Scoped() bool

Scoped reports whether the entry narrows injection to named workloads. Unscoped entries fan out to every server and resource. An entry that declared an empty scope is scoped (to nothing), not unscoped.

func (*SecretSetRef) UnmarshalYAML

func (r *SecretSetRef) UnmarshalYAML(value *yaml.Node) error

UnmarshalYAML accepts the scalar shorthand ("- shared") or the mapping form. Any other node kind is rejected so a stray nested sequence fails loudly instead of decoding to an empty entry. Mirrors LinkEntry, with the addition of unknown-key rejection because a dropped key here fails open.

type Secrets

type Secrets struct {
	Sets []SecretSetRef `yaml:"sets,omitempty" json:"sets,omitempty"`
}

Secrets configures automatic secret injection from variable sets. Each entry is a set name (fan-out to every workload) or a mapping that scopes the set to named servers and resources. See SecretSetRef.

type ServerAuth

type ServerAuth struct {
	Type string `yaml:"type"` // "bearer", "header", or "oauth"

	// Static bearer token (type: bearer). Sent as "Authorization: Bearer <token>".
	// Use ${VAR} or ${var:KEY} references rather than literal secrets.
	Token string `yaml:"token,omitempty"`

	// Static header (type: header).
	Header string `yaml:"header,omitempty"` // header name, e.g. "X-API-Key"
	Value  string `yaml:"value,omitempty"`  // header value; use ${VAR} references

	// OAuth 2.1 brokering (type: oauth). All fields optional: scopes default
	// to what the server advertises, and a pre-registered client_id (plus
	// client_secret when the provider issued one) bypasses dynamic client
	// registration for authorization servers that do not support it.
	Scopes       []string `yaml:"scopes,omitempty"`
	ClientID     string   `yaml:"client_id,omitempty"`
	ClientSecret string   `yaml:"client_secret,omitempty"`
}

ServerAuth defines downstream authentication for an external URL MCP server. Type selects the behavior; the other fields belong to exactly one type.

type SkillsPolicyConfig

type SkillsPolicyConfig struct {
	// Default is the fate of a skill matching neither list: "allow" (the
	// default when empty) or "deny".
	Default string `yaml:"default,omitempty" json:"default,omitempty"`
	// Allow lists skill-name globs admitted even under default: deny.
	Allow []string `yaml:"allow,omitempty" json:"allow,omitempty"`
	// Deny lists skill-name globs hidden from exposure; deny beats allow.
	Deny []string `yaml:"deny,omitempty" json:"deny,omitempty"`
}

SkillsPolicyConfig is the optional top-level `skills:` block: a global exposure policy for registry skills, filtering which skills the gateway serves via prompts/resources and which the daemon projects into client skill directories. Omitting the block preserves legacy behavior — every active skill is exposed (Article IX).

Allow and Deny are skill-name globs (path.Match syntax). A Deny match always wins, then an Allow match admits, then Default decides ("deny" denies; anything else, including omitted, allows). Denial filters exposure only: denied skills keep their registry state, stay visible in the registry API and UI flagged with the matching rule, and `gridctl apply` warns for every active skill the policy hides.

The policy is global by design; per-client skill scoping remains the documented v1 deferral on ClientsConfig. Distinct from the "skill sources" config in ~/.gridctl/skills.yaml (pkg/skills.SkillsConfig), which declares where git-imported skills come from, not what is exposed.

type Source

type Source struct {
	Type       string      `yaml:"type"` // "git" or "local"
	URL        string      `yaml:"url,omitempty"`
	Ref        string      `yaml:"ref,omitempty"`
	Path       string      `yaml:"path,omitempty"`
	Dockerfile string      `yaml:"dockerfile,omitempty"`
	Auth       *SourceAuth `yaml:"auth,omitempty"`
}

Source defines how to build an MCP server from source code.

type SourceAuth

type SourceAuth struct {
	Method        string `yaml:"method,omitempty"`         // "", "none", "token", "ssh-agent", "ssh-key"
	CredentialRef string `yaml:"credential_ref,omitempty"` // e.g. "${vault:GIT_TOKEN}" — resolved on every clone/fetch
	SSHUser       string `yaml:"ssh_user,omitempty"`       // defaults to "git" when empty
	SSHKeyPath    string `yaml:"ssh_key_path,omitempty"`   // required for method "ssh-key"
}

SourceAuth is the declarative auth block on an MCP server git source. Raw tokens must NOT appear here — use CredentialRef (e.g. "${vault:GIT_TOKEN}") which is resolved against the live vault at clone time. Never add a Token field to this struct: anything with a yaml tag here gets persisted to disk.

type SpecHealth

type SpecHealth struct {
	Validation   ValidationStatus `json:"validation"`
	Drift        DriftStatus      `json:"drift"`
	Dependencies DependencyStatus `json:"dependencies"`

	// Replicas reports live per-replica health for every server that has
	// more than one replica registered. Servers with replicas <= 1 are
	// omitted so the shape is backward compatible with single-replica
	// deployments. Keyed by server name.
	Replicas map[string][]ReplicaHealth `json:"replicas,omitempty"`
}

SpecHealth aggregates validation, drift, and dependency status.

type Stack

type Stack struct {
	Version    string                 `yaml:"version"`
	Name       string                 `yaml:"name"`
	Extends    string                 `yaml:"extends,omitempty"` // Path to a parent stack file for composition
	Gateway    *GatewayConfig         `yaml:"gateway,omitempty"`
	Logging    *LoggingConfig         `yaml:"logging,omitempty"`
	Telemetry  *TelemetryConfig       `yaml:"telemetry,omitempty"` // Opt-in disk persistence for logs/metrics/traces
	Secrets    *Secrets               `yaml:"secrets,omitempty"`   // Variable set references
	Network    Network                `yaml:"network"`             // Single network (simple mode)
	Networks   []Network              `yaml:"networks,omitempty"`  // Multiple networks (advanced mode)
	MCPServers []MCPServer            `yaml:"mcp-servers"`
	Resources  []Resource             `yaml:"resources,omitempty"`
	Clients    *ClientsConfig         `yaml:"clients,omitempty"`                        // Optional per-client access scoping (NetworkPolicy semantics)
	Limits     *LimitsConfig          `yaml:"limits,omitempty" json:"limits,omitempty"` // Optional rate limits enforced at dispatch
	Groups     map[string]GroupConfig `yaml:"groups,omitempty" json:"groups,omitempty"` // Optional named tool bundles, each at /groups/{name}/mcp
	Skills     *SkillsPolicyConfig    `yaml:"skills,omitempty" json:"skills,omitempty"` // Optional global skill exposure policy (allow/deny name globs)

	// ModelPreferences is the optional top-level `model_preferences:`
	// block: per-scope model preference defaults and overrides applied to
	// skill and agent projections. Omitting the block preserves legacy
	// behavior; projections are byte-identical pass-throughs (Article
	// IX). Deliberately a sibling of Skills, never nested under it: the
	// `skills:` block is gateway exposure policy, an unrelated domain.
	// Not inherited across `extends` (matching clients/groups/limits).
	ModelPreferences *ModelPreferencesConfig `yaml:"model_preferences,omitempty" json:"model_preferences,omitempty"`

	// Link declares LLM clients that `gridctl apply` connects to this
	// stack's gateway once it is healthy. See LinkEntry for entry forms and
	// reconcile semantics. Empty (the default) preserves legacy behavior:
	// linking stays a manual `gridctl link` step. Not inherited across
	// `extends` (matching clients/groups/limits).
	Link []LinkEntry `yaml:"link,omitempty" json:"link,omitempty"`

	// Experimental enables registered experimental feature flags by name
	// (see pkg/flags for the registry and lifecycle). Omitted (the default)
	// enables nothing — Article IX by construction. Unknown, graduated, and
	// removed names warn at validate and apply time but never block a
	// deploy. Deliberately typed map[string]bool so YAML 1.1 boolean
	// spellings (on/yes) decode as booleans.
	Experimental map[string]bool `yaml:"experimental,omitempty" json:"experimental,omitempty"`

	// References is the variable-usage index, derived during expandStackVars:
	// which consumers reference each ${var:KEY}/${vault:KEY} key. It is computed
	// from the stack, not persisted with it — the yaml/json "-" tags keep it out
	// of every existing serialization path. Nil until a stack is loaded/expanded.
	References ReferenceIndex `yaml:"-" json:"-"`

	// UnresolvedRefs lists the ${var:KEY}/${vault:KEY} keys that the resolver
	// used at expansion time could not satisfy and that carry no default
	// operator, in first-seen order. It is the loader's own definition of
	// "missing": a reference written as ${var:KEY:-fallback} resolves to its
	// default and is deliberately absent here even though it still appears in
	// References.
	//
	// The meaning depends on the resolver. LoadStack with a vault yields keys
	// missing from vault and environment both, which is exactly what it refuses
	// to deploy. ValidateStackFile expands with the environment alone, so its
	// list is a superset that callers narrow by checking the vault themselves
	// (see the drift endpoint). Computed, never persisted.
	UnresolvedRefs []string `yaml:"-" json:"-"`
}

Stack represents the complete gridctl configuration.

func LoadStack

func LoadStack(path string, opts ...LoadOption) (*Stack, error)

LoadStack reads and parses a stack file.

func (*Stack) ContainerWorkloads

func (s *Stack) ContainerWorkloads() []string

ContainerWorkloads returns human-readable descriptions of workloads that require a container runtime.

func (*Stack) ModelPolicies

func (s *Stack) ModelPolicies() (skills, agents *registry.ModelPolicy)

ModelPolicies compiles the stack's model_preferences block into the per-scope policies for the skill and agent projection engines. A loaded stack always yields non-nil policies, block or no block: deleting the block is the same known-absent off switch as `rewrite: false`. Only a nil stack (no stack context at all) yields nil policies, which the engines treat as preserve-not-revert.

func (*Stack) NeedsContainerRuntime

func (s *Stack) NeedsContainerRuntime() bool

NeedsContainerRuntime returns true if the stack has workloads requiring a container runtime.

func (*Stack) NonContainerWorkloads

func (s *Stack) NonContainerWorkloads() []string

NonContainerWorkloads returns human-readable descriptions of workloads that work without a container runtime.

func (*Stack) SetDefaults

func (s *Stack) SetDefaults()

SetDefaults applies default values to the stack.

type TelemetryConfig

type TelemetryConfig struct {
	// Persist names which signals are written to disk by default. Per-server
	// blocks can override individual signals.
	Persist TelemetryPersistence `yaml:"persist,omitempty" json:"persist,omitempty"`
	// Retention controls lumberjack rotation for every persisted signal file.
	// SetDefaults fills sensible defaults when this block is omitted.
	Retention *RetentionConfig `yaml:"retention,omitempty" json:"retention,omitempty"`
}

TelemetryConfig configures opt-in disk persistence for the three signals gridctl already captures (logs, metrics, traces). All fields are optional; when the block is omitted entirely, every signal stays ephemeral (today's behavior). Per-server overrides on MCPServer.Telemetry can flip individual signals on or off relative to these defaults.

Stack-global Persist fields are plain bool (binary on/off). Per-server MCPServerPersistence fields are *bool to express tri-state inheritance — see MCPServerTelemetry.

type TelemetryPersistence

type TelemetryPersistence struct {
	Logs    bool `yaml:"logs,omitempty" json:"logs,omitempty"`
	Metrics bool `yaml:"metrics,omitempty" json:"metrics,omitempty"`
	Traces  bool `yaml:"traces,omitempty" json:"traces,omitempty"`
}

TelemetryPersistence is the stack-global signal toggle. Stack-global is binary (a bool) — the per-server override carries the tri-state.

type TracingConfig

type TracingConfig struct {
	// Enabled controls whether tracing is active. Default: true.
	// A pointer so an omitted `enabled:` inherits the default-on behavior
	// rather than YAML's zero value (false); set it explicitly to false to
	// disable tracing.
	Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
	// Sampling is the head-based sampling rate [0.0, 1.0]. Default: 1.0.
	Sampling float64 `yaml:"sampling,omitempty" json:"sampling,omitempty"`
	// Retention is how long completed traces are kept in memory (e.g. "24h"). Default: "24h".
	Retention string `yaml:"retention,omitempty" json:"retention,omitempty"`
	// Export selects an exporter: "otlp" or "" (none).
	Export string `yaml:"export,omitempty" json:"export,omitempty"`
	// Endpoint is the OTLP endpoint URL (e.g. "http://localhost:4318").
	Endpoint string `yaml:"endpoint,omitempty" json:"endpoint,omitempty"`
	// MaxTraces is the in-memory ring buffer capacity (number of traces). Default: 1000.
	MaxTraces int `yaml:"max_traces,omitempty" json:"max_traces,omitempty"`
	// IncludeInfra admits spans from non-gridctl instrumentation scopes (e.g.
	// Docker SDK HTTP self-instrumentation) into the UI trace buffer. Default: false.
	IncludeInfra bool `yaml:"include_infra,omitempty" json:"include_infra,omitempty"`
}

TracingConfig configures distributed tracing for the gateway.

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError represents a configuration validation error.

func (ValidationError) Error

func (e ValidationError) Error() string

type ValidationErrors

type ValidationErrors []ValidationError

ValidationErrors is a collection of validation errors.

func (ValidationErrors) Error

func (e ValidationErrors) Error() string

type ValidationIssue

type ValidationIssue struct {
	Field    string        `json:"field"`
	Message  string        `json:"message"`
	Severity IssueSeverity `json:"severity"`
}

ValidationIssue is a validation finding with severity level.

type ValidationResult

type ValidationResult struct {
	Valid        bool              `json:"valid"`
	ErrorCount   int               `json:"errorCount"`
	WarningCount int               `json:"warningCount"`
	Issues       []ValidationIssue `json:"issues"`
}

ValidationResult holds the complete output of spec validation.

func ValidateWithIssues

func ValidateWithIssues(s *Stack) *ValidationResult

ValidateWithIssues runs full validation and returns structured issues with severity. This wraps the existing Validate() and adds warning-level checks.

type ValidationStatus

type ValidationStatus struct {
	Status       string `json:"status"` // "valid", "warnings", "errors"
	ErrorCount   int    `json:"errorCount"`
	WarningCount int    `json:"warningCount"`
}

ValidationStatus summarizes the spec validation state.

type VaultLookup

type VaultLookup interface {
	Get(key string) (string, bool)
}

VaultLookup is the interface the vault store must satisfy.

type VaultSecret

type VaultSecret struct {
	Key   string
	Value string
}

VaultSecret is a minimal secret view for set lookups.

type VaultSetLookup

type VaultSetLookup interface {
	VaultLookup
	GetSetSecrets(setName string) []VaultSecret
}

VaultSetLookup extends VaultLookup with set operations for secrets.sets support.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL