permconfig

package
v0.0.35 Latest Latest
Warning

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

Go to latest
Published: Sep 11, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Overview

Package permconfig is the file-based permission-config adapter (issue #13). It loads `.mecatl/settings.yaml`-style permission rules from disk and from Claude-Code-compatible `settings.json`, and resolves them PER SESSION against each session's workspace root via a permpolicy.RuleResolver.

What it produces

Every loaded entry becomes a governance.Rule tagged with a config Scope (project vs user) and an Audience (issue #32): top-level allow/ask bind the MAIN engine only, the `subagent:` block binds child engines only, and top-level deny binds BOTH (a deny only tightens). The Resolver hands those rules to the policy on the SAME lowest-scope `extra` channel the learned-rule store rides — so a config rule never out-ranks a static/configured deny or ask, and a config Allow can only LOOSEN the built-in Ask floor (ScopeBuiltinDefault), never a deny.

The trust gate

Project-level config is part of the repository the model is editing, so its ALLOW rules are gated behind a trust flag: an UNTRUSTED project's allows are dropped (the rule reverts to the built-in Ask/whatever a higher scope says), while its DENY and ASK rules are ALWAYS honoured (they only tighten). User- global config (under the user's XDG dir / home) is the operator's own and is always fully trusted. See Resolver.

Layering

permconfig is an ADAPTER: it reads the filesystem (project files via the session tool.Workspace, user-global files via an injectable env), parses YAML/ JSON, and emits domain governance.Rule values. It implements permpolicy.RuleResolver and is wired in internal/app. It imports no other adapter and is never imported by the domain/port/agent layers.

Index

Constants

View Source
const MaxContextWindowTokens = 2_000_000

MaxContextWindowTokens is the sane upper bound for configured and live model context windows. It is deliberately shared with composition's live metadata validation so either source cannot disable compaction with an absurd value.

View Source
const (

	// UserSettingsRelPath is the user-level (operator-tier) YAML config location,
	// relative to the XDG config base (joined under <XDG_CONFIG_HOME>/...). It is the
	// SINGLE definition of where the operator settings.yaml lives: the resolver READS
	// it here (loadUserRules), and `mecated config init` WRITES it via this same const
	// (re-exported through internal/configgen), so the read and write paths can never
	// resolve different files.
	UserSettingsRelPath = "mecatl/settings.yaml"
)

Conventional permission-config locations. Project files are discovered under the session workspace root; user-global files under the XDG config dir / home. The project tier splits into SHARED (checked-in) and LOCAL (gitignored, personal), mirroring the Scope ordering ScopeLocalProject > ScopeSharedProject.

Variables

This section is empty.

Functions

func ParseRetentionDuration

func ParseRetentionDuration(raw string) (time.Duration, error)

ParseRetentionDuration parses a retention/sweep-cadence duration string. An empty or "0" value means disabled (0, nil); anything else must be a valid, non-negative time.ParseDuration value.

func ValidateYAML

func ValidateYAML(data []byte) error

ValidateYAML validates a complete settings document with the same bounded parser used when loading operator and project configuration. It does not expose parsed values or mutate configuration state.

Types

type Config

type Config struct {
	// Providers holds strict, operator-tier custom LLM provider definitions. Project
	// values are ignored by Resolver with a value-free warning.
	Providers ProviderDefinitions `yaml:"providers"`
	// ProviderOverrides holds strict operator-tier endpoint overrides for eligible
	// built-in providers. It is never accepted from a project workspace.
	ProviderOverrides ProviderOverrides `yaml:"provider_overrides"`
	// Permissions holds the allow/ask/deny rule-spec lists plus the child-scoped
	// `subagent:` block.
	Permissions Permissions `yaml:"permissions"`
	// Guardrails holds the OPERATOR-TIER LLM-content-checker config (issue #27). It
	// is parsed STRICTLY (unknown sub-keys = error, like permissions:) so a typo
	// cannot silently disable a guardrail. It is honoured ONLY from the user-global +
	// CLI tiers; a project-tier file's guardrails: block is IGNORED with a WARN (a
	// project repo weakening/disabling a checker is a security DOWNGRADE — the usual
	// tighten-only gate reverses here). The presence flag (whether the key appeared at
	// all) is tracked via GuardrailsPresent so the resolver can WARN about an ignored
	// project block. A nil Guardrails means the key was absent.
	Guardrails *GuardrailsSection `yaml:"guardrails"`
	// Posture is the OPERATOR-TIER posture-ladder scalar (the graduated trust/
	// automation tier: strict/trusted/auto/yolo). Like Guardrails it is honoured ONLY
	// from the user-global + CLI tiers; a project-tier file's posture: key is IGNORED
	// with a WARN (a project repo RAISING the automation posture — e.g. posture: yolo
	// — is a security DOWNGRADE the tighten-only project gate forbids, the fail-closed
	// core of this feature). Empty = absent (the resolver returns "" and composition
	// keeps the CLI/default). The composition layer parses the string; permconfig only
	// reads the scalar.
	Posture string `yaml:"posture"`
	// Models holds the per-slot model config (ADR 0030): the `models.slots` /
	// `models.aliases` maps, the session `default`, and the operator-tier `allowlist`
	// cap. At the OPERATOR tier (user-global + CLI) all fields are honoured. At the
	// PROJECT tier (Phase 4) a models: block is honoured WITHIN the operator allowlist
	// on a TRUSTED workspace (slots/aliases/default only); with no operator allowlist
	// it stays WARN-ignored (the opt-in — byte-identical to pre-Phase-4), and a
	// project-tier allowlist: key is always ignored with a WARN (non-wideable cap).
	// The TOP `models:` mapping is parsed STRICTLY (an unknown key like `slotz:`
	// errors), the inner slots/aliases maps stay free-form (composition validates the
	// slot keys fail-soft). A nil Models means the key was absent. The composition
	// layer reads the maps; permconfig only carries them.
	Models *ModelsSection `yaml:"models"`
	// ReasoningEffort is the OPERATOR-TIER reasoning-effort scalar (ADR 0055: the
	// neutral vocabulary "" / "auto" / "low" / "medium" / "high" / "xhigh" / "max").
	// Like Posture it is honoured ONLY from the user-global + CLI tiers; a
	// project-tier file's reasoning-effort: key is IGNORED with a WARN
	// (operator-tier only, for consistency — a project cannot raise the model's
	// reasoning spend). Empty = absent (the resolver returns "" and composition uses
	// the provider default). The composition layer interprets + clamps the token;
	// permconfig only reads the scalar.
	ReasoningEffort string `yaml:"reasoning-effort"`
	// PlanModeAutoApprove is the OPERATOR-TIER plan-mode-auto-approve flag (issue
	// #206 Wave 6a). Like Posture/ReasoningEffort it is honoured ONLY
	// from the user-global + CLI tiers; a project-tier file's plan-mode-auto-approve:
	// key is IGNORED with a WARN (operator-tier only — a project repo enabling
	// autonomous plan approval is a security DOWNGRADE). false = absent (the resolver
	// returns false and composition keeps the default OFF). The composition layer
	// interprets the bool; permconfig only reads the scalar.
	PlanModeAutoApprove bool `yaml:"plan-mode-auto-approve"`
	// Learning configures optional completed-trajectory observation. The subtree is
	// strict; composition parses the closed off/review/auto mode vocabulary.
	Learning *LearningSection `yaml:"learning"`
	// Steer is the OPERATOR-TIER mid-run steer knob (steer-while-running, issue
	// #512): enable (default) or disable the mid-run steer inbox. Like
	// Posture/ReasoningEffort it is honoured ONLY from the user-global + CLI tiers;
	// a project-tier file's steer: key is IGNORED with a WARN (operator-tier only —
	// the harness's operator surface is not a project repo's to flip, in either
	// direction). It is a *bool so ABSENT is distinguishable from an explicit false:
	// nil = absent (the resolver reports not-present and composition keeps the
	// DEFAULT-ON); a non-nil value is honoured (composition maps steer: false onto
	// the opt-OUT DisableSteer).
	Steer *bool `yaml:"steer"`
	// OpenRouter holds the OPERATOR-TIER OpenRouter downstream-provider routing
	// config (issue #480): a per-model preferred DOWNSTREAM provider order, sent as
	// OpenRouter's `provider` request-body object. Like Guardrails/Posture it is
	// honoured ONLY from the user-global + CLI tiers; a project-tier file's
	// openrouter: block is IGNORED with a WARN (steering requests to a particular
	// downstream is a spend/compliance/capability decision the operator owns — the
	// same operator-only discipline as default_provider/allowlist/router). It is
	// parsed STRICTLY (unknown sub-keys = error, like guardrails:/router:) so a
	// typo cannot silently drop a routing preference. A nil OpenRouter means the
	// key was absent. The composition layer reads + validates the maps; permconfig
	// only carries them.
	OpenRouter *OpenRouterSection `yaml:"openrouter"`
	// MCP holds named global Streamable HTTP MCP server profiles. It is strict and
	// OPERATOR-TIER ONLY: project files cannot choose endpoints, authentication,
	// credential references, or egress policy. Values are metadata only; parsing
	// never reads an environment variable, opens a credential store, or performs I/O.
	MCP *MCPSection `yaml:"mcp"`
	// Retention is the strict, versioned operator-only automatic session cleanup policy.
	// Project-tier values are ignored; explicit CLI flags remain the highest precedence.
	Retention *RetentionSection `yaml:"retention"`
	// StorageManagement names the verified OIDC identities allowed to operate on
	// process-wide storage. It is strict and operator-tier only.
	StorageManagement *StorageManagementSection `yaml:"storage_management"`
	// TemporaryStorage controls managed command temporary storage. It is strict and
	// read exclusively from the user-global settings.yaml; project-tier and explicit
	// CLI configuration values are ignored by the Resolver.
	TemporaryStorage *TemporaryStorageSection `yaml:"temporary_storage"`
}

Config is the on-disk `.mecatl/settings.yaml` (or user-global settings.yaml) schema for file-based permissions. It is intentionally a small mirror of the Claude-Code permissions shape so a user familiar with one can read the other.

Each list entry is a RULE SPEC string of the form "Tool(pattern)" or bare "Tool" (tool-wide). For Shell the pattern is a command glob, e.g. "Shell(go test*)". See parseSpec / normalizeGlob for the exact grammar.

The TOP level of Config stays LENIENT (other keys — trustedWorkspaces etc. — must keep parsing); strictness applies only INSIDE the permissions: subtree, where a typo'd key would silently disable a rule list (see the custom UnmarshalYAML on Permissions / SubagentPermissions).

func (*Config) UnmarshalYAML added in v0.0.22

func (c *Config) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes known top-level sections while preserving the lenient top-level compatibility contract.

type ContextWindows

type ContextWindows map[string]map[string]int

ContextWindows is the operator-owned exact provider → final model → token map. Its custom decoder keeps type/range failures attributable to the precise entry; yaml's generic nested-map error otherwise reports only a line number.

func (*ContextWindows) UnmarshalYAML

func (c *ContextWindows) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes and validates each configured context window with its full path.

type GuardrailRuleSpec

type GuardrailRuleSpec struct {
	// Match is the tool-name matcher (exact / "prefix*" / "*").
	Match string `yaml:"match"`
	// Phases lists "pre"/"post"; empty = both.
	Phases []string `yaml:"phases"`
	// Mode is "block"/"sanitize"/"advisory"; empty defaults to block.
	Mode string `yaml:"mode"`
	// Prompt overrides the built-in inspection rubric.
	Prompt string `yaml:"prompt"`
	// FailClosed flips the fail-open default for enforcing modes.
	FailClosed bool `yaml:"failClosed"`
	// FailClosedPresent reports whether the failClosed key was explicitly set in
	// the YAML — a bool can't distinguish "false" from "not set", so this lets the
	// global onCheckerDown toggle distinguish a per-rule explicit opt-out from an
	// unset rule that should inherit the global.
	FailClosedPresent bool `yaml:"-"`
}

GuardrailRuleSpec is one operator-tier guardrail rule as parsed from YAML. It is the on-disk mirror of app.GuardrailRule; composition maps the two. Parsed strictly.

func (*GuardrailRuleSpec) UnmarshalYAML

func (r *GuardrailRuleSpec) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes a guardrails rule mapping STRICTLY.

type GuardrailsSection

type GuardrailsSection struct {
	// Model is the checker model id / alias. Empty leaves the CLI --guardrails-model
	// to supply it; a value here is overridden by the CLI flag when both are set.
	Model string `yaml:"model"`
	// MinContentBytes skips the checker for content shorter than this. 0 = check all.
	MinContentBytes int `yaml:"minContentBytes"`
	// Disabled is the YAML-level kill switch (the CLI --guardrails=off also sets it).
	Disabled bool `yaml:"disabled"`
	// OnCheckerDown sets the global posture when the checker model is unavailable
	// (error/timeout): "warn" (default, fail-open) or "fail" (fail-closed for all
	// rules). Per-rule failClosed overrides: failClosed:true tightens even under
	// warn; failClosed:false (explicit) loosens even under fail. Empty = warn.
	OnCheckerDown string `yaml:"onCheckerDown"`
	// DefaultMode sets the enforcement mode for the built-in default rules when no
	// explicit rules are configured: "block" (default), "advisory", or "sanitize".
	// An explicit rules list replaces the defaults entirely (this key is ignored).
	DefaultMode string `yaml:"defaultMode"`
	// Escape is the ADR-0080 escape knob: when true AND a checker model is
	// configured, an out-of-root FS escape at posture auto routes through the
	// guardrail checker (an unsafe verdict denies; a checker error fails closed
	// to the write-escape Ask). Default false = the un-routed posture table.
	Escape bool `yaml:"escape"`
	// Rules is the guardrail rule list.
	Rules []GuardrailRuleSpec `yaml:"rules"`
}

GuardrailsSection is the operator-tier `guardrails:` YAML subtree (issue #27): a checker model, a master-disable, and the rule list. It is parsed STRICTLY (unknown keys error).

func (*GuardrailsSection) UnmarshalYAML

func (g *GuardrailsSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the guardrails: mapping STRICTLY (issue #27): an unknown key inside the guardrails subtree is a parse error — a typo like `moddel:` or `rulez:` must not silently disable a guardrail. Same rationale as Permissions.UnmarshalYAML.

type LearningAutomaticSection

type LearningAutomaticSection struct {
	// Cooldown is the per-principal weighted-admission cooldown; zero disables it.
	Cooldown time.Duration `yaml:"cooldown"`
	// Window is the sliding count/token window, strictly 1m..24h.
	Window time.Duration `yaml:"window"`
	// MaxReflections is the global count cap; zero disables automatic reflection.
	MaxReflections int `yaml:"max_reflections"`
	// MaxTokens is the global reserved-token cap; zero disables automatic reflection.
	MaxTokens int `yaml:"max_tokens"`
	// MaxReflectionsPerPrincipal is the per-principal count cap; zero disables automatic reflection.
	MaxReflectionsPerPrincipal int `yaml:"max_reflections_per_principal"`
	// MaxTokensPerPrincipal is the per-principal reserved-token cap; zero disables automatic reflection.
	MaxTokensPerPrincipal int `yaml:"max_tokens_per_principal"`
}

LearningAutomaticSection is the strict automatic-admission budget. Standard composition enforces it through the selected durable ledger.

func (*LearningAutomaticSection) UnmarshalYAML

func (s *LearningAutomaticSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes and bounds the automatic-admission policy.

type LearningSection

type LearningSection struct {
	// Mode controls automatic completed-trajectory observation: off (default; no
	// automatic reflection), review (signal-gated reflection stages durable proposals
	// without memory writes), or auto (stage first, then conservatively promote only
	// eligible non-conflicting facts). Operator settings establish the ceiling;
	// project settings may only tighten it under off < review < auto and never raise
	// autonomy. It does not override separately configured maintenance schedules such
	// as --user-model-consolidate-interval.
	Mode string `yaml:"mode"`
	// Sensitivity controls weighted automatic admission. Empty means balanced.
	Sensitivity string `yaml:"sensitivity"`
	// Skills controls learned-skill lifecycle policy.
	Skills *LearningSkillsSection `yaml:"skills"`
	// Automatic is operator-only admission policy. Standard non-off composition
	// applies it through a durable ledger, making count/token windows, cooldown,
	// and deduplication deployment-wide across cooperating processes.
	Automatic *LearningAutomaticSection `yaml:"automatic"`
}

LearningSection is the strict learning: settings subtree.

func (*LearningSection) UnmarshalYAML

func (s *LearningSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes learning.mode and validates its closed vocabulary.

type LearningSkillsSection

type LearningSkillsSection struct {
	// Activation is validated (default for Auto) or evaluated. Project settings
	// may only tighten validated to evaluated.
	Activation string `yaml:"activation"`
}

LearningSkillsSection is the strict learned-skill policy subtree.

func (*LearningSkillsSection) UnmarshalYAML

func (s *LearningSkillsSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes learning.skills.activation.

type MCPAuthProfile

type MCPAuthProfile struct {
	// Mode is exactly none, static_bearer, or oauth.
	Mode string `yaml:"mode"`
	// StaticBearer names the bearer-token environment reference.
	StaticBearer *MCPStaticBearerProfile `yaml:"static_bearer"`
	// OAuth declares the OAuth identity, client, credentials, scopes, and network policy.
	OAuth *MCPOAuthProfile `yaml:"oauth"`
}

MCPAuthProfile is a closed tagged union. none has no payload; the other modes require exactly their matching payload and reject cross-variant fields.

func (*MCPAuthProfile) UnmarshalYAML

func (a *MCPAuthProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes the closed MCP authentication union.

type MCPBrokerProfile added in v0.0.26

type MCPBrokerProfile struct {
	// CallbackURL is required exactly when broker mode contains an OAuth route. It must be an absolute HTTPS URL without userinfo, query, or fragment; an omitted path or / is normalized to /.
	CallbackURL string `yaml:"callback_url"`
}

MCPBrokerProfile contains broker-only trusted configuration.

func (*MCPBrokerProfile) UnmarshalYAML added in v0.0.26

func (b *MCPBrokerProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes broker-only metadata. Selection-specific validation belongs to the canonical authority resolver.

type MCPCIMDClientProfile

type MCPCIMDClientProfile struct {
	// DocumentURL is the required HTTPS metadata-document URL.
	DocumentURL string `yaml:"document_url"`
}

MCPCIMDClientProfile contains the HTTPS client-id metadata document URL.

func (*MCPCIMDClientProfile) UnmarshalYAML

func (c *MCPCIMDClientProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes CIMD client metadata.

type MCPDCRClientProfile added in v0.0.27

type MCPDCRClientProfile struct {
	// DiscoveryURL is the required HTTPS authorization-server metadata URL.
	DiscoveryURL string `yaml:"discovery_url"`
}

MCPDCRClientProfile contains the HTTPS RFC 8414 discovery document URL.

func (*MCPDCRClientProfile) UnmarshalYAML added in v0.0.27

func (c *MCPDCRClientProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes an HTTPS RFC 8414 discovery document URL.

type MCPEnvironmentCredentialProfile

type MCPEnvironmentCredentialProfile struct {
	// CredentialEnv is a MECATL_* environment variable containing the opaque credential record.
	CredentialEnv string `yaml:"credential_env"`
	// AllowProcessLocalRefresh permits refreshed credentials to live only in this process.
	AllowProcessLocalRefresh bool `yaml:"allow_process_local_refresh"`
}

MCPEnvironmentCredentialProfile declares a read-only environment credential source.

func (*MCPEnvironmentCredentialProfile) UnmarshalYAML

func (c *MCPEnvironmentCredentialProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes environment credential metadata.

type MCPLocalCredentialProfile

type MCPLocalCredentialProfile struct {
	// Root is the required absolute credential-store root.
	Root string `yaml:"root"`
	// KeyEnv is a MECATL_* environment variable name containing the encryption key.
	KeyEnv string `yaml:"key_env"`
}

MCPLocalCredentialProfile declares local encrypted credential persistence metadata.

func (*MCPLocalCredentialProfile) UnmarshalYAML

func (c *MCPLocalCredentialProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes local credential-store metadata.

type MCPOAuth2UpstreamProfile added in v0.0.26

type MCPOAuth2UpstreamProfile struct {
	AuthorizationEndpoint string `yaml:"authorization_endpoint"`
	// TokenEndpoint is a canonical HTTPS URL with no query string or fragment:
	// the hardened runtime token client pins the exact origin and controls the
	// request query itself.
	TokenEndpoint string `yaml:"token_endpoint"`
}

MCPOAuth2UpstreamProfile contains trusted explicit generic OAuth2 endpoints.

func (*MCPOAuth2UpstreamProfile) UnmarshalYAML added in v0.0.26

func (u *MCPOAuth2UpstreamProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes explicit generic OAuth2 endpoints.

type MCPOAuthClientProfile

type MCPOAuthClientProfile struct {
	// Mode is exactly preregistered, cimd, or dcr.
	Mode string `yaml:"mode"`
	// Preregistered declares a confidential client registered with the issuer.
	Preregistered *MCPPreregisteredClientProfile `yaml:"preregistered"`
	// CIMD declares an HTTPS client-id metadata document URL.
	CIMD *MCPCIMDClientProfile `yaml:"cimd"`
	// DCR declares an RFC 8414 metadata URL for RFC 7591 registration.
	DCR *MCPDCRClientProfile `yaml:"dcr"`
}

MCPOAuthClientProfile is a closed preregistered/CIMD/DCR tagged union.

func (*MCPOAuthClientProfile) UnmarshalYAML

func (c *MCPOAuthClientProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes the closed preregistered/CIMD/DCR client union.

type MCPOAuthCredentialProfile

type MCPOAuthCredentialProfile struct {
	// Mode is exactly local or environment.
	Mode string `yaml:"mode"`
	// Local declares encrypted mutable credentials rooted at an absolute path.
	Local *MCPLocalCredentialProfile `yaml:"local"`
	// Environment declares one externally provisioned read-only credential record.
	Environment *MCPEnvironmentCredentialProfile `yaml:"environment"`
}

MCPOAuthCredentialProfile is a closed local/environment tagged union.

func (*MCPOAuthCredentialProfile) UnmarshalYAML

func (c *MCPOAuthCredentialProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes the closed local/environment credential union.

type MCPOAuthNetworkProfile

type MCPOAuthNetworkProfile struct {
	// AdditionalOrigins lists canonical exact origins additionally allowed for OAuth traffic.
	AdditionalOrigins []string `yaml:"additional_origins"`
	// PrivateOrigins lists allowed origins that may resolve only to RFC1918 IPv4 or ULA IPv6 addresses. Loopback, link-local, metadata, unspecified, multicast, mapped, public, and other special addresses remain denied.
	PrivateOrigins []string `yaml:"private_origins"`
	// MaxRedirects is the redirect bound, from zero through five.
	MaxRedirects int `yaml:"max_redirects"`
}

MCPOAuthNetworkProfile is the required immutable OAuth egress policy.

func (*MCPOAuthNetworkProfile) UnmarshalYAML

func (n *MCPOAuthNetworkProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes immutable OAuth network policy metadata.

type MCPOAuthProfile

type MCPOAuthProfile struct {
	// Profile is the required global-mode credential identity profile and is forbidden in broker mode.
	Profile string `yaml:"profile"`
	// Principal is the required global-mode credential identity principal and is forbidden in broker mode.
	Principal string `yaml:"principal"`
	// Issuer is the canonical exact origin used by OIDC discovery. It is forbidden
	// when Upstream explicitly selects generic OAuth2.
	Issuer string `yaml:"issuer"`
	// Upstream optionally selects OIDC discovery or explicit generic OAuth2.
	// Omitted defaults to OIDC.
	Upstream *MCPOAuthUpstreamProfile `yaml:"upstream"`
	// Client selects exactly one preregistered, CIMD, or DCR client declaration.
	Client MCPOAuthClientProfile `yaml:"client"`
	// Scopes is the non-empty allowlist of OAuth scopes the client may request.
	Scopes []string `yaml:"scopes"`
	// RequestRefreshToken asks the authorization server for refresh capability.
	RequestRefreshToken bool `yaml:"request_refresh_token"`
	// Credentials selects one global-mode local or environment credential source and is forbidden in broker mode.
	Credentials MCPOAuthCredentialProfile `yaml:"credentials"`
	// Network is required. Global profiles enforce its exact-origin egress policy;
	// broker OAuth accepts only an explicit empty mapping until ToolHive can enforce it equivalently.
	Network *MCPOAuthNetworkProfile `yaml:"network"`
	// Tools optionally declares this protected backend's tool catalogue
	// statically. Declarations are visible before connection; the first call
	// starts ToolHive's aggregate authorization for every protected backend.
	// The granted bundle unlocks the declared surface only. Omitted, the backend
	// remains discoverable only through pre-prompt workspace enrollment.
	Tools []MCPStaticToolProfile `yaml:"tools"`
}

MCPOAuthProfile is the metadata-only OAuth configuration for one server.

func (*MCPOAuthProfile) UnmarshalYAML

func (o *MCPOAuthProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes lossless OAuth metadata. Authority-specific required/forbidden fields are validated after the root default is resolved.

type MCPOAuthUpstreamProfile added in v0.0.26

type MCPOAuthUpstreamProfile struct {
	Mode   string                    `yaml:"mode"`
	OAuth2 *MCPOAuth2UpstreamProfile `yaml:"oauth2"`
}

MCPOAuthUpstreamProfile is a strict OIDC/OAuth2 tagged union. Omitted means OIDC.

func (*MCPOAuthUpstreamProfile) UnmarshalYAML added in v0.0.26

func (u *MCPOAuthUpstreamProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes an optional upstream protocol selector.

type MCPPreregisteredClientProfile

type MCPPreregisteredClientProfile struct {
	// ID is the required preregistered OAuth client identifier.
	ID string `yaml:"id"`
	// SecretEnv is a MECATL_* environment variable name containing the client secret.
	SecretEnv string `yaml:"secret_env"`
}

MCPPreregisteredClientProfile contains client identity metadata and a secret reference.

func (*MCPPreregisteredClientProfile) UnmarshalYAML

func (c *MCPPreregisteredClientProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes preregistered client metadata.

type MCPSection

type MCPSection struct {
	// Mode selects global or broker authority. Empty uses the command-root default.
	Mode string `yaml:"mode"`
	// Broker contains options meaningful only in broker mode.
	Broker MCPBrokerProfile `yaml:"broker"`
	// Servers is the ordered list of neutral Streamable HTTP route declarations.
	Servers []MCPServerProfile `yaml:"servers"`
}

MCPSection is the strict operator-only mcp: subtree. Mode-specific requirements are applied once by the canonical authority resolver after the command root supplies its default.

func (*MCPSection) UnmarshalYAML

func (s *MCPSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes and validates an MCP operator section.

type MCPServerProfile

type MCPServerProfile struct {
	// Name is an ASCII [A-Za-z0-9_]+ identifier, unique case-insensitively.
	Name string `yaml:"name"`
	// URL is an absolute HTTP(S) endpoint without userinfo or a fragment.
	URL string `yaml:"url"`
	// Auth selects exactly one of none, static_bearer, or oauth.
	Auth MCPAuthProfile `yaml:"auth"`
}

MCPServerProfile is one named Streamable HTTP endpoint and its explicit auth mode.

func (*MCPServerProfile) UnmarshalYAML

func (s *MCPServerProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes and validates one MCP server profile.

type MCPStaticBearerProfile

type MCPStaticBearerProfile struct {
	// TokenEnv is a MECATL_* environment variable name containing the opaque token.
	TokenEnv string `yaml:"token_env"`
}

MCPStaticBearerProfile contains a reference only, never a bearer-token value.

func (*MCPStaticBearerProfile) UnmarshalYAML

func (s *MCPStaticBearerProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes a static bearer secret reference.

type MCPStaticToolProfile added in v0.0.26

type MCPStaticToolProfile struct {
	Name        string          `yaml:"name"`
	Description string          `yaml:"description"`
	InputSchema json.RawMessage `yaml:"input_schema"`
	ReadOnly    bool            `yaml:"read_only"`
}

MCPStaticToolProfile is one trusted protected-backend tool declaration.

func (*MCPStaticToolProfile) UnmarshalYAML added in v0.0.26

func (t *MCPStaticToolProfile) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes one protected tool declaration.

type ModelsSection

type ModelsSection struct {
	// Slots binds a slot name (a call-slot "compaction"/"ask-reviewer"/"guardrail" or
	// a tier "cheap"/"fast"/"reasoning") to a model selector (alias or concrete id).
	Slots map[string]string `yaml:"slots"`
	// Aliases binds a short alias to a concrete model id (merged onto the CLI
	// --model-alias map, CLI winning per key).
	Aliases map[string]string `yaml:"aliases"`
	// Default is the session-default model selector (alias or concrete id). It is the
	// project-overridable session default (ADR 0030 Phase 4) — within the operator
	// allowlist; the operator's own Default is uncapped. Empty = absent.
	Default string `yaml:"default"`
	// Subagent is the OPERATOR-TIER def-less child-default model selector (alias or
	// concrete id): the settings.yaml twin of the --subagent-model flag (issue #288).
	// It sets the global default model for every Subagent / Parallel-branch / team-member
	// child that does not pin its own model (via an agent definition or a per-call
	// override). Operator-tier ONLY: a project-tier subagent: is IGNORED with a WARN (the
	// child-default model is an operator decision — the same operator-only captureModels
	// discipline as default_provider/allowlist/router). The CLI --subagent-model WINS when
	// both are set. Validated FAIL-FAST at Build (normalizeSubagentModel): a value that
	// does not resolve to a usable model id is a startup error (unlike fail-soft
	// models.default). Empty = absent (the flag/inherit-parent behaviour is unchanged).
	Subagent string `yaml:"subagent"`
	// DefaultProvider is the OPERATOR-TIER deployment-wide default provider id (e.g.
	// openai, openrouter, anthropic, toolhive). It mirrors the --default-provider flag
	// (app.Config.DefaultProvider) so an operator can declare "toolhive is my default
	// despite my API key" persistently in settings.yaml without unsetting the key. It
	// feeds the UNCHANGED preferredDefaultProvider ladder as an explicit override — it
	// does NOT lower the precedence of key-driven providers. Operator-tier only: a
	// project-tier default_provider: is IGNORED with a WARN (the same operator-only
	// captureModels discipline as posture/guardrails/allowlist). Validated FAIL-FAST at
	// Build (validateDefaultModel): an unknown/unavailable provider is a startup error.
	// Empty = absent (the ladder's preferred default wins). The name pair
	// (default = model, default_provider = provider) mirrors the wire grammar exactly.
	DefaultProvider string `yaml:"default_provider"`
	// Allowlist is the OPERATOR-TIER, non-wideable cap (ADR 0030 Phase 4): the set of
	// model selectors (alias names and/or concrete ids) a PROJECT-tier models: block
	// may bind to. An empty/absent allowlist means project models stay WARN-ignored
	// (the opt-in: no cap ⇒ no project override, byte-identical to pre-Phase-4). It is
	// honoured ONLY from the operator tiers; a project-tier allowlist: key is ignored
	// with a WARN (a project cannot widen its own cap).
	Allowlist []string `yaml:"allowlist"`
	// Router is the OPERATOR-TIER semantic Subagent model-router taxonomy (ADR 0031,
	// Phase 5; enable model superseded by ADR 0042): a classifier slot, the routing
	// categories, the default category, and the YAML kill-switch. It is operator-tier
	// ONLY — a project-tier router: sub-block is STRIPPED with a WARN (the taxonomy is
	// an autonomous-spend/capability decision the operator owns, like the allowlist).
	// nil/absent = no taxonomy ⇒ the router is OFF (byte-identical, silent). Per ADR
	// 0042 the TAXONOMY is the enable: a non-empty router: with categories turns the
	// router ON unless `disabled: true` (or the CLI kill-switch) forces it off — the
	// guardrails-parity enable model, replacing 0031's flag-to-enable.
	Router *RouterSection `yaml:"router"`
	// ContextWindows is the OPERATOR-TIER exact provider ID → exact final model ID
	// → total context token override map. It is intentionally not a selector map:
	// aliases and slots are resolved before this lookup, and project values are ignored.
	ContextWindows ContextWindows `yaml:"context_windows"`
}

ModelsSection is the `models:` YAML subtree (ADR 0030): a per-slot model-binding map, an alias map, a session-default binding, and the operator-tier allowlist cap. The TOP mapping is parsed STRICTLY (unknown keys error); the inner Slots/Aliases maps are free-form name→selector (composition validates the slot names fail-soft via knownSlotNames).

The block appears at BOTH tiers but the tiers differ in what they may carry (Phase 4):

  • OPERATOR tier (user-global + CLI): all four fields. The Allowlist is the non-wideable cap on what a PROJECT may bind; Slots/Aliases/Default are the operator's own bindings (never capped — the operator is authoritative).
  • PROJECT tier (.mecatl/settings.yaml): Slots/Aliases/Default ONLY, honoured only within the operator Allowlist and only on a TRUSTED workspace. A project Allowlist: key is IGNORED with a WARN (a project cannot widen its own cap).

func (*ModelsSection) UnmarshalYAML

func (m *ModelsSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the models: mapping STRICTLY (ADR 0030): an unknown key inside the models subtree is a parse error — a typo like `slotz:` or `aliasez:` must not silently drop a whole binding map. Same rationale as GuardrailsSection.

type OpenRouterModelRoute

type OpenRouterModelRoute struct {
	// Order lists downstream provider slugs (lowercase-kebab, e.g. "anthropic",
	// "google-vertex", "deepinfra/turbo") tried in order. Setting it disables
	// OpenRouter's default price load-balancing. Base-slug matching applies:
	// "google-vertex" matches all its regions/variants (service tiers excepted).
	Order []string `yaml:"order"`
	// AllowFallbacks, when explicitly false, pins the request to Order with no
	// fallback to other downstreams. Omit the key to keep OpenRouter's default
	// (true); set it to false to disable fallback.
	AllowFallbacks *bool `yaml:"allow_fallbacks"`
}

OpenRouterModelRoute is one model's downstream-provider routing preference (issue #480), the on-disk mirror of OpenRouter's `provider` request object (v1 surface: order + allow_fallbacks). Composition maps it to OpenRouterProviderPreferences.

func (*OpenRouterModelRoute) UnmarshalYAML

func (m *OpenRouterModelRoute) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes an openrouter.models.<id> entry STRICTLY.

type OpenRouterSection

type OpenRouterSection struct {
	// Models maps a model id (or alias, resolved in composition) to its
	// downstream-provider routing preference.
	Models map[string]OpenRouterModelRoute `yaml:"models"`
}

OpenRouterSection is the `openrouter:` operator-tier YAML subtree (issue #480): per-model downstream-provider routing preferences. mecatl's "provider" stays the wire adapter — these are the DOWNSTREAM inference providers OpenRouter routes a model to (Anthropic, Amazon Bedrock, Google Vertex, …). The TOP mapping is parsed STRICTLY (unknown keys error); the per-model entries are also strict. Validated fail-soft in composition (invalid slugs WARN-dropped).

func (*OpenRouterSection) UnmarshalYAML

func (s *OpenRouterSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the openrouter: mapping STRICTLY (issue #480): an unknown key inside the subtree is a parse error — a typo like `moddels:` must not silently drop the routing preferences. Same rationale as GuardrailsSection.

type Options

type Options struct {
	// Conventional turns on auto-discovery of the project-level conventional files
	// (the shared/local .mecatl + .claude files) and the user-global files. Default
	// false keeps discovery fully off (only ExplicitFiles, if any, are loaded).
	Conventional bool
	// ImportClaude turns on importing Claude-Code settings.json (project + user),
	// with the lossy fail-safe table applied (see claudeimport.go). Default false.
	ImportClaude bool
	// TrustProject, when true, honours a project's ALLOW rules (shared AND local —
	// both are project-supplied). When false (the default — the safe stance) a
	// project's allows are DROPPED while its deny/ask rules are still honoured.
	// User-global and explicit (CLI) config is always trusted regardless.
	TrustProject bool
	// ExplicitFiles are operator-pointed YAML config files (e.g. from a repeatable
	// --permission-config flag), loaded at ScopeCLI (the HIGHEST config precedence,
	// fully trusted) regardless of Conventional. Read from the host filesystem at
	// construction.
	ExplicitFiles []string
	// Diagnostics is the operational-logging sink for the lossy import report
	// (demoted/inert/dropped specs) and the per-file fail-soft skip lines. nil is
	// tolerated: newWithEnv defaults it to port.NopDiagnostics so the resolver stays
	// silent rather than nil-panicking. The composition layer injects the shared sink.
	Diagnostics port.Diagnostics
}

Options configures a Resolver's discovery posture (issue #13).

type Permissions

type Permissions struct {
	// Allow lists rule specs that GRANT a tool call (effect Allow) on the MAIN
	// engine. Under an untrusted project these are DROPPED by the trust gate
	// (see Resolver).
	Allow []string `yaml:"allow"`
	// Ask lists rule specs that REQUIRE approval (effect Ask) on the MAIN
	// engine. Always honoured.
	Ask []string `yaml:"ask"`
	// Deny lists rule specs that BLOCK a tool call (effect Deny) EVERYWHERE —
	// main engine and subagents (a deny only tightens). Always honoured.
	Deny []string `yaml:"deny"`
	// Subagent holds the child-scoped rule-spec lists (issue #32): rules that
	// bind ONLY subagent/member/branch engines, resolved through the child-ask
	// model (a subagent allow can clear a substitution-floored ask; a subagent
	// ask surfaces to the human or auto-denies; a subagent deny blocks).
	Subagent SubagentPermissions `yaml:"subagent"`
}

Permissions is the three-bucket rule-spec set plus the child-scoped `subagent:` block (issue #32). Resolution is deny-dominant, so a spec appearing in Deny always wins over the same spec in Allow regardless of bucket order here. Audience semantics (applied by rulesFromConfig): Allow/Ask bind the MAIN engine only; Deny binds BOTH main and subagents (tighten-only); the Subagent block binds child engines only.

func (*Permissions) UnmarshalYAML

func (p *Permissions) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the permissions: mapping STRICTLY (issue #32): an unknown key inside the permissions subtree is a parse error — surfaced through the existing per-file fail-soft log-and-skip — rather than silently ignored config (a typo like `alow:` or `subagnet:` would otherwise disable a whole rule list without a trace). The top level of Config stays lenient.

type ProviderAuth

type ProviderAuth struct {
	Method string `yaml:"method"`
}

ProviderAuth controls the closed custom-provider authentication vocabulary.

func (*ProviderAuth) UnmarshalYAML

func (a *ProviderAuth) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the closed custom-provider authentication method.

type ProviderDefinition

type ProviderDefinition struct {
	ID           string
	BaseURL      string       `yaml:"base_url"`
	DefaultModel string       `yaml:"default_model"`
	APIFlavor    string       `yaml:"api_flavor"`
	Auth         ProviderAuth `yaml:"auth"`
}

ProviderDefinition is one custom provider's non-secret registry definition.

func (*ProviderDefinition) UnmarshalYAML

func (p *ProviderDefinition) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes and validates one strict custom provider definition.

type ProviderDefinitions

type ProviderDefinitions map[string]ProviderDefinition

ProviderDefinitions is the strict, operator-owned custom provider map.

func (*ProviderDefinitions) UnmarshalYAML

func (p *ProviderDefinitions) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes a strict map of custom provider definitions.

type ProviderOverride

type ProviderOverride struct {
	BaseURL string `yaml:"base_url"`
}

ProviderOverride is one eligible built-in's endpoint override.

func (*ProviderOverride) UnmarshalYAML

func (p *ProviderOverride) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes and validates one strict endpoint override.

type ProviderOverrides

type ProviderOverrides map[string]ProviderOverride

ProviderOverrides is the strict operator-owned built-in endpoint map.

func (*ProviderOverrides) UnmarshalYAML

func (p *ProviderOverrides) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes a strict map of eligible built-in endpoint overrides.

type Report

type Report struct {
	// Demoted records Allow specs that were demoted to Ask for safety (e.g. a
	// Claude WebFetch(domain:...) allow — see importClaudeRules).
	Demoted []ReportEntry
	// Inert records specs that parsed but cannot match anything as written (e.g. a
	// Read(~/path) whose "~" is left unexpanded), so the operator knows the rule
	// has no effect.
	Inert []ReportEntry
	// Dropped records specs that could not be parsed at all and were discarded.
	Dropped []ReportEntry
}

Report records the LOSSY outcomes of importing/loading permission config so the composition layer can log exactly what was demoted, left inert, or dropped. Honest reporting is the contract: a fail-safe must never SILENTLY weaken intent.

func (*Report) Empty

func (r *Report) Empty() bool

Empty reports whether the report recorded any lossy outcome.

type ReportEntry

type ReportEntry struct {
	Spec   string
	Reason string
}

ReportEntry is one lossy outcome: the original spec and a human reason.

type Resolver

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver discovers and caches file-based permission rules per workspace root (issue #13). It implements permpolicy.RuleResolver: the policy calls Resolve on every Evaluate, so the resolver caches — discovery is file I/O. The cache is keyed by ws.Root() with a sync.RWMutex; each entry is REVALIDATED against the config files' mtime/size on every hit, so the cache speeds up the common unchanged case without going stale on an edit (matching the re-probe stance of the MCP source prober and command lister).

The USER-GLOBAL + explicit (CLI) rules are root-independent and resolved ONCE at construction (they are the operator's own, fully trusted). Only the PROJECT rules are re-resolved per root, gated by TrustProject.

func New

func New(opts Options) *Resolver

New constructs a Resolver from opts, reading the user-global + explicit (CLI) rules once (project rules are read lazily per root in Resolve). It logs the import report (demoted/inert/dropped specs) so lossy outcomes are observable. It never fails: an unreadable/malformed user file is logged and skipped (the process must still start), matching the rest of the composition's fail-soft posture. Returns nil when opts requests no sources at all, so callers can pass the result straight to permpolicy.NewPolicyWithResolver (a nil resolver = off).

func NewWithEnv

func NewWithEnv(opts Options, env xdgconfig.ResolveEnv) *Resolver

NewWithEnv constructs a Resolver using env for user-global discovery. It is for composition callers that need to provide an isolated environment; New preserves the production binding to xdgconfig.OSEnv.

func (*Resolver) OperatorGuardrails

func (r *Resolver) OperatorGuardrails() *GuardrailsSection

OperatorGuardrails returns the operator-tier guardrails config (user-global + CLI only), or nil when none was configured. It is the SOLE accessor the composition layer uses to read guardrails from config — by construction it never returns a project-tier block (decision 3: operator-tier-only).

func (*Resolver) OperatorLearning

func (r *Resolver) OperatorLearning() *LearningSection

OperatorLearning returns the complete operator-tier learning policy. Callers must treat it as immutable.

func (*Resolver) OperatorLearningMode

func (r *Resolver) OperatorLearningMode() string

OperatorLearningMode returns the operator-tier learning mode token, or empty when no learning subtree was configured.

func (*Resolver) OperatorMCP

func (r *Resolver) OperatorMCP() *MCPSection

OperatorMCP returns the complete operator-tier mcp subtree, or nil when absent. It is metadata only and can never originate from project settings.

func (*Resolver) OperatorModelPolicy

func (r *Resolver) OperatorModelPolicy() *ModelsSection

OperatorModelPolicy returns the operator-tier models: subtree (user-global + CLI only), or nil when none was configured (ADR 0030 Phase 4). It is the accessor the composition layer reads the operator ALLOWLIST and the operator DEFAULT from — the non-wideable cap that gates project-tier bindings. It reads the SAME operatorModels backing field as OperatorModelSlots (allowlist+default+slots+aliases all ride the one operator models: block, captured once via captureModels); there is no second capture path. nil-safe. It mirrors OperatorGuardrails()/OperatorPosture().

func (*Resolver) OperatorModelSlots

func (r *Resolver) OperatorModelSlots() *ModelsSection

OperatorModelSlots returns the operator-tier models: subtree (user-global + CLI only), or nil when none was configured. It is the SOLE accessor the composition layer uses to read per-slot model config from disk — by construction it never returns a project-tier block (a project models: is ignored with a WARN in loadProjectRules). nil-safe.

func (*Resolver) OperatorOpenRouter

func (r *Resolver) OperatorOpenRouter() *OpenRouterSection

OperatorOpenRouter returns the operator-tier openrouter: subtree (user-global + CLI only), or nil when none was configured (issue #480). It is the SOLE accessor the composition layer uses to read OpenRouter downstream-provider routing from config — by construction it never returns a project-tier block (a project openrouter: is ignored with a WARN in loadProjectRules). nil-safe. Mirrors OperatorModelSlots().

func (*Resolver) OperatorPlanModeAutoApprove

func (r *Resolver) OperatorPlanModeAutoApprove() bool

OperatorPlanModeAutoApprove returns the operator-tier plan-mode-auto-approve: bool (user-global + CLI only), or false when none was configured (issue #206 Wave 6a). It is the SOLE accessor the composition layer uses to read the flag from config — by construction it never returns a project-tier value (a project plan-mode-auto-approve: is ignored with a WARN in loadProjectRules). nil-safe. Mirrors OperatorPosture().

func (*Resolver) OperatorPosture

func (r *Resolver) OperatorPosture() string

OperatorPosture returns the operator-tier posture: scalar (user-global + CLI only), or "" when none was configured. It is the SOLE accessor the composition layer uses to read posture from config — by construction it never returns a project-tier value (a project posture: is ignored with a WARN in loadProjectRules).

func (*Resolver) OperatorProviders

func (r *Resolver) OperatorProviders() (ProviderDefinitions, ProviderOverrides, error)

OperatorProviders returns the operator-tier custom provider definitions and endpoint overrides. Both maps are immutable snapshots; the returned error records a strict operator configuration parse failure involving either section.

func (*Resolver) OperatorReasoningEffort

func (r *Resolver) OperatorReasoningEffort() string

OperatorReasoningEffort returns the operator-tier reasoning-effort: scalar (user-global + CLI only), or "" when none was configured (ADR 0055). It is the SOLE accessor the composition layer uses to read reasoning-effort from config — by construction it never returns a project-tier value (a project reasoning-effort: is ignored with a WARN in loadProjectRules). nil-safe. Mirrors OperatorPosture().

func (*Resolver) OperatorRetention

func (r *Resolver) OperatorRetention() (*RetentionSection, error)

OperatorRetention returns the immutable operator-tier retention block.

func (*Resolver) OperatorSteer

func (r *Resolver) OperatorSteer() (value, present bool)

OperatorSteer returns the OPERATOR-TIER steer: bool (user-global + CLI only) and whether ANY operator-tier file carried the key (steer-while-running, issue #512). It is the SOLE accessor the composition layer uses to read the knob from config — by construction it never returns a project-tier value (a project steer: is ignored with a WARN in loadProjectRules). The presence bit matters because the knob is an opt-OUT of a DEFAULT-ON feature: absent (present=false) means composition keeps the default; an explicit steer: false (present=true, value=false) disables it. nil-safe. Mirrors OperatorPosture().

func (*Resolver) OperatorStorageManagement

func (r *Resolver) OperatorStorageManagement() (*StorageManagementSection, error)

OperatorStorageManagement returns the immutable operator-tier management authority block and any strict parse failure that would otherwise disable it.

func (*Resolver) OperatorTemporaryStorage added in v0.0.26

func (r *Resolver) OperatorTemporaryStorage() (*TemporaryStorageSection, error)

OperatorTemporaryStorage returns the immutable operator-tier command temporary storage policy and any strict parse failure that would otherwise disable it.

func (*Resolver) ProjectLearningModes

func (r *Resolver) ProjectLearningModes(ws tool.WorkspaceReader) []string

ProjectLearningModes returns project-tier mode tokens in precedence order. Composition applies them only as autonomy ceilings (off < review < auto).

func (*Resolver) ProjectLearningSettings

func (r *Resolver) ProjectLearningSettings(ws tool.WorkspaceReader) []*LearningSection

ProjectLearningSettings returns strict project learning subtrees in precedence order. Composition applies only mode and sensitivity as tighten-only ceilings; Automatic is operator-only and ignored with a warning.

func (*Resolver) ProjectModelBindings

func (r *Resolver) ProjectModelBindings(ws tool.WorkspaceReader) *ModelsSection

ProjectModelBindings returns the SANITIZED project-tier models: block for the given workspace (ADR 0030 Phase 4): slots/aliases/default only (the allowlist: key is stripped at capture — non-wideable), captured ONLY when the project was trusted AND an operator allowlist exists. It is a pure read of the per-root cacheEntry; a cold root is resolved first (the same revalidated-cache path Resolve uses), so the trust/allowlist decision was already applied at CAPTURE time (loadProjectRules) — this accessor adds no policy. Returns nil when ws is nil, the resolver is nil, or the project carried no honoured models: block.

func (*Resolver) Resolve

Resolve returns the rules that apply to the given workspace: the project rules (gated by TrustProject) plus the root-independent user/CLI rules. A nil ws yields only the user/CLI rules (no project to discover). The per-root cache is revalidated against the config files' mtime/size, so a mid-process edit takes effect on the next call. It satisfies permpolicy.RuleResolver.

type RetentionLimitSection

type RetentionLimitSection struct {
	// MaxAge deletes eligible rows older than this Go duration; 0 disables the age limit.
	MaxAge string `yaml:"max_age"`
	// MaxCount keeps the newest eligible rows up to this count; 0 disables the count limit.
	MaxCount               int  `yaml:"max_count"`
	MaxAgeSet, MaxCountSet bool `yaml:"-"`
}

RetentionLimitSection controls one durable session-kind partition. Zero disables.

func (*RetentionLimitSection) UnmarshalYAML

func (s *RetentionLimitSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes one retention partition.

type RetentionSection

type RetentionSection struct {
	// Version is the required schema version; the only supported value is 1.
	Version int `yaml:"version"`
	// Main controls top-level operator/service sessions.
	Main RetentionLimitSection `yaml:"main"`
	// Child controls subagent, parallel-branch, and team-member sessions.
	Child RetentionLimitSection `yaml:"child"`
	// Scheduled controls scheduled-fire sessions.
	Scheduled RetentionLimitSection `yaml:"scheduled"`
	// SweepCadence is the repeat interval; 0 disables repeats while retaining the compatibility startup sweep.
	SweepCadence string `yaml:"sweep_cadence"`
	// AcknowledgeMainDeletion explicitly consents to destructive main-session cleanup.
	AcknowledgeMainDeletion bool `yaml:"acknowledge_main_deletion"`
	SweepCadenceSet         bool `yaml:"-"`
}

RetentionSection is the versioned operator automatic-cleanup policy.

func (*RetentionSection) UnmarshalYAML

func (s *RetentionSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes and validates the versioned retention policy.

type RouterCategory

type RouterCategory struct {
	// Name is the routing key the classifier echoes back as its verdict and the key
	// composition maps to Model.
	Name string `yaml:"name"`
	// Description is the one-line summary the classifier reads to choose this category.
	Description string `yaml:"description"`
	// Model is the model selector (alias / slot / concrete id) a task classified into
	// this category is minted on, resolved through the operator-merged alias map.
	Model string `yaml:"model"`
}

RouterCategory is one routing category in the operator taxonomy (ADR 0031): a name, a one-line description the classifier reads, and the model selector the category maps to. A category with an empty Name or Description is WARN-dropped fail-soft in composition (foldOperatorModelRouter) — a category the classifier cannot describe or name is useless.

func (*RouterCategory) UnmarshalYAML

func (c *RouterCategory) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes a router category mapping STRICTLY.

type RouterSection

type RouterSection struct {
	// ClassifierSlot names the model slot the CLASSIFIER itself runs on (the tiny,
	// cheap one-turn classification call). Empty falls through to the `router` slot's
	// default tier (cheap) — the classifier is housekeeping, not the routed work.
	ClassifierSlot string `yaml:"classifier-slot"`
	// Categories are the routing choices. Each carries a Name (the classifier's verdict
	// key), a Description (the classifier's only signal — make them distinct), and a
	// Model selector (an alias / slot / concrete id, resolved through the operator-
	// merged alias map; operator taxonomy targets are UNCAPPED).
	Categories []RouterCategory `yaml:"categories"`
	// DefaultCategory is the category the classifier is told to choose when none clearly
	// fits (advisory to the classifier; the real safety net is the fail-soft inherit).
	DefaultCategory string `yaml:"default-category"`
	// Disabled is the YAML-level kill switch (ADR 0042, mirroring
	// GuardrailsSection.Disabled): per ADR 0042 a non-empty taxonomy ENABLES the router,
	// so `disabled: true` is the "taxonomy defined but temporarily off" override. The
	// CLI kill-switch --subagent-model-router=false also sets it (the two OR together).
	// Default false ⇒ the router is enabled whenever categories are present.
	Disabled bool `yaml:"disabled"`
}

RouterSection is the `models.router:` operator-tier subtree (ADR 0031): the semantic Subagent model-router taxonomy. The classifier reads the category descriptions to choose which category a delegated task belongs to; composition maps the chosen category's Model selector through the alias/slot machinery to a concrete model id.

func (*RouterSection) UnmarshalYAML

func (r *RouterSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the models.router: mapping STRICTLY (ADR 0031): an unknown key inside the router subtree is a parse error (same rationale as ModelsSection).

type StorageManagementPrincipal

type StorageManagementPrincipal struct {
	// Issuer must equal the verified token issuer byte-for-byte.
	Issuer string `yaml:"issuer"`
	// Subject must equal the verified token subject byte-for-byte.
	Subject string `yaml:"subject"`
}

StorageManagementPrincipal is one exact verified issuer/subject pair.

func (*StorageManagementPrincipal) UnmarshalYAML

func (p *StorageManagementPrincipal) UnmarshalYAML(node ast.Node) error

UnmarshalYAML keeps each principal mapping closed to prevent a misspelled identity field from silently removing the management boundary.

type StorageManagementSection

type StorageManagementSection struct {
	// Version is the required schema version; the only supported value is 1.
	Version int `yaml:"version"`
	// Principals lists exact verified OIDC issuer/subject pairs. Empty grants nobody.
	Principals []StorageManagementPrincipal `yaml:"principals"`
}

StorageManagementSection is the explicit operator authority for process-wide storage health, migration, and cleanup.

func (*StorageManagementSection) UnmarshalYAML

func (s *StorageManagementSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly validates storage-management authority. An empty list grants nobody; there is no wildcard or grant-type shortcut.

type SubagentPermissions

type SubagentPermissions struct {
	// Allow lists child-scoped rule specs with effect Allow (trust-gated for
	// project tiers).
	Allow []string `yaml:"allow"`
	// Ask lists child-scoped rule specs with effect Ask (always honoured). A
	// configured subagent Ask is NEVER auto-approved by the isolation carve-out —
	// it surfaces to a human or auto-denies.
	Ask []string `yaml:"ask"`
	// Deny lists child-scoped rule specs with effect Deny (always honoured).
	Deny []string `yaml:"deny"`
}

SubagentPermissions is the child-scoped allow/ask/deny rule-spec set (issue #32). Same spec grammar as the top-level buckets; every parsed rule is tagged AudienceSubagent so it binds only child engines. Project-tier subagent ALLOWS are trust-gated exactly like top-level allows; ask/deny always hold.

func (*SubagentPermissions) UnmarshalYAML

func (s *SubagentPermissions) UnmarshalYAML(node ast.Node) error

UnmarshalYAML decodes the permissions.subagent: mapping STRICTLY — same rationale as Permissions.UnmarshalYAML.

type TemporaryStorageSection added in v0.0.26

type TemporaryStorageSection struct {
	Mode                string        `yaml:"mode"`
	ManagedRoot         string        `yaml:"managed_root"`
	SystemTempDir       string        `yaml:"system_temp_dir"`
	CommandReapAfter    time.Duration `yaml:"command_reap_after"`
	ReapInterval        time.Duration `yaml:"reap_interval"`
	ReapTimeout         time.Duration `yaml:"reap_timeout"`
	ShutdownReapTimeout time.Duration `yaml:"shutdown_reap_timeout"`
}

TemporaryStorageSection is the strict operator policy for command temporary storage. Durations are parsed during decoding so invalid settings fail before composition can enable a runner.

func (*TemporaryStorageSection) UnmarshalYAML added in v0.0.26

func (s *TemporaryStorageSection) UnmarshalYAML(node ast.Node) error

UnmarshalYAML strictly decodes the temporary-storage policy and applies its defaults. Paths are lexically validated here; host ownership checks happen in composition where filesystem access belongs.

Jump to

Keyboard shortcuts

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