config

package
v0.1.0-alpha.2 Latest Latest
Warning

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

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

Documentation

Overview

Package config provides configuration loading and defaults.

Index

Constants

View Source
const (
	DefaultMaxParallelTools = 4
	MinMaxParallelTools     = 1
	MaxMaxParallelTools     = 16
)

Parallel tool execution bounds. The ceiling protects file descriptors, memory for large reads, and remote rate limits for WebFetch and MCP; real batches from a model are two to five calls, so the default captures nearly all of the available win while keeping the failure modes boring.

View Source
const (
	// TransportDirect calls an OpenAI-compatible provider from this machine
	// using the entry's own credential. It is the default.
	TransportDirect = "direct"
	// TransportBuildMax calls a BuildMax server's managed gateway, which holds
	// the provider credential and decides which model the team may use.
	TransportBuildMax = "buildmax"
)

LLM connection modes for a model entry.

The two are never mixed and never fall back to one another: a run either calls a provider from this machine or calls a BuildMax deployment, and the user can tell which from the entry. See docs/design/llm-gateway.md.

View Source
const (
	// LLMProviderOpenAICompatible is OpenAI Chat Completions, spoken by OpenRouter,
	// LiteLLM, vLLM, and local inference servers. It is the default.
	LLMProviderOpenAICompatible = "openai_compatible"
	// LLMProviderOpenAI is OpenAI's own Responses API.
	LLMProviderOpenAI = "openai"
	// LLMProviderAnthropic is the Anthropic Messages API.
	LLMProviderAnthropic = "anthropic"
)

LLM wire protocols a direct model entry can speak. The value names a protocol family, not a vendor: Claude served through an OpenAI-compatible gateway is LLMProviderOpenAICompatible, and Claude served from Anthropic's own endpoint is LLMProviderAnthropic.

View Source
const (
	// ReasoningOff does no extra reasoning. It is the default.
	ReasoningOff    = "off"
	ReasoningLow    = "low"
	ReasoningMedium = "medium"
	ReasoningHigh   = "high"
)

Reasoning effort levels. They are a neutral scale: each protocol maps them to its own vocabulary, and a level a model does not support fails that model's call rather than being silently downgraded.

View Source
const (
	ModelGPT35Turbo        = "openai/gpt-3.5-turbo"
	ModelGemma327bItFree   = "google/gemma-3-27b-it:free"
	ModelGLM45AirFree      = "z-ai/glm-4.5-air:free"
	ModelGPT4oMini         = "openai/gpt-4o-mini"
	ModelGemini25FlashLite = "google/gemini-2.5-flash-lite"
	ModelGemini31FlashLite = "google/gemini-3.1-flash-lite"
)

Model names on OpenRouter (switch DefaultModel to use one).

View Source
const (
	// BUILDMAX_HOME — path to the data directory; tells every binary where to find its
	// config files (settings.yaml, server.yaml). Must remain an env var because the
	// config files cannot be located until this path is known.
	EnvKeyBuildmaxHome = "BUILDMAX_HOME"

	// BUILDMAX_JWT_SECRET — optional override for jwt_secret in server.yaml.
	// In production, inject via env var (Kubernetes Secret, Docker secret) instead of
	// storing the value in the YAML file on disk.
	EnvKeyBuildmaxJWTSecret = "BUILDMAX_JWT_SECRET"

	// BUILDMAX_RUN_TOKEN — the credential one task run presents to the managed LLM
	// gateway. The scheduler mints it per run and puts it in the worker process or
	// Job pod; nothing inherits it, which is why it is not marked WorkerNeeds
	// below. See docs/design/worker-run-token.md.
	EnvKeyBuildmaxRunToken = "BUILDMAX_RUN_TOKEN"

	// Test only.
	EnvKeyBuildmaxTestDSN = "BUILDMAX_TEST_DSN"
)

Environment variable key names.

View Source
const (
	PermissionAllow = "allow"
	PermissionAsk   = "ask"
	PermissionDeny  = "deny"
)

Permission actions as written in settings.yaml.

View Source
const (
	PluginSkillsSubdir = "skills"
	PluginAgentsSubdir = "agents"
)

Plugin content subdirectories. A plugin contributes only what a workspace .buildmax directory already supports, under the same names.

View Source
const (
	// BUILDMAX_DATABASE_PASSWORD overrides database.password.
	EnvKeyBuildmaxDatabasePassword = "BUILDMAX_DATABASE_PASSWORD"
	// BUILDMAX_STORAGE_MINIO_ACCESS_KEY overrides storage.minio.access_key.
	EnvKeyBuildmaxMinIOAccessKey = "BUILDMAX_STORAGE_MINIO_ACCESS_KEY"
	// BUILDMAX_STORAGE_MINIO_SECRET_KEY overrides storage.minio.secret_key.
	EnvKeyBuildmaxMinIOSecretKey = "BUILDMAX_STORAGE_MINIO_SECRET_KEY"
	// BUILDMAX_WORKER_TOKEN overrides worker.token, the shared secret for /api/worker/*.
	EnvKeyBuildmaxWorkerToken = "BUILDMAX_WORKER_TOKEN"
	// BUILDMAX_CONVERSATION_MODEL_API_KEY overrides conversation.model.api_key.
	EnvKeyBuildmaxConversationAPIKey = "BUILDMAX_CONVERSATION_MODEL_API_KEY"
)

Environment overrides for secret-bearing server.yaml fields. The file stays the source of truth for shape and non-secret values; these exist so a deployment can inject credentials from a Kubernetes Secret, a Docker secret, or a CI variable without writing them to disk. Same pattern as jwt_secret.

View Source
const (
	ProviderLocalFS = "local_fs"
	ProviderMinIO   = "minio"
)
View Source
const DefaultCallTimeoutSecs = 300

DefaultCallTimeoutSecs is the per-LLM-call timeout used when a model entry does not specify call_timeout. 300 seconds (5 minutes) is generous enough for long reasoning responses while still bounding hung connections.

View Source
const DefaultContextWindow = 32_000

DefaultContextWindow is the fallback context window token limit used when a model entry does not specify context_window (i.e. the value is 0). 32 000 tokens is a conservative default that fits most hosted models.

View Source
const DefaultDBTLSMode = "preferred"

DefaultDBTLSMode is used when database.tls is unset.

"preferred" upgrades a connection whenever the server advertises TLS and behaves exactly as before against one that does not, so it has no failure mode a plaintext connection did not already have. It does not verify the certificate; a deployment that needs that sets "true".

View Source
const DefaultMaxTokens = 8_192

DefaultMaxTokens caps one response when a model entry does not set max_tokens and the protocol requires the field. The Anthropic Messages API rejects a request without it, so its adapter substitutes this value; the OpenAI protocols leave the cap to the provider when it is unset. 8 192 tokens fits a long tool-calling turn without truncating mid-thought.

View Source
const DefaultModel = ModelGemini25FlashLite

DefaultModel is the model used when no model is configured.

View Source
const DefaultOpenRouterBaseURL = "https://openrouter.ai/api/v1"

DefaultOpenRouterBaseURL is the OpenRouter OpenAI-compatible API base URL.

View Source
const EnvKeyBuildmaxSandboxEnabled = "BUILDMAX_SANDBOX_ENABLED"

Env override key. Phase A only honors BUILDMAX_SANDBOX_ENABLED; later phases may add more.

View Source
const EnvKeyBuildmaxTraceDisabled = "BUILDMAX_TRACE_DISABLED"

EnvKeyBuildmaxTraceDisabled, when truthy, turns off durable run traces. Per-subsystem env constant (kept here next to the resolver, mirroring the sandbox convention) and registered in env_spec.go EnvVars.

View Source
const MCPVarWorkspaceRoot = "WORKSPACE_ROOT"

MCPVarWorkspaceRoot is the variable name available in mcp.json $VAR expansion that resolves to the workspace directory passed to LoadMCPConfigForWorkspace. MCP config authors use it as $WORKSPACE_ROOT in command, args, env, or url fields.

View Source
const PluginStateFile = ".state.json"

PluginStateFile is the name of the supplemental state file. It begins with a dot because §4.1 reserves dot-prefixed entries in the plugins directory for BuildMax's own staging, cache, and state.

View Source
const PluginStateVersion = 1

PluginStateVersion is the schema version written into .state.json. The file is machine-authored, so stamping it costs an author nothing and lets a later format change be detected instead of guessed at.

View Source
const PluginVarRoot = plugin.VarPluginRoot

PluginVarRoot names the plugin-root variable, defined with the rest of the document format in internal/core/plugin.

Each plugin's configuration is expanded with its own root before layers are merged, which is the only order that gives two plugins two different answers for the same text. BuildMax supplies the value; a plugin cannot override it by exporting an environment variable of the same name.

Variables

View Source
var (
	// Version is the BuildMax application version, without a leading "v".
	Version = "dev"

	// Commit is the short git commit SHA the binary was built from.
	Commit = "dev"
)

Version and Commit identify the build. The release pipeline and ./make build inject both at link time so the git tag stays the single source of truth for what a binary calls itself:

-ldflags "-X github.com/gougoujiang/buildmax/internal/config.Version=0.1.0 \
          -X github.com/gougoujiang/buildmax/internal/config.Commit=abc1234"

When the linker sets neither — `go install module@version`, `go build`, `go run`, `go test` — init falls back to the Go build info, so a binary installed with `go install` reports the released version it was built from instead of a bare "dev". See resolveBuildInfo.

View Source
var EnvVars = []EnvVar{
	{Name: EnvKeyBuildmaxHome, Default: "~/.buildmax", Description: "Application data directory; locates settings.yaml and server.yaml", WorkerNeeds: true},
	{Name: EnvKeyBuildmaxJWTSecret, Description: "Override for jwt_secret in server.yaml; inject at deploy time in production"},
	{Name: EnvKeyBuildmaxDatabasePassword, Description: "Override for database.password in server.yaml"},
	{Name: EnvKeyBuildmaxMinIOAccessKey, Description: "Override for storage.minio.access_key in server.yaml", WorkerNeeds: true},
	{Name: EnvKeyBuildmaxMinIOSecretKey, Description: "Override for storage.minio.secret_key in server.yaml", WorkerNeeds: true},
	{Name: EnvKeyBuildmaxWorkerToken, Description: "Override for worker.token in server.yaml; shared secret for /api/worker/*", WorkerNeeds: true},
	{Name: EnvKeyBuildmaxConversationAPIKey, Description: "Override for conversation.model.api_key in server.yaml", WorkerNeeds: true, DirectLLMOnly: true},

	{Name: EnvKeyBuildmaxRunToken, Description: "Per-run credential for the managed LLM gateway; minted by the scheduler, not set by an operator"},
	{Name: EnvKeyBuildmaxTestDSN, Description: "MySQL DSN for store integration tests; unset skips those tests"},
	{Name: EnvKeyBuildmaxSandboxEnabled, Description: "Override sandbox.enabled in settings; values: 1/true/yes/on or 0/false/no/off", WorkerNeeds: true},
	{Name: EnvKeyBuildmaxTraceDisabled, Description: "Disable durable run traces when truthy (1/true/yes/on); traces are on by default", WorkerNeeds: true},
}

EnvVars lists every environment variable read by BuildMax binaries.

Functions

func AgentDefSources

func AgentDefSources(workspace string, plugins []DiscoveredPlugin) []plugin.Source

AgentDefSources returns the priority-ordered directories to scan for subagent definitions, in the same layering as SkillSources.

func AgentDefsSearchPaths

func AgentDefsSearchPaths(workspace string) []string

AgentDefsSearchPaths returns the ordered list of directories to scan for agent definitions. Priority: workspace-local first, then global DataDir.

func AuthPath

func AuthPath() string

AuthPath returns the path to the auth credentials file under DataDir.

func DataDir

func DataDir() string

DataDir returns the application data folder path. BUILDMAX_HOME overrides; default is ~/.buildmax. Does not create the directory; callers must create it if needed.

func FilterWorkerEnv

func FilterWorkerEnv(environ []string, managedLLM bool) []string

FilterWorkerEnv returns environ with the BUILDMAX_ variables a worker does not read removed.

Everything outside the BUILDMAX_ prefix passes through untouched: PATH, HOME and the rest are what let the worker binary run at all. Use this when handing a local worker process the server's environment.

func KnownLLMProvider

func KnownLLMProvider(name string) bool

KnownLLMProvider reports whether name is a wire protocol BuildMax implements. The empty string is known: it means the default.

func KnownReasoningEffort

func KnownReasoningEffort(level string) bool

KnownReasoningEffort reports whether level is one BuildMax implements. The empty string is known: it means off.

func LoadMCPConfigForWorkspace

func LoadMCPConfigForWorkspace(workspaceDir string) (*coremcp.ConfigRoot, error)

LoadMCPConfigForWorkspace loads and validates MCP config, or returns (nil, nil) if none.

It merges <DataDir>/mcp.json (global) with <workspace>/.buildmax/mcp.json when workspaceDir is non-empty: all servers from both files are combined, and when a server id appears in both, the workspace entry replaces the global one.

After loading, $VAR and ${VAR} in each server's command, args, env values, and url are expanded against a variable table: snapshot of the process environment (os.Environ) plus MCPVarWorkspaceRoot set to workspaceDir (overrides the same key from the env if present).

func LoadWorkspaceHooks

func LoadWorkspaceHooks(workspace string) (corehook.Config, error)

LoadWorkspaceHooks reads <workspace>/.buildmax/hooks.yaml. A missing file is not an error — returns (corehook.Config{}, nil) so callers can merge unconditionally. A malformed file is reported as an error so misconfig surfaces at startup instead of silently dropping rules.

The file shape mirrors the "hooks:" block of settings.yaml; the top-level keys are pre_tool_use / post_tool_use / pre_compact / post_compact / run_end (the same snake_case event keys).

func LogLevel

func LogLevel(fromSettings string) string

LogLevel returns the effective log level from the config file, defaulting to "info".

func LogsDir

func LogsDir() string

LogsDir returns the path to the logs directory under DataDir.

func MergeHooks

func MergeHooks(layers ...corehook.Config) corehook.Config

MergeHooks returns a single corehook.Config containing every entry from every layer, in the order the layers are given, per event. The merge is additive: every layer runs for the same event; the dispatcher's first-block-wins rule still applies, but every matching hook still executes.

The documented order is global settings, then plugins in name order, then the workspace. A workspace cannot remove a global hook, which is what makes the global layer usable as an operator control — and the same is true of a plugin.

Mutating the returned config does not affect the inputs.

func PersistentWorkspaceDir

func PersistentWorkspaceDir(workspacesDir, workspaceID string) string

PersistentWorkspaceDir returns the persistent home directory for a team's workspace.

func PluginRootFor

func PluginRootFor(p DiscoveredPlugin) string

PluginRootFor returns the value BUILDMAX_PLUGIN_ROOT takes inside one plugin's configuration.

func PluginsDir

func PluginsDir() string

PluginsDir is where installed plugins live. Respecting BUILDMAX_HOME is what keeps tests, workers, and isolated installations out of a contributor's real plugins directory.

func PolicyPath

func PolicyPath() string

PolicyPath returns the operator-controlled policy file path. Optional; missing file means "no operator lock-out."

func ReasoningEfforts

func ReasoningEfforts() []string

ReasoningEfforts returns every level an operator may set, for help text and error messages that must not drift from the list above.

func ReasoningEnabled

func ReasoningEnabled(level string) bool

ReasoningEnabled reports whether a configured level asks for reasoning.

func ResolveMCPConfigPath

func ResolveMCPConfigPath(workspaceDir string) string

ResolveMCPConfigPath returns one existing MCP config path for display or tooling, or "". Order: <workspace>/.buildmax/mcp.json, then <DataDir>/mcp.json. LoadMCPConfigForWorkspace merges both files when they exist; this helper returns only the highest-priority existing path for display purposes.

func ResolveMaxParallelTools

func ResolveMaxParallelTools(cfg AgentConfig) int

ResolveMaxParallelTools clamps the configured value into the supported range. Out-of-range is clamped rather than rejected: a number that is merely too large should not stop the agent starting.

func RuntimeTaskRunArtifactsDir

func RuntimeTaskRunArtifactsDir(workspacesDir, workspaceID, taskID, taskRunID string) string

RuntimeTaskRunArtifactsDir returns the run's artifacts dir.

func RuntimeTaskRunDir

func RuntimeTaskRunDir(workspacesDir, workspaceID, taskID, taskRunID string) string

RuntimeTaskRunDir returns the run directory for a specific task run.

func RuntimeTaskRunGlobalDir

func RuntimeTaskRunGlobalDir(workspacesDir, workspaceID, taskID, taskRunID string) string

RuntimeTaskRunGlobalDir returns the run's global dir (BUILDMAX_HOME for that run).

func RuntimeTaskRunHomeDir

func RuntimeTaskRunHomeDir(workspacesDir, workspaceID, taskID, taskRunID string) string

RuntimeTaskRunHomeDir returns the run's home dir (materialized workspace home).

func ServerConfigPath

func ServerConfigPath() string

ServerConfigPath returns the path to the server config file (BUILDMAX_HOME/server.yaml).

func SessionsDir

func SessionsDir() string

SessionsDir returns the path to the sessions directory under DataDir.

func SettingsPath

func SettingsPath() string

SettingsPath returns the path to the local settings file (BUILDMAX_HOME/settings.yaml).

func SkillSearchPaths

func SkillSearchPaths(workspace string) []string

SkillSearchPaths returns the ordered list of directories to scan for skills. Priority: workspace-local first, then global DataDir.

func SkillSources

func SkillSources(workspace string, plugins []DiscoveredPlugin) []plugin.Source

SkillSources returns the priority-ordered directories to scan for skills: workspace, then global, then each plugin in name order.

Plugins come last because a workspace or a user's own configuration may deliberately override what a plugin ships. Plugins are sorted by name so loading is deterministic — not so that order can settle a collision between two of them, which resolution refuses to do.

func TraceEnabled

func TraceEnabled() bool

TraceEnabled reports whether durable run traces should be written. Traces are on by default; set BUILDMAX_TRACE_DISABLED to a truthy value (1/true/yes/on) to turn them off.

func TracesDir

func TracesDir() string

TracesDir returns the directory holding durable run traces under DataDir. Layout: <DataDir>/traces/<session_id>/<run_id>.jsonl. Does not create the directory; the recorder creates it on demand.

func UpdatePluginStates

func UpdatePluginStates(pluginsDir string, mutate func(*PluginStates) error) error

UpdatePluginStates applies mutate to the state file under a lock held across the read and the write.

Every writer goes through here rather than through a Load/Save pair, because the file is one shared map: a read-modify-write that is not held together loses whichever entry the other writer added. A CLI install racing a running Desktop is the ordinary case, not a rare one.

func VersionString

func VersionString() string

VersionString returns the human-readable version string, e.g. "0.1.0 (abc1234)". The commit is omitted when it is unknown, which is the normal case for a `go install` build.

func WorkerEnvKeys

func WorkerEnvKeys(managedLLM bool) []string

WorkerEnvKeys returns the environment variable names a worker is given, in declaration order.

func WorkerNeedsEnv

func WorkerNeedsEnv(name string, managedLLM bool) bool

WorkerNeedsEnv reports whether a task-run worker reads name.

managedLLM says whether this deployment's task runs reach models through the server. When they do, provider credentials are withheld: the run has a gateway credential instead and never calls a provider itself.

It answers false for an unrecognized BUILDMAX_ variable as well as for a known one a worker does not use, so a variable added to the server without a thought for workers stays on the server.

func WorkspaceHooksPath

func WorkspaceHooksPath(workspace string) string

WorkspaceHooksPath returns the per-workspace hooks config path (<workspace>/.buildmax/hooks.yaml). It does not check existence.

Types

type AgentConfig

type AgentConfig struct {
	// MaxParallelTools bounds how many tool calls from one assistant message
	// may run at once. 1 disables parallel execution; 0 means unset.
	MaxParallelTools int `mapstructure:"max_parallel_tools" json:"max_parallel_tools,omitempty" yaml:"max_parallel_tools,omitempty"`
}

AgentConfig is the "agent" block of settings.yaml.

type DiscoveredPlugin

type DiscoveredPlugin struct {
	// Dir is the directory name. It is the state key and the only identity a
	// plugin has when its manifest does not parse.
	Dir  string
	Path string

	Manifest plugin.Manifest
	// Findings are this plugin's own problems, including a name collision with
	// another directory, so Loadable can be answered without looking around.
	Findings []plugin.Finding

	State PluginState
	// StateKnown is false when nothing recorded this directory, which is the
	// normal condition of a manual `git clone`.
	StateKnown bool

	// PolicyRefused says the operator's source restriction is what stopped this
	// plugin, so a surface can tell a decision from a defect. The reason is
	// also in Findings, which is what carries it to every surface without each
	// one having to know about policy.
	PolicyRefused bool
}

DiscoveredPlugin is one directory under the plugins directory that holds a plugin.yaml, whether or not that manifest turned out to be usable.

func (DiscoveredPlugin) Loadable

func (d DiscoveredPlugin) Loadable() bool

Loadable reports whether this plugin should contribute to a runtime.

func (DiscoveredPlugin) Name

func (d DiscoveredPlugin) Name() string

Name is the plugin's identity: the manifest name, falling back to the directory when the manifest could not supply one.

func (DiscoveredPlugin) Source

func (d DiscoveredPlugin) Source() PluginSource

Source is where this directory came from.

A recorded source is the answer when there is one. Otherwise the directory is classified by looking: a checkout is a repository plugin and anything else is a local one. That is decided here rather than persisted, because the directory is the source of truth and a stored answer would go stale the first time somebody ran `git init` in it.

The look is a stat for `.git`, not a call to Git. Asking Git would answer for the nearest enclosing repository, so a plugins directory inside somebody's home checkout would make every plugin in it look like a clone.

type EnvVar

type EnvVar struct {
	Name        string
	Default     string
	Description string
	// WorkerNeeds marks a variable that a task-run worker reads.
	//
	// A worker executes model-chosen code, so a credential it does not read is
	// exposure with no purpose behind it. Only the marked variables are handed
	// to a worker process or Job pod. internal/bootstrap/worker.go is the
	// authority on what that set is: if a worker starts reading a new variable,
	// mark it here, and it will reach the worker. Nothing else will.
	WorkerNeeds bool
	// DirectLLMOnly narrows WorkerNeeds to deployments whose task runs call a
	// provider themselves. A managed run reaches models through the server and
	// holds no provider credential, so passing one would hand a model-driven
	// process a key it has no use for.
	DirectLLMOnly bool
}

EnvVar describes one environment variable used by BuildMax.

type LLM

type LLM struct {
	APIKey  string
	BaseURL string
	Model   string
}

LLM holds resolved LLM provider settings.

func EffectiveLLM

func EffectiveLLM() (LLM, string)

EffectiveLLM returns the LLM config and display name using the first model from settings or the env-var fallback.

func EffectiveLLMWithModelName

func EffectiveLLMWithModelName(modelName string) (LLM, string, error)

EffectiveLLMWithModelName returns the LLM config and display name to use at startup. When modelName is empty: uses the first model from settings.yaml. When modelName is set: must match an entry by name or model id. Returns an error when settings.yaml has no models configured.

type MCPResolution

type MCPResolution struct {
	// Config is nil when no layer declared a server.
	Config *coremcp.ConfigRoot
	// Shadowed lists plugin servers a higher layer replaced, so a plugin does
	// not appear fully active when part of it never runs.
	Shadowed []plugin.Shadowed
	Findings []plugin.Finding
}

MCPResolution is the merged MCP configuration plus what merging noticed.

func ResolveMCPConfig

func ResolveMCPConfig(workspaceDir string, plugins []DiscoveredPlugin) (MCPResolution, error)

ResolveMCPConfig merges the plugin layer under the global and workspace ones.

Each plugin's file is expanded with its own PluginVarRoot before anything is merged, which is the only order in which two plugins can read the same text and get their own directory. Layer precedence then works as it always has: a later layer replaces a server id an earlier one declared.

type ModelEntry

type ModelEntry struct {
	Model         string `mapstructure:"model"`
	Name          string `mapstructure:"name"`
	APIURL        string `mapstructure:"api_url"`
	APIKey        string `mapstructure:"api_key"`
	ContextWindow int    `mapstructure:"context_window"` // 0 = uses DefaultContextWindow
	CallTimeout   int    `mapstructure:"call_timeout"`   // seconds; 0 = uses DefaultCallTimeoutSecs
	// MaxTokens caps one response; 0 = uses the adapter's own default. The
	// Anthropic Messages protocol requires the field, so its adapter substitutes
	// DefaultMaxTokens; the OpenAI protocols send it only when set.
	MaxTokens int `mapstructure:"max_tokens"`
	// Reasoning is how much the model should reason before answering:
	// ReasoningOff (the default), ReasoningLow, ReasoningMedium, or
	// ReasoningHigh. Any level other than off also replays the reasoning on
	// later turns. It has no effect on a protocol that carries none.
	Reasoning string `mapstructure:"reasoning"`
	// Vision says this model accepts image input. When false, an image a tool
	// returns is described in text rather than sent, because a model that
	// cannot read images rejects the request rather than ignoring the image.
	Vision bool `mapstructure:"vision"`
	// PromptCache asks the provider to cache the stable prefix of a request —
	// the tool definitions and system prompt — so later calls in the same run
	// pay less for them.
	PromptCache bool `mapstructure:"prompt_cache"`
	// Provider is the wire protocol a direct entry speaks: LLMProviderOpenAICompatible
	// (the default), LLMProviderOpenAI, or LLMProviderAnthropic. It is ignored by a
	// "buildmax" entry, where the operator's catalog decides.
	Provider string `mapstructure:"provider"`
	// Transport is "direct" (the default) or "buildmax".
	Transport string `mapstructure:"transport"`
	// ServerURL and TeamID apply to a "buildmax" entry. The credential is not
	// copied here: the remote client reads it from the login state in
	// auth.json, and only when it belongs to this server.
	ServerURL string `mapstructure:"server_url"`
	TeamID    string `mapstructure:"team_id"`
}

ModelEntry is one LLM model entry in settings.yaml (snake_case on disk).

func (ModelEntry) IsManaged

func (m ModelEntry) IsManaged() bool

IsManaged reports whether this entry calls a BuildMax gateway.

func (ModelEntry) LLMProvider

func (m ModelEntry) LLMProvider() string

LLMProvider returns the wire protocol this entry speaks, defaulting to LLMProviderOpenAICompatible so a configuration written before providers existed keeps calling what it always called.

type PermissionEntry

type PermissionEntry struct {
	// Key is the match key, lowercased. Viper lowercases every map key it
	// loads, so a rule written as "Write" arrives as "write" and matching has
	// to be case-insensitive or no rule would ever fire against a tool name.
	Key string
	// Display is the key as the user wrote it, for output.
	Display string
	Action  string
	Source  string
}

PermissionEntry is one resolved rule, carrying where it came from so `buildmax tools status` can name the source.

type PermissionResolution

type PermissionResolution struct {
	Entries []PermissionEntry
	// Invalid lists keys whose action was not recognised. They are ignored
	// rather than fatal: a typo in one rule must not stop the agent starting.
	Invalid []string
}

PermissionResolution is the resolved permission table.

There is one configurable source, settings.yaml. An operator-controlled policy.yaml block was specified and dropped: a worker's BUILDMAX_HOME is created fresh per run, so nothing there could reach the surface that needs it. See docs/design/tool-permissions.md §2 and §7.

func ResolvePermissions

func ResolvePermissions(tools ToolsConfig) PermissionResolution

ResolvePermissions validates and orders the configured rules.

func (PermissionResolution) Lookup

func (r PermissionResolution) Lookup(name, scope string) (PermissionEntry, bool)

Lookup returns the action configured for a call, and whether one matched.

Most specific wins: the exact scope, then the longest matching prefix pattern, then the bare tool name. Without that order a broad "CallMcpTool: deny" would swallow the narrow exception written next to it.

type PluginDiscovery

type PluginDiscovery struct {
	Dir     string
	Plugins []DiscoveredPlugin

	// Findings belong to the directory rather than to any one plugin.
	Findings []plugin.Finding

	// StateErr is set when .state.json existed but could not be read. Every
	// valid plugin still loads; what is lost is provenance and the disabled
	// flag, and a caller must say so rather than report a clean scan.
	StateErr error

	// Policy is the source restriction this scan applied, so a surface can say
	// that a plugin is missing by decision rather than by accident.
	Policy PluginPolicy
}

PluginDiscovery is the result of scanning the plugins directory.

func DiscoverPlugins

func DiscoverPlugins() PluginDiscovery

DiscoverPlugins scans <BUILDMAX_HOME>/plugins under the deployment's policy.

A policy that cannot be read is reported and treated as asserting nothing. Refusing to load any plugin because policy.yaml has a typo would turn one mistake into an outage, and the scan says what it could not apply.

func DiscoverPluginsIn

func DiscoverPluginsIn(dir string, policy PluginPolicy) PluginDiscovery

DiscoverPluginsIn scans one plugins directory.

Directory contents are the source of truth: a manually cloned repository is a plugin the moment it holds a valid plugin.yaml, with no registry to generate and no state file to write. Nothing here touches the network or Git.

func (PluginDiscovery) Loadable

func (d PluginDiscovery) Loadable() []DiscoveredPlugin

Loadable returns the plugins that should contribute to a runtime, in directory order.

type PluginHooks

type PluginHooks struct {
	Config   corehook.Config
	Findings []plugin.Finding
}

PluginHooks is the plugin layer's contribution, already concatenated in name order, plus what loading it noticed.

func ResolvePluginHooks

func ResolvePluginHooks(plugins []DiscoveredPlugin) PluginHooks

ResolvePluginHooks reads every plugin's hooks.yaml in name order.

A plugin whose file will not parse contributes nothing and is reported: hook configuration fails open by design, and refusing to start the agent over one plugin's typo would be a worse failure than running without its hooks.

type PluginPolicy

type PluginPolicy struct {
	// AllowedSources lists the source types that may load. Empty means every
	// source loads, which is the state of a deployment that asserted nothing.
	AllowedSources []string `mapstructure:"allowed_sources" json:"allowed_sources,omitempty" yaml:"allowed_sources,omitempty"`
}

PluginPolicy is the operator-controlled `plugins` block of policy.yaml.

It constrains where plugins may come from, not what they may do. Restricting sources says which bytes are allowed to load; tool permissions, hook gates, and the sandbox are what constrain the bytes that do.

It is fleet management rather than a security boundary: policy.yaml sits in the user's own BUILDMAX_HOME, so a local user can edit it. It is worth having where an operator controls the machine — a managed device, a built image, a container — and worth nothing against somebody who could equally write the configuration by hand.

func (PluginPolicy) Allows

func (p PluginPolicy) Allows(source PluginSource) bool

Allows reports whether a source may load under this policy.

func (PluginPolicy) IsSet

func (p PluginPolicy) IsSet() bool

IsSet reports whether the operator constrained anything.

type PluginSource

type PluginSource string

PluginSource is how a plugin directory came to exist. It is recorded by whatever put the directory there, never inferred from the directory itself.

const (
	// PluginSourceRepository is a Git working tree the user cloned or checked out.
	PluginSourceRepository PluginSource = "repository"
	// PluginSourceMarketplace is a release this machine downloaded and verified.
	PluginSourceMarketplace PluginSource = "marketplace"
	// PluginSourceLocal is an ordinary directory with no other provenance.
	PluginSourceLocal PluginSource = "local"
	// PluginSourceUnknown is a directory nothing recorded, which is the normal
	// state of a manual `git clone` until something inspects it.
	PluginSourceUnknown PluginSource = ""
)

type PluginState

type PluginState struct {
	Source   PluginSource `json:"source,omitempty"`
	Disabled bool         `json:"disabled,omitempty"`

	// Repository provenance, when the installer knew it. A manual clone has
	// none until something inspects the checkout.
	RepositoryURL string `json:"repository_url,omitempty"`
	LastCommit    string `json:"last_commit,omitempty"`

	// Marketplace provenance, which identifies the exact bytes installed.
	MarketplaceServer string `json:"marketplace_server,omitempty"`
	CatalogID         string `json:"catalog_id,omitempty"`
	ReleaseVersion    string `json:"release_version,omitempty"`
	Digest            string `json:"digest,omitempty"`

	InstalledAt int64 `json:"installed_at,omitempty"`
	UpdatedAt   int64 `json:"updated_at,omitempty"`
}

PluginState is the installer's record for one plugin directory.

It holds only what the directory cannot tell BuildMax itself. Nothing here is required to discover or load a plugin: a lost state file costs provenance and the disabled flag, not the plugin.

type PluginStates

type PluginStates struct {
	Version int                    `json:"version"`
	Plugins map[string]PluginState `json:"plugins"`
}

PluginStates is the on-disk shape of .state.json.

Entries are keyed by directory name rather than by plugin name, because the directory is the only identity that exists before a manifest parses — and a manifest that fails to parse is exactly when the disabled flag still matters.

func LoadPluginStates

func LoadPluginStates(pluginsDir string) (PluginStates, error)

LoadPluginStates reads pluginsDir/.state.json.

A missing file returns empty state and no error: discovery does not depend on it. A damaged one is an error the caller reports as lost provenance, having still loaded every valid plugin directory.

func (PluginStates) Get

func (s PluginStates) Get(dir string) (PluginState, bool)

Get returns the record for a directory, and whether one was recorded.

func (*PluginStates) Remove

func (s *PluginStates) Remove(dir string)

Remove drops a directory's record.

func (*PluginStates) Set

func (s *PluginStates) Set(dir string, st PluginState)

Set records state for a directory.

type PolicyFile

type PolicyFile struct {
	Sandbox SandboxConfig `mapstructure:"sandbox" json:"sandbox,omitempty" yaml:"sandbox,omitempty"`
	Plugins PluginPolicy  `mapstructure:"plugins" json:"plugins,omitempty" yaml:"plugins,omitempty"`
}

PolicyFile is the on-disk shape of <BUILDMAX_HOME>/policy.yaml, the operator-controlled layer above settings.yaml.

func LoadPolicyFile

func LoadPolicyFile() (PolicyFile, error)

LoadPolicyFile reads <BUILDMAX_HOME>/policy.yaml. A missing file is not an error: it is the state of every deployment that asserted nothing.

type RedactedDBConfig

type RedactedDBConfig struct {
	Host string `json:"host,omitempty"`
	Port int    `json:"port,omitempty"`
	User string `json:"user,omitempty"`
	Name string `json:"name,omitempty"`
	// TLS is the effective mode, not the raw setting, so an unset value reads
	// as what the connection actually does.
	TLS      string       `json:"tls"`
	Password SecretStatus `json:"password"`
}

RedactedDBConfig shows where the database is, never how to open it.

type RedactedLLMConfig

type RedactedLLMConfig struct {
	DefaultAlias  string            `json:"default_alias,omitempty"`
	Aliases       map[string]string `json:"aliases,omitempty"`
	ConversationM RedactedModel     `json:"conversation_model"`
}

RedactedLLMConfig shows the deployment's model policy. Aliases name catalog ids, which are identifiers rather than credentials; the credentials are in the llm_model table and are never served anywhere.

type RedactedModel

type RedactedModel struct {
	Name        string       `json:"name,omitempty"`
	Model       string       `json:"model,omitempty"`
	APIURL      string       `json:"api_url,omitempty"`
	ModelTarget string       `json:"model_target,omitempty"`
	APIKey      SecretStatus `json:"api_key"`
}

RedactedModel describes the Tier 1 model without its credential.

type RedactedServerConfig

type RedactedServerConfig struct {
	LogLevel             string `json:"log_level,omitempty"`
	Port                 int    `json:"port"`
	AllowSignup          bool   `json:"allow_signup"`
	CORSOrigin           string `json:"cors_origin,omitempty"`
	WorkspacesDir        string `json:"workspaces_dir,omitempty"`
	DefaultQuotaTier     string `json:"default_quota_tier,omitempty"`
	AccessTokenTTL       string `json:"access_token_ttl,omitempty"`
	RefreshTokenTTL      string `json:"refresh_token_ttl,omitempty"`
	RefreshRotationGrace string `json:"refresh_rotation_grace,omitempty"`

	JWTSecret SecretStatus `json:"jwt_secret"`

	Database RedactedDBConfig      `json:"database"`
	Storage  RedactedStorageConfig `json:"storage"`
	Worker   RedactedWorkerConfig  `json:"worker"`
	LLM      RedactedLLMConfig     `json:"llm"`

	// Warnings are configuration states worth an operator's attention. They are
	// not errors — the server is running — and they are computed rather than
	// stored, so the list reflects the process's own view of itself.
	Warnings []string `json:"warnings"`
}

RedactedServerConfig is the effective server configuration, safe to show a System Administrator.

type RedactedStorageConfig

type RedactedStorageConfig struct {
	PersistBackend  string `json:"persist_backend,omitempty"`
	ArtifactBackend string `json:"artifact_backend,omitempty"`
	// MaxArtifactMB is shown because an operator diagnosing a refused upload
	// needs to see the limit that refused it. Zero means the built-in default.
	MaxArtifactMB  int          `json:"max_artifact_mb,omitempty"`
	MinIOEndpoint  string       `json:"minio_endpoint,omitempty"`
	MinIOBucket    string       `json:"minio_bucket,omitempty"`
	MinIORegion    string       `json:"minio_region,omitempty"`
	MinIOAccessKey SecretStatus `json:"minio_access_key"`
	MinIOSecretKey SecretStatus `json:"minio_secret_key"`
}

RedactedStorageConfig shows which backends are in use.

type RedactedWorkerConfig

type RedactedWorkerConfig struct {
	RunMode      string       `json:"run_mode,omitempty"`
	ServerURL    string       `json:"server_url,omitempty"`
	RunTokenTTL  string       `json:"run_token_ttl,omitempty"`
	RunTimeout   string       `json:"run_timeout,omitempty"`
	LLMTransport string       `json:"llm_transport,omitempty"`
	LLMAlias     string       `json:"llm_alias,omitempty"`
	K8sNamespace string       `json:"k8s_namespace,omitempty"`
	K8sImage     string       `json:"k8s_image,omitempty"`
	SharedToken  SecretStatus `json:"shared_token"`
}

RedactedWorkerConfig shows how runs are launched.

type SandboxConfig

type SandboxConfig struct {
	// Enabled is the master switch.
	Enabled bool `mapstructure:"enabled" json:"enabled,omitempty" yaml:"enabled,omitempty"`

	// FailIfUnavailable: refuse to start (rather than fall back to
	// unsandboxed) when the OS backend cannot run. Intended for managed
	// deployments that require sandboxing as a hard gate.
	FailIfUnavailable bool `mapstructure:"fail_if_unavailable" json:"fail_if_unavailable,omitempty" yaml:"fail_if_unavailable,omitempty"`

	// AutoAllowBashIfSandboxed: when true and the sandbox wraps the bash
	// call, skip the approval prompt. When false ("regular permissions
	// mode"), sandboxed bash still goes through the regular approval
	// flow.
	AutoAllowBashIfSandboxed *bool `` /* 135-byte string literal not displayed */

	// AllowUnsandboxedCommands: honor the per-call
	// dangerously_disable_sandbox arg. When false ("strict sandbox
	// mode") the arg is ignored.
	AllowUnsandboxedCommands *bool `` /* 129-byte string literal not displayed */

	// ExcludedCommands lists bash patterns that should run outside the
	// sandbox (convenience, not a security boundary).
	ExcludedCommands []string `mapstructure:"excluded_commands" json:"excluded_commands,omitempty" yaml:"excluded_commands,omitempty"`

	Filesystem SandboxFSConfig  `mapstructure:"filesystem" json:"filesystem,omitempty" yaml:"filesystem,omitempty"`
	Network    SandboxNetConfig `mapstructure:"network"    json:"network,omitempty"    yaml:"network,omitempty"`

	// IgnoreViolations: per-tool list of violation kinds to hide from
	// status/trace. Internal counts are still kept.
	IgnoreViolations map[string][]string `mapstructure:"ignore_violations" json:"ignore_violations,omitempty" yaml:"ignore_violations,omitempty"`

	// EnableWeakerNestedSandbox: allow running inside Docker without
	// privileged namespaces by bind-mounting the container's existing
	// /proc instead of mounting a fresh one. Documented as weaker.
	EnableWeakerNestedSandbox bool `` /* 135-byte string literal not displayed */

	// EnableWeakerNetworkIsolation: macOS only; allow access to
	// com.apple.trustd.agent so Go-based CLIs can verify TLS through a
	// MITM proxy. Documented as weaker.
	EnableWeakerNetworkIsolation bool `` /* 144-byte string literal not displayed */
}

SandboxConfig is the "sandbox" block of settings.yaml (and policy.yaml). Key names mirror Claude Code's sandbox schema (snake_case per CLAUDE.md §6.1) so users and operators familiar with that product can port configuration directly. Detail design lives in docs/design/sandbox-boundaries.md.

Phase A only loads and resolves these values — nothing enforces them yet.

func LoadPolicySandbox

func LoadPolicySandbox() (SandboxConfig, error)

LoadPolicySandbox reads the sandbox block of <BUILDMAX_HOME>/policy.yaml. A missing file is not an error — returns (SandboxConfig{}, nil) so callers can merge unconditionally.

func (SandboxConfig) EffectiveAllowUnsandboxed

func (c SandboxConfig) EffectiveAllowUnsandboxed() bool

EffectiveAllowUnsandboxed returns the resolved AllowUnsandboxedCommands flag, applying the documented default (true) when unset.

func (SandboxConfig) EffectiveAutoAllowBash

func (c SandboxConfig) EffectiveAutoAllowBash() bool

EffectiveAutoAllowBash returns the resolved AutoAllowBashIfSandboxed flag, applying the documented default (true) when unset.

func (SandboxConfig) EffectiveMode

func (c SandboxConfig) EffectiveMode() string

EffectiveMode returns "auto_allow" or "regular" based on the resolved AutoAllowBashIfSandboxed flag. Returns "" when the sandbox is disabled.

func (SandboxConfig) IgnoredViolationsFor

func (c SandboxConfig) IgnoredViolationsFor(toolName string) []string

IgnoredViolationsFor returns the violation patterns suppressed for the named tool. Lookup is case-insensitive because Viper lowercases map keys when loading YAML; users may write "Bash:" matching the tool name.

type SandboxFSConfig

type SandboxFSConfig struct {
	AllowWrite []string `mapstructure:"allow_write" json:"allow_write,omitempty" yaml:"allow_write,omitempty"`
	DenyWrite  []string `mapstructure:"deny_write"  json:"deny_write,omitempty"  yaml:"deny_write,omitempty"`
	AllowRead  []string `mapstructure:"allow_read"  json:"allow_read,omitempty"  yaml:"allow_read,omitempty"`
	DenyRead   []string `mapstructure:"deny_read"   json:"deny_read,omitempty"   yaml:"deny_read,omitempty"`

	// AllowManagedReadPathsOnly: policy-only knob. When set in
	// policy.yaml, lower sources' allow_read entries are ignored;
	// deny_read still merges from every source.
	AllowManagedReadPathsOnly bool `` /* 138-byte string literal not displayed */
}

SandboxFSConfig mirrors Claude Code's sandbox.filesystem schema. Paths follow standard conventions documented in docs/design/sandbox-boundaries.md §4.3.

type SandboxNetConfig

type SandboxNetConfig struct {
	AllowedDomains      []string `mapstructure:"allowed_domains"        json:"allowed_domains,omitempty"        yaml:"allowed_domains,omitempty"`
	DeniedDomains       []string `mapstructure:"denied_domains"         json:"denied_domains,omitempty"         yaml:"denied_domains,omitempty"`
	AllowUnixSockets    []string `mapstructure:"allow_unix_sockets"     json:"allow_unix_sockets,omitempty"     yaml:"allow_unix_sockets,omitempty"`
	AllowAllUnixSockets bool     `mapstructure:"allow_all_unix_sockets" json:"allow_all_unix_sockets,omitempty" yaml:"allow_all_unix_sockets,omitempty"`
	AllowLocalBinding   bool     `mapstructure:"allow_local_binding"    json:"allow_local_binding,omitempty"    yaml:"allow_local_binding,omitempty"`
	HTTPProxyPort       int      `mapstructure:"http_proxy_port"        json:"http_proxy_port,omitempty"        yaml:"http_proxy_port,omitempty"`
	SOCKSProxyPort      int      `mapstructure:"socks_proxy_port"       json:"socks_proxy_port,omitempty"       yaml:"socks_proxy_port,omitempty"`

	// AllowManagedDomainsOnly: policy-only knob. When set in
	// policy.yaml, lower sources' allowed_domains are ignored;
	// denied_domains still merges from every source.
	AllowManagedDomainsOnly bool `` /* 129-byte string literal not displayed */
}

SandboxNetConfig mirrors Claude Code's sandbox.network schema.

type SandboxResolution

type SandboxResolution struct {
	Config SandboxConfig

	// Sources lists every layer that contributed a non-default value, in
	// resolution order: "default", "settings", "policy", "env".
	Sources []string
}

SandboxResolution is the resolved sandbox plus a per-field source map for display via `buildmax sandbox status`.

func ResolveSandbox

func ResolveSandbox(global, policy SandboxConfig, surface SandboxSurface) SandboxResolution

ResolveSandbox merges settings + policy + env + surface defaults into a final SandboxConfig. Mirrors the layering in docs/design/sandbox-boundaries.md §4.1.

Precedence (highest wins for scalars; arrays union):

  1. Env (BUILDMAX_SANDBOX_ENABLED).
  2. Policy file.
  3. Settings file.
  4. Surface default.

For the managed-only flags (AllowManagedDomainsOnly, AllowManagedReadPathsOnly) set in policy.yaml, the corresponding allow array in lower sources is suppressed. Deny arrays always union.

type SandboxSurface

type SandboxSurface string

SandboxSurface names a runtime surface so ResolveSandbox can pick a surface-appropriate default. Surfaces differ on:

  • default Enabled (off for interactive local, on for the worker)
  • default FailIfUnavailable (off for interactive, on for worker)
  • default AllowUnsandboxedCommands (on for interactive, off for worker)
const (
	// SandboxSurfaceCLI is the local CLI/Desktop default surface. Defaults
	// match today's behavior: sandbox off unless the user opts in.
	SandboxSurfaceCLI SandboxSurface = "cli"
	// SandboxSurfaceWorker is the buildmax-worker default surface.
	// Defaults satisfy docs/design/trust-harness.md §3.2's "stricter than trusted local."
	SandboxSurfaceWorker SandboxSurface = "worker"
)

type SecretStatus

type SecretStatus struct {
	Set bool `json:"set"`
}

SecretStatus reports that a credential is configured, never anything about its value. Not a prefix, not a length, not a hash: each of those narrows a search for someone who has the response and wants the secret.

type ServerAuditConfig

type ServerAuditConfig struct {
	// RetentionDays expires audit events older than the window. Zero, the
	// default, keeps them forever.
	//
	// Keeping is the default because the trail is evidence, and a deployment
	// that never chose a retention policy has not decided to discard anything.
	// Setting this is a deliberate act with a cost, which is why the sweep
	// records what it removed: a trail that begins partway through then says
	// that policy shortened it, rather than leaving a reader to guess between
	// policy and loss.
	RetentionDays int `mapstructure:"retention_days"`
}

ServerAuditConfig decides how long the governance trail is kept.

type ServerConfig

type ServerConfig struct {
	LogLevel  string `mapstructure:"log_level"`
	Port      int    `mapstructure:"port"`
	JWTSecret string `mapstructure:"jwt_secret"`
	// AccessTokenTTL is how long a signed access token stays valid. It is not
	// stored anywhere, so this is also the window in which a leaked one still
	// works — shortening it costs nothing but refresh traffic.
	AccessTokenTTL time.Duration `mapstructure:"access_token_ttl"`
	// RefreshTokenTTL is how long a login can be renewed without a new login
	// code. Every rotation restarts it, so an active session lives on and an
	// abandoned one expires.
	RefreshTokenTTL time.Duration `mapstructure:"refresh_token_ttl"`
	// RefreshRotationGrace is how long a just-rotated refresh token may be
	// exchanged again before the server treats it as reuse and revokes the
	// session. It absorbs concurrent refreshes from processes sharing one
	// credentials file; it is not a security setting to raise casually.
	RefreshRotationGrace time.Duration `mapstructure:"refresh_rotation_grace"`
	// AllowSignup opens POST /api/otp/request to self-registration. It defaults
	// to false, and the zero value is the safe one on purpose: a server that
	// forgets to configure this is closed, not open.
	AllowSignup      bool                `mapstructure:"allow_signup"`
	CORSOrigin       string              `mapstructure:"cors_origin"`
	WorkspacesDir    string              `mapstructure:"workspaces_dir"`
	DefaultQuotaTier string              `mapstructure:"default_quota_tier"`
	Conversation     ServerConvConfig    `mapstructure:"conversation"`
	LLM              ServerLLMConfig     `mapstructure:"llm"`
	Database         ServerDBConfig      `mapstructure:"database"`
	Webhook          ServerWebhookConfig `mapstructure:"webhook"`
	Worker           ServerWorkerConfig  `mapstructure:"worker"`
	Storage          ServerStorageConfig `mapstructure:"storage"`
	Audit            ServerAuditConfig   `mapstructure:"audit"`
}

ServerConfig is the root of BUILDMAX_HOME/server.yaml.

func LoadServerConfig

func LoadServerConfig() (ServerConfig, error)

LoadServerConfig reads BUILDMAX_HOME/server.yaml via Viper and applies defaults. Secret-bearing fields can be overridden by the environment variables above. A missing file is not an error — returns a config with all defaults applied.

func (ServerConfig) Redacted

func (sc ServerConfig) Redacted() RedactedServerConfig

Redacted returns the operator-facing view of the configuration.

type ServerConvConfig

type ServerConvConfig struct {
	Model ServerModelEntry `mapstructure:"model"`
	// ModelTarget names a catalog model (an llm_model row) to use for Tier 1
	// inference instead of the model above. It is a catalog ID, not a team
	// alias: the server picks its own model rather than being granted one.
	ModelTarget string `mapstructure:"model_target"`
}

ServerConvConfig holds Tier 1 conversation LLM settings.

type ServerDBConfig

type ServerDBConfig struct {
	Host     string `mapstructure:"host"`
	Port     int    `mapstructure:"port"`
	User     string `mapstructure:"user"`
	Password string `mapstructure:"password"`
	Name     string `mapstructure:"name"`
	// TLS is the go-sql-driver tls parameter. Empty means DefaultDBTLSMode.
	//
	// A deployment pointing at a database it already runs — RDS, Aurora, Cloud
	// SQL — is the case this exists for: most managed MySQL either requires TLS
	// or is reached over a network where the credentials should not travel in
	// the clear.
	//
	// Values: "preferred" (TLS when the server offers it, certificate not
	// verified), "true" (require TLS and verify the certificate against the
	// system roots), "skip-verify" (require TLS, accept any certificate), or
	// "false" (never).
	TLS string `mapstructure:"tls"`
}

ServerDBConfig holds MySQL connection settings.

func (ServerDBConfig) DSN

func (d ServerDBConfig) DSN() string

DSN builds a MySQL DSN from the database config.

type ServerK8sConfig

type ServerK8sConfig struct {
	Namespace string `mapstructure:"namespace"`
	Image     string `mapstructure:"image"`
	// ConfigMap names a ConfigMap holding a server.yaml key. It is mounted into
	// every worker pod so the worker reads the same configuration the server does.
	// Empty means no config file is mounted and the worker relies on inherited
	// environment variables alone, which is rarely enough.
	ConfigMap string `mapstructure:"config_map"`
	// HomeDir is BUILDMAX_HOME inside a worker pod; server.yaml is mounted there.
	HomeDir string `mapstructure:"home_dir"`
	// RunAsUser is the uid a worker pod runs as. Zero uses BuildMax's default.
	// Set it on clusters that assign their own uid ranges, OpenShift most
	// commonly. The worker never needs a uid the image knows about — it writes
	// only into mounted volumes.
	RunAsUser int64 `mapstructure:"run_as_user"`
	// Resources bounds a worker pod. Empty leaves the matching request or limit
	// unset, so an existing deployment keeps running unbounded until an
	// operator chooses values.
	Resources ServerK8sResources `mapstructure:"resources"`
}

ServerK8sConfig holds Kubernetes worker job settings.

type ServerK8sResources

type ServerK8sResources struct {
	CPURequest    string `mapstructure:"cpu_request"`
	CPULimit      string `mapstructure:"cpu_limit"`
	MemoryRequest string `mapstructure:"memory_request"`
	MemoryLimit   string `mapstructure:"memory_limit"`
}

ServerK8sResources holds Kubernetes quantity strings for a worker pod.

type ServerLLMConfig

type ServerLLMConfig struct {
	// DefaultAlias is the alias used when a managed caller names none.
	DefaultAlias string `mapstructure:"default_alias"`
	// Aliases maps a stable alias such as "fast" to a target id in Targets.
	// Leaving it empty means no team may call the gateway.
	Aliases map[string]string `mapstructure:"aliases"`
}

ServerLLMConfig is the deployment-wide team model policy for the managed LLM gateway. The catalog itself lives in the llm_model table, edited with `buildmax-server model`, because it changes while the server runs.

It is optional. A server with no llm section still serves Tier 1 conversations from conversation.model, and grants no team any managed model.

type ServerMinIOConfig

type ServerMinIOConfig struct {
	Endpoint  string `mapstructure:"endpoint"`
	Region    string `mapstructure:"region"`
	AccessKey string `mapstructure:"access_key"`
	SecretKey string `mapstructure:"secret_key"`
	Bucket    string `mapstructure:"bucket"`
	Prefix    string `mapstructure:"prefix"`
	// PathStyle forces bucket-in-path addressing. Unset derives it from
	// endpoint: set means an S3-compatible store such as MinIO, which needs
	// path style; empty means real AWS S3, which does not. Set it explicitly
	// for a compatible store that uses virtual-host addressing.
	PathStyle *bool `mapstructure:"path_style"`
}

ServerMinIOConfig holds MinIO/S3 connection settings.

type ServerModelEntry

type ServerModelEntry struct {
	Model         string `mapstructure:"model"`
	Name          string `mapstructure:"name"`
	APIURL        string `mapstructure:"api_url"`
	APIKey        string `mapstructure:"api_key"`
	ContextWindow int    `mapstructure:"context_window"`
	CallTimeout   int    `mapstructure:"call_timeout"` // seconds; 0 = uses DefaultCallTimeoutSecs
	MaxTokens     int    `mapstructure:"max_tokens"`   // 0 = the adapter's own default
	// Provider is the wire protocol this model speaks. Empty means
	// LLMProviderOpenAICompatible.
	Provider string `mapstructure:"provider"`
	// Reasoning is the effort level: off (the default), low, medium, or high.
	Reasoning string `mapstructure:"reasoning"`
	// PromptCache caches the stable prefix of a request.
	PromptCache bool `mapstructure:"prompt_cache"`
	// Vision says this model accepts image input.
	Vision bool `mapstructure:"vision"`
}

ServerModelEntry is the LLM model used for Tier 1 conversation.

func (ServerModelEntry) RuntimeModelEntry

func (m ServerModelEntry) RuntimeModelEntry() ModelEntry

RuntimeModelEntry converts the server's resolved model configuration into the model shape used by the shared agent runtime. Environment overrides have already been applied before this conversion, so credentials stay in memory.

The result is always a direct entry: a worker runs the server's own model with the server's own credential, and does not call the gateway. Worker adoption of managed inference is separate work — see docs/design/llm-gateway.md section 15.

type ServerStorageConfig

type ServerStorageConfig struct {
	PersistBackend  string            `mapstructure:"persist_backend"`
	ArtifactBackend string            `mapstructure:"artifact_backend"`
	MinIO           ServerMinIOConfig `mapstructure:"minio"`
	// MaxArtifactMB caps one artifact upload. Zero uses the built-in default.
	//
	// It is a per-file limit and deliberately not a team storage allowance: a
	// stock of bytes held is a different measurement from the rates the quota
	// model records, and it waits for its own decision. What this already
	// settles is that one request cannot cost the deployment unbounded disk.
	MaxArtifactMB int `mapstructure:"max_artifact_mb"`
}

ServerStorageConfig holds blob storage backend selection and MinIO settings.

type ServerWebhookConfig

type ServerWebhookConfig struct {
	MessagePath string `mapstructure:"message_path"`
	UserID      string `mapstructure:"user_id"`
}

ServerWebhookConfig holds webhook handler options.

type ServerWorkerConfig

type ServerWorkerConfig struct {
	Binary    string                `mapstructure:"binary"`
	RunMode   string                `mapstructure:"run_mode"`
	Token     string                `mapstructure:"token"`
	ServerURL string                `mapstructure:"server_url"`
	LLM       ServerWorkerLLMConfig `mapstructure:"llm"`
	K8s       ServerK8sConfig       `mapstructure:"k8s"`
	// RunTokenTTL bounds a run token. Zero uses authtoken's default. It has to
	// outlast the longest run: the token is not renewable, so a run that outlives
	// it can no longer report anything, including its own result.
	RunTokenTTL time.Duration `mapstructure:"run_token_ttl"`
	// RunTimeout is how long a run may stay SCHEDULED or RUNNING before the
	// server records it as abandoned. Zero uses the scheduler's default.
	//
	// Only the worker moves a run out of those states, so without this a run
	// whose worker died stays there forever. Keep it at or below RunTokenTTL: a
	// run that outlived its credential cannot report an outcome, so nothing else
	// will ever close it.
	RunTimeout time.Duration `mapstructure:"run_timeout"`
}

ServerWorkerConfig holds worker launch and connection options.

type ServerWorkerLLMConfig

type ServerWorkerLLMConfig struct {
	// Transport is TransportDirect or TransportBuildMax. Empty means direct,
	// which is what every existing deployment gets.
	Transport string `mapstructure:"transport"`
	// Alias is the team model alias a managed run calls. Empty uses the team's
	// default alias.
	Alias string `mapstructure:"alias"`
	// ContextWindow and CallTimeout describe the alias to the run. The protocol
	// does not report them per call, so they come from configuration or stay
	// unset.
	ContextWindow int `mapstructure:"context_window"`
	CallTimeout   int `mapstructure:"call_timeout"`
}

ServerWorkerLLMConfig decides how a task run reaches a model.

The choice is the operator's and lives on the server, not in the worker's hands: a worker executes model-chosen code, so it is told which transport and alias to use rather than selecting them.

func (ServerWorkerLLMConfig) Managed

func (c ServerWorkerLLMConfig) Managed() bool

Managed reports whether task runs call the gateway instead of a provider.

type Settings

type Settings struct {
	LogLevel  string          `mapstructure:"log_level"`
	ServerURL string          `mapstructure:"server_url"`
	Models    []ModelEntry    `mapstructure:"models"`
	Hooks     corehook.Config `mapstructure:"hooks"`
	Sandbox   SandboxConfig   `mapstructure:"sandbox"`
	Tools     ToolsConfig     `mapstructure:"tools"`
	Agent     AgentConfig     `mapstructure:"agent"`
}

Settings is the root structure for settings.yaml (BUILDMAX_HOME/settings.yaml). Used by the CLI and desktop app.

func LoadSettings

func LoadSettings() (Settings, error)

LoadSettings reads BUILDMAX_HOME/settings.yaml via Viper. A missing file is not an error — returns (Settings{}, nil) so callers fall back gracefully.

type ToolsConfig

type ToolsConfig struct {
	// Permissions maps a tool key to allow, ask, or deny. A key is a tool name
	// ("Write") or a tool name plus the target it dispatches to
	// ("CallMcpTool:github/create_issue"), optionally with a trailing "*".
	Permissions map[string]string `mapstructure:"permissions" json:"permissions,omitempty" yaml:"permissions,omitempty"`
}

ToolsConfig is the "tools" block of settings.yaml.

type WorkspaceStorageConfig

type WorkspaceStorageConfig struct {
	PersistProvider  string
	ArtifactProvider string
	Endpoint         string
	Region           string
	AccessKey        string
	SecretKey        string
	Bucket           string
	Prefix           string
	// PathStyle forces bucket-in-path addressing on or off. Nil derives it
	// from Endpoint, which is the right answer for both of the cases that
	// actually occur — see bootstrap.BuildS3Client.
	PathStyle *bool
}

WorkspaceStorageConfig holds resolved provider selection and S3/MinIO connection settings. Populated from ServerStorageConfig by bootstrap; not read from env directly.

Jump to

Keyboard shortcuts

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