Documentation
¶
Index ¶
- Constants
- func ExpectedMigratableKeysSkipped() map[string]string
- func GenerateExampleConfig() ([]byte, error)
- func GenerateReferenceMarkdown() ([]byte, error)
- func IsSecretReference(value string) bool
- func KeysInFile(path string) (map[string]bool, error)
- func OperationalKeysInFile(path string) ([]string, error)
- func ParseFlags() (configFile string, generateConfig bool, err error)deprecated
- func PrintConfigHelp() error
- func ResolveSecretValue(ctx context.Context, value string, vault SecretResolver) (string, error)
- func ValidateClassifications(settings []MigratableSetting) error
- func ValidateSettingDefs(defs []SettingDef) error
- func ValidateTimmyConfig(t TimmyConfig, buildMode string) error
- func ValidateTimmyDumpFlag(enabled bool, buildMode string) error
- type AdministratorConfig
- type AlertingConfig
- type AuthConfig
- type CORSConfig
- type Category
- type Config
- func (c *Config) GetBaseURL() string
- func (c *Config) GetCookieDomain() string
- func (c *Config) GetEnabledOAuthProviders() []OAuthProviderConfig
- func (c *Config) GetJWTDuration() time.Duration
- func (c *Config) GetLogLevel() slogging.LogLevel
- func (c *Config) GetMigratableSettings() []MigratableSetting
- func (c *Config) GetOAuthProvider(providerID string) (OAuthProviderConfig, bool)
- func (c *Config) GetWebSocketInactivityTimeout() time.Duration
- func (c *Config) IsSecureCookies() bool
- func (c *Config) IsTestMode() bool
- func (c *Config) ResolveSecretReferences(ctx context.Context, vault SecretResolver) error
- func (c *Config) ResolveSecretsConfigReferences(ctx context.Context) error
- func (c *Config) Validate() error
- func (c *Config) ValidateRequired() error
- type ConfigClass
- type ConfluenceConfig
- type ConnectionPoolConfig
- type Consumer
- type ContentExtractorsConfig
- type ContentOAuthConfig
- type ContentOAuthProviderConfig
- type ContentSourcesConfig
- type CookieConfig
- type DatabaseConfig
- type Delivery
- type DiscoveredField
- type EmbeddingProfile
- type Flags
- type GoogleDriveConfig
- type GoogleWorkspaceConfig
- type JWTConfig
- type LoggingConfig
- type MicrosoftConfig
- type MigratableSetting
- type Mutability
- type OAuthConfig
- type OAuthProviderConfig
- type ObservabilityConfig
- type OperatorConfig
- type ProcessEnvVar
- type RedisConfig
- type SAMLConfig
- type SAMLProviderConfig
- type SSRFConfig
- type SSRFURIConfig
- type SecretResolver
- type SecretsConfig
- type ServerConfig
- type SettingDef
- type StampedConfig
- type StampedConfigProvider
- type TimmyConfig
- type UserInfoEndpoint
- type ValueKind
- type Visibility
- type WebSocketConfig
- type WebhookConfig
Constants ¶
const MaxPerUserConcurrency = 16
MaxPerUserConcurrency is the hardcoded ceiling on the per-user concurrent extraction count. Operators may configure a per-user default at or below this value; runtime overrides are also clamped to this ceiling. Exported so the api package can enforce the same cap without duplicating the constant.
Variables ¶
This section is empty.
Functions ¶
func ExpectedMigratableKeysSkipped ¶
ExpectedMigratableKeysSkipped returns the set of leaf fields that exist on Config but are intentionally NOT emitted as migratable settings. This allowlist is what keeps the completeness gate from flagging fields that have a legitimate reason to be invisible to the DB-backed settings service.
Adding an entry here is a deliberate act — every entry must have a justification comment. An entry that says "TODO: migrate later" is a promise to do that work, tracked by the follow-up issue noted in the comment.
func GenerateExampleConfig ¶
GenerateExampleConfig produces the contents of config-example.yml from the classification registry. Only CategoryBootstrap keys are included — the example file documents the bootstrap-only config, since operational config lives in the database. Secret values are emitted as reference placeholders.
func GenerateReferenceMarkdown ¶
GenerateReferenceMarkdown produces the per-key configuration reference from the classification registry. It emits a precedence explainer plus two tables — bootstrap and operational — so the wiki Configuration-Reference page is generated, not hand-maintained. Secret defaults are shown as vault:// placeholders, never real values.
Rows come from referenceSettings, i.e. the registry itself, not from GetMigratableSettings: that projection omits empty-valued and conditionally-emitted settings for database-seeding reasons, and the reference doubles as the TMI_* env-var allowlist, so it must name every setting the server can read (#810).
func IsSecretReference ¶
IsSecretReference reports whether value is a secret-reference locator (vault://, env://, or file://) rather than an inline secret literal.
func KeysInFile ¶
KeysInFile returns every dotted leaf key present in a YAML config file, as a set. Used to distinguish a value an operator actually wrote from the struct default that every setting otherwise carries — the distinction the settings service needs to know whether a database value may apply (#415).
func OperationalKeysInFile ¶
OperationalKeysInFile returns the operational-category setting keys present in a YAML config file. Operational config belongs in the database; finding it in a file indicates drift during the bootstrap-only migration.
func ParseFlags
deprecated
func PrintConfigHelp ¶
func PrintConfigHelp() error
PrintConfigHelp prints guidance about configuration files. It is invoked when the server binary is run with --generate-config.
func ResolveSecretValue ¶
ResolveSecretValue dereferences a secret config value.
- An inline value (no recognized scheme:// prefix) is returned unchanged. A value such as "postgres://..." is inline — postgres is not a reference scheme.
- env://NAME resolves to the value of environment variable NAME; an unset or empty variable is an error.
- file://PATH reads PATH and returns its whitespace-trimmed contents.
- vault://PATH dereferences PATH through the supplied SecretResolver. A nil resolver is an error (a vault reference requires a configured secrets provider).
Resolution failures are returned as typed errors; this function never panics.
func ValidateClassifications ¶
func ValidateClassifications(settings []MigratableSetting) error
ValidateClassifications checks that every setting carries a complete and internally consistent ConfigClass. It returns a single error listing every violation found. This is the mechanism that makes the classification model self-enforcing: a misclassified setting fails the build.
func ValidateSettingDefs ¶
func ValidateSettingDefs(defs []SettingDef) error
ValidateSettingDefs checks the registry's internal consistency and returns an error naming every problem found, one per line, or nil. It is the enforcement point for the rule that every setting must be classified: a definition whose Category is the zero value is rejected.
This supersedes ValidateClassifications for the SettingDef registry: every rule that function enforces on a MigratableSetting is carried across here, plus the additional legality rules that apply to SettingDef's config-path (YAMLPath/EnvVar/Get) and Seeded fields.
func ValidateTimmyConfig ¶ added in v1.21.13
func ValidateTimmyConfig(t TimmyConfig, buildMode string) error
ValidateTimmyConfig enforces the Timmy invariants on any Timmy config (env/YAML or DB-resolved) for the given build mode.
func ValidateTimmyDumpFlag ¶ added in v1.21.13
ValidateTimmyDumpFlag rejects enabling timmy.dump_extracted_text_to_note in production builds.
Types ¶
type AdministratorConfig ¶
type AdministratorConfig struct {
Provider string `yaml:"provider" json:"provider"` // OAuth/SAML provider ID (required)
ProviderId string `yaml:"provider_id,omitempty" json:"provider_id,omitempty"` // Provider's user ID (for users, preferred)
Email string `yaml:"email,omitempty" json:"email,omitempty"` // Provider's email (for users, fallback)
GroupName string `yaml:"group_name,omitempty" json:"group_name,omitempty"` // Group name (for groups)
SubjectType string `yaml:"subject_type" json:"subject_type"` // "user" or "group" (required)
}
AdministratorConfig represents a single administrator entry configuration
type AlertingConfig ¶
type AlertingConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_ALERTING_ENABLED"`
WebhookURL string `yaml:"webhook_url" env:"TMI_ALERTING_WEBHOOK_URL"`
WebhookSecret string `yaml:"webhook_secret" env:"TMI_ALERTING_WEBHOOK_SECRET"`
}
AlertingConfig holds configuration for the operator-pinned audit alert sink (#395). When Enabled is true, EnsurePinnedAlertSubscription upserts a single operator-owned webhook subscription that delivers system_audit.admin_write events to WebhookURL. WebhookSecret is the HMAC signing secret for that subscription; it may be an env:// or vault:// reference resolved at startup by ResolveSecretReferences.
type AuthConfig ¶
type AuthConfig struct {
JWT JWTConfig `yaml:"jwt"`
OAuth OAuthConfig `yaml:"oauth"`
SAML SAMLConfig `yaml:"saml"`
Cookie CookieConfig `yaml:"cookie"`
AutoPromoteFirstUser bool `yaml:"auto_promote_first_user" env:"TMI_AUTH_AUTO_PROMOTE_FIRST_USER"`
EveryoneIsAReviewer bool `yaml:"everyone_is_a_reviewer" env:"TMI_AUTH_EVERYONE_IS_A_REVIEWER"`
BuildMode string `yaml:"build_mode" env:"TMI_BUILD_MODE"` // dev, test, or production
// StepUpWindowSeconds controls how recent the auth_time JWT claim must be
// for /admin/* writes (#355). Default 300 (5 minutes); minimum 60.
StepUpWindowSeconds int `yaml:"step_up_window_seconds" env:"TMI_AUTH_STEP_UP_WINDOW_SECONDS"`
}
AuthConfig holds authentication configuration
type CORSConfig ¶
type CORSConfig struct {
AllowedOrigins []string `yaml:"allowed_origins" env:"TMI_CORS_ALLOWED_ORIGINS"` // Comma-separated allowed origins
}
CORSConfig holds CORS configuration
type Category ¶
type Category int
Category answers: where does the value come from at rest?
const ( // CategoryUnclassified is the zero value. A setting with this category // fails the validation suite — it forces every setting to be classified. CategoryUnclassified Category = iota // CategoryBootstrap settings are loaded from file/env only, never the DB, // and consumed at startup before the settings service exists. CategoryBootstrap // CategoryOperational settings are DB-backed and runtime-editable. CategoryOperational )
func ClassificationCategoryFor ¶
ClassificationCategoryFor returns the Category of a setting key. It is the exported entry point for callers outside the config package that need to know whether a key is bootstrap or operational.
type Config ¶
type Config struct {
Server ServerConfig `yaml:"server"`
Database DatabaseConfig `yaml:"database"`
Auth AuthConfig `yaml:"auth"`
WebSocket WebSocketConfig `yaml:"websocket"`
Webhooks WebhookConfig `yaml:"webhooks"`
Logging LoggingConfig `yaml:"logging"`
Operator OperatorConfig `yaml:"operator"`
Secrets SecretsConfig `yaml:"secrets"`
Administrators []AdministratorConfig `yaml:"administrators"`
Timmy TimmyConfig `yaml:"timmy"`
SSRF SSRFConfig `yaml:"ssrf"`
Observability ObservabilityConfig `yaml:"observability"`
ContentSources ContentSourcesConfig `yaml:"content_sources"`
ContentExtractors ContentExtractorsConfig `yaml:"content_extractors"`
ContentOAuth ContentOAuthConfig `yaml:"content_oauth"`
ContentTokenEncryptionKey string `yaml:"content_token_encryption_key" env:"TMI_CONTENT_TOKEN_ENCRYPTION_KEY"`
Alerting AlertingConfig `yaml:"alerting"`
// contains filtered or unexported fields
}
Config holds all application configuration
func LoadSettingsSource ¶
LoadSettingsSource loads a YAML file that carries only operational settings — a `tmi-dbtool --export-config` snapshot, say — rather than a server configuration.
It skips the bootstrap validation Load() performs, because a settings source legitimately has no database.url, jwt.secret or build_mode: those are CategoryBootstrap keys that live in the server's own config file, and --export-config never writes them. Validating such a file as though it were a server config is what broke the export/import round-trip that --export-config's own output header advertises, and that `deploy-aws.sh --config-export` depends on — it failed with "database url is required (TMI_DATABASE_URL)" (#791).
Callers get a Config whose operational fields reflect the file and whose bootstrap fields are defaults; only GetMigratableSettings() output is meaningful. Do NOT use this to configure a running server.
func (*Config) GetBaseURL ¶
GetBaseURL returns the server's public base URL for callbacks. If BaseURL is explicitly configured, it is returned as-is. Otherwise, the URL is auto-inferred from Interface, Port, and TLSEnabled.
func (*Config) GetCookieDomain ¶
GetCookieDomain returns the cookie domain for HttpOnly auth cookies.
It returns ONLY an explicitly configured domain; otherwise it returns "" so the cookie is set without a Domain attribute (a host-only cookie scoped to the exact request host). The domain is deliberately never inferred from the bind/listen address or base URL: the server binds to 0.0.0.0 in dev, and deriving the cookie Domain from that produced Domain=0.0.0.0, which browsers reject (an IP literal that does not match the request host), so the auth cookies were silently discarded (#497). A host-only cookie is also the strictly more secure default in production -- it cannot leak to sibling subdomains -- so omitting Domain is correct everywhere unless an operator explicitly needs cross-host scoping.
func (*Config) GetEnabledOAuthProviders ¶
func (c *Config) GetEnabledOAuthProviders() []OAuthProviderConfig
GetEnabledOAuthProviders returns a slice of enabled OAuth providers
func (*Config) GetJWTDuration ¶
GetJWTDuration returns the JWT expiration duration
func (*Config) GetLogLevel ¶
GetLogLevel returns the parsed log level
func (*Config) GetMigratableSettings ¶
func (c *Config) GetMigratableSettings() []MigratableSetting
GetMigratableSettings returns every setting that has a config-file or environment delivery path, with its current value read from this Config.
This projects the registry (setting_defs_*.go) rather than maintaining a parallel list. A def with OmitWhenEmpty is left out entirely when its value is empty, reproducing the pre-registry builders' conditional emission (see OmitWhenEmpty's doc comment on SettingDef for why this matters beyond cosmetics). server.tls_cert_file and server.tls_key_file are special-cased instead of using OmitWhenEmpty: their old emission guard was server.tls_enabled, a different field than the one being emitted, and OmitWhenEmpty can only test a def's own Get() output.
auth.oauth.providers.*, auth.saml.providers.* and content_oauth.providers.* are generated per configured provider rather than declared statically — dynamic cardinality means no fixed dotted key — and are appended after the registry projection.
A def whose Key appears in ExpectedMigratableKeysSkipped() is never emitted, regardless of OmitWhenEmpty: that list's predicate is exactly "intentionally not a migratable setting" (see its doc comment), and two of its entries — content_token_encryption_key, database.oracle_wallet_location — now have a SettingDef with a real Get accessor. Matching on Key rather than YAMLPath matters: the list's other three entries are rename cases keyed by the pre-refactor STRUCT path (e.g. "auth.oauth.callback_url"), while the corresponding defs' Key is the renamed migratable-setting key (e.g. "auth.oauth_callback_url") — matching Key against that map leaves those three correctly emitted under their renamed key and skips only the two bootstrap-by-construction keys.
Class is assigned via classificationFor(key) in the final pass below, for every emitted setting — defs-projected and generated provider keys alike — exactly as the pre-registry implementation did. It is NOT taken from SettingDef.Class directly, even though the two agree on Category/Visibility/Secret for every currently-emitted key (Task 5 built SettingDef.Class as classificationFor(key) with Mutability overridden via withMutability(...) on many operational defs). Consuming SettingDef.Class here would flow those Mutability overrides into GetMigratableSettings' observable output for the first time — a real behavior change (dozens of keys' hot/static column in config-reference.md) that Ruling 15 explicitly deferred to Phase E ("making classificationFor() consult the registry... belongs in Phase E").
func (*Config) GetOAuthProvider ¶
func (c *Config) GetOAuthProvider(providerID string) (OAuthProviderConfig, bool)
GetOAuthProvider returns a specific OAuth provider configuration
func (*Config) GetWebSocketInactivityTimeout ¶
GetWebSocketInactivityTimeout returns the websocket inactivity timeout duration
func (*Config) IsSecureCookies ¶
IsSecureCookies returns whether cookies should have the Secure flag set. Returns true if explicitly configured or if TLS is enabled.
func (*Config) IsTestMode ¶
IsTestMode returns true if running in test mode
func (*Config) ResolveSecretReferences ¶
func (c *Config) ResolveSecretReferences(ctx context.Context, vault SecretResolver) error
ResolveSecretReferences walks the secret fields of the loaded Config and, for any field whose value is a vault://, env://, or file:// reference, dereferences it in place. Inline values (the dev/test default shape) are left unchanged.
The fields walked include every CategoryBootstrap secret plus the four Timmy API keys. The Timmy keys are CategoryOperational + Secret (see classification_registry.go), but their runtime consumers still read from the in-memory cfg.Timmy struct, so they need load-time resolution until #419 routes those consumers through SettingsService. Once #419 lands, the Timmy entries here can be removed and resolution moves to per-read.
It must run AFTER secrets.NewProvider (so the vault leg works) but BEFORE any consumer reads Auth.JWT.Secret or Database.URL (JWT init, DB connect). Secrets.VaultToken is resolved separately by ResolveSecretsConfigReferences, which runs before the provider is built; it is not re-resolved here.
vault references the secrets provider's vault leg; it may be nil, in which case any vault:// reference is reported as an error.
func (*Config) ResolveSecretsConfigReferences ¶
ResolveSecretsConfigReferences dereferences secret-reference values inside the Secrets block itself (currently only Secrets.VaultToken). It is called BEFORE the secrets provider is constructed, so it is passed no SecretResolver: only env:// and file:// references resolve here. A vault:// reference for Secrets.VaultToken is rejected — the provider cannot dereference a token it needs in order to exist.
An inline value (the dev/test default shape) is left unchanged.
func (*Config) Validate ¶
Validate validates the configuration at config.Load() time.
IMPORTANT: This runs at startup BEFORE the database settings service exists. As of #415, the config files (config-development.yml, config-test.yml, config-production.yml) are bootstrap-only — all CategoryOperational config (OAuth/SAML providers, websocket tunables, content extractors/OAuth, Timmy, administrators) was removed from the files and is now DB-seeded from the classification registry via SettingsService.SeedDefaults -> DefaultOperationalSettings(). Validating operational config here is wrong by construction: the file no longer carries it, so the values would always be the zero/default fallbacks rather than the effective runtime config.
Therefore Validate() checks ONLY CategoryBootstrap config (server, database, CORS, JWT secret). Operational config correctness is enforced by the classification registry + ValidateClassifications suite; its values come from SeedDefaults. See validateOperationalRemoved below for the rationale on each operational check that used to live here.
func (*Config) ValidateRequired ¶
ValidateRequired checks that every Required-classified bootstrap setting has a non-empty effective value. It is called from Load() after Validate() succeeds, so the Config has already had defaults, file values, and env overrides applied.
All five Required settings (server.port, server.interface, database.url, auth.build_mode, auth.jwt.secret) are CategoryBootstrap — their values are known at Load() time, making this the correct enforcement point.
All missing required keys are collected into a single error rather than failing on the first, mirroring the ValidateClassifications multi-error style.
type ConfigClass ¶
type ConfigClass struct {
Category Category
Secret bool
ValueKind ValueKind
Delivery *Delivery // nil for CategoryBootstrap
Visibility Visibility
Mutability Mutability
Consumers []Consumer
Required bool
}
ConfigClass is the complete classification of one configuration item.
func ClassificationFor ¶
func ClassificationFor(key string) ConfigClass
ClassificationFor returns the full ConfigClass for a setting key. It is the exported entry point for callers outside the config package.
type ConfluenceConfig ¶
type ConfluenceConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_CONTENT_SOURCE_CONFLUENCE_ENABLED"`
}
ConfluenceConfig holds settings for the delegated Confluence content source. OAuth-provider settings (client id/secret, endpoints, scopes, extra_authorize_params for audience=api.atlassian.com) live under content_oauth.providers.confluence.
This struct intentionally has no picker fields — Confluence has no picker UX; users paste page URLs.
func (ConfluenceConfig) IsConfigured ¶
func (c ConfluenceConfig) IsConfigured() bool
IsConfigured returns true when the source is enabled. The OAuth provider configuration is validated separately via the content_oauth registry.
type ConnectionPoolConfig ¶
type ConnectionPoolConfig struct {
MaxOpenConns int `yaml:"max_open_conns" env:"TMI_DB_MAX_OPEN_CONNS"` // Maximum open connections (default: 10)
MaxIdleConns int `yaml:"max_idle_conns" env:"TMI_DB_MAX_IDLE_CONNS"` // Maximum idle connections (default: 2)
ConnMaxLifetime int `yaml:"conn_max_lifetime" env:"TMI_DB_CONN_MAX_LIFETIME"` // Max connection lifetime in seconds (default: 240)
ConnMaxIdleTime int `yaml:"conn_max_idle_time" env:"TMI_DB_CONN_MAX_IDLE_TIME"` // Max idle time in seconds (default: 30)
}
ConnectionPoolConfig holds database connection pool settings
type Consumer ¶
type Consumer int
Consumer is a closed enum of the processes that read configuration. Add a value here when a new component type is introduced.
const ( // ConsumerMonolith is the primary TMI server process. ConsumerMonolith Consumer = iota // ConsumerTMIUX is the tmi-ux frontend client. ConsumerTMIUX // ConsumerWorkerExtractor is the extractor worker process. ConsumerWorkerExtractor // ConsumerWorkerChunkEmbed is the chunk-embed worker process. ConsumerWorkerChunkEmbed )
type ContentExtractorsConfig ¶
type ContentExtractorsConfig struct {
CompressedSizeBytes int64 `yaml:"compressed_size_bytes" env:"TMI_CONTENT_EXTRACTORS_COMPRESSED_SIZE_BYTES"`
DecompressedSizeBytes int64 `yaml:"decompressed_size_bytes" env:"TMI_CONTENT_EXTRACTORS_DECOMPRESSED_SIZE_BYTES"`
PartSizeBytes int64 `yaml:"part_size_bytes" env:"TMI_CONTENT_EXTRACTORS_PART_SIZE_BYTES"`
PPTXSlides int `yaml:"pptx_slides" env:"TMI_CONTENT_EXTRACTORS_PPTX_SLIDES"`
XLSXCells int `yaml:"xlsx_cells" env:"TMI_CONTENT_EXTRACTORS_XLSX_CELLS"`
MarkdownSizeBytes int64 `yaml:"markdown_size_bytes" env:"TMI_CONTENT_EXTRACTORS_MARKDOWN_SIZE_BYTES"`
WallClockBudget time.Duration `yaml:"wall_clock_budget" env:"TMI_CONTENT_EXTRACTORS_WALL_CLOCK_BUDGET"`
PerUserConcurrencyDefault int `yaml:"per_user_concurrency_default" env:"TMI_CONTENT_EXTRACTORS_PER_USER_CONCURRENCY_DEFAULT"`
// AsyncEnabled routes extraction through the async worker pipeline instead
// of inline processing. Requires NATS. Default false (inline mode).
AsyncEnabled bool `yaml:"async_enabled" env:"TMI_EXTRACTION_ASYNC_ENABLED"`
}
ContentExtractorsConfig holds operator-tunable defaults for the OOXML extractor pipeline. Each value must be > 0 and <= the corresponding hardcoded ceiling.
func DefaultContentExtractorsConfig ¶
func DefaultContentExtractorsConfig() ContentExtractorsConfig
DefaultContentExtractorsConfig returns the project-wide defaults documented in the OOXML design spec.
func (ContentExtractorsConfig) Validate ¶
func (c ContentExtractorsConfig) Validate() error
Validate enforces > 0 and <= ceiling for every field.
type ContentOAuthConfig ¶
type ContentOAuthConfig struct {
CallbackURL string `yaml:"callback_url" env:"TMI_CONTENT_OAUTH_CALLBACK_URL"`
AllowedClientCallbacks []string `yaml:"allowed_client_callbacks"`
Providers map[string]ContentOAuthProviderConfig `yaml:"providers"`
}
ContentOAuthConfig holds configuration for delegated content OAuth providers.
func (*ContentOAuthConfig) Validate ¶
func (c *ContentOAuthConfig) Validate(encryptionKey string) error
Validate returns an error if any enabled provider is missing required fields, or if at least one provider is enabled but the encryption key is empty/invalid.
type ContentOAuthProviderConfig ¶
type ContentOAuthProviderConfig struct {
Enabled bool `yaml:"enabled"`
Name string `yaml:"name"`
Icon string `yaml:"icon"`
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
AuthURL string `yaml:"auth_url"`
TokenURL string `yaml:"token_url"`
UserinfoURL string `yaml:"userinfo_url"`
RevocationURL string `yaml:"revocation_url"`
RequiredScopes []string `yaml:"required_scopes"`
ExtraAuthorizeParams map[string]string `yaml:"extra_authorize_params"`
}
ContentOAuthProviderConfig is one entry under content_oauth.providers.*
ExtraAuthorizeParams are appended to the authorize URL query string. They are useful for providers that require non-standard parameters beyond the standard OAuth 2.0 + PKCE set (e.g. Atlassian's audience=api.atlassian.com). yaml-only for now; if env-var support is needed later it can be added without breaking existing configs.
type ContentSourcesConfig ¶
type ContentSourcesConfig struct {
GoogleDrive GoogleDriveConfig `yaml:"google_drive"`
GoogleWorkspace GoogleWorkspaceConfig `yaml:"google_workspace"`
Confluence ConfluenceConfig `yaml:"confluence"`
Microsoft MicrosoftConfig `yaml:"microsoft"`
}
ContentSourcesConfig holds configuration for all content source providers.
type CookieConfig ¶
type CookieConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_COOKIE_ENABLED"` // Enable HttpOnly cookie-based auth (default: true)
Domain string `yaml:"domain" env:"TMI_COOKIE_DOMAIN"` // Cookie domain; leave empty for a host-only cookie (recommended). Never inferred from the bind address (#497).
Secure bool `yaml:"secure" env:"TMI_COOKIE_SECURE"` // Require HTTPS (auto-derived from TLSEnabled if not set)
}
CookieConfig holds HttpOnly session cookie configuration
type DatabaseConfig ¶
type DatabaseConfig struct {
URL string `yaml:"url" env:"TMI_DATABASE_URL"` // Connection string URL (12-factor app pattern) - REQUIRED
OracleWalletLocation string `yaml:"oracle_wallet_location" env:"TMI_ORACLE_WALLET_LOCATION"` // Path to Oracle wallet directory (Oracle ADB only)
ConnectionPool ConnectionPoolConfig `yaml:"connection_pool"`
Redis RedisConfig `yaml:"redis"`
}
DatabaseConfig holds database configuration. The primary configuration method is DATABASE_URL which contains all connection parameters. Database type is automatically detected from the URL scheme (postgres://, mysql://, etc.)
type Delivery ¶
type Delivery struct {
// StampedIntoEnvelope: the monolith copies this into job envelopes.
StampedIntoEnvelope bool
// monolith must agree. The validation suite enforces that SharedInvariant
// implies StampedIntoEnvelope.
SharedInvariant bool
}
Delivery describes how an operational setting reaches a process that cannot ask the monolith over HTTP. It is nil on bootstrap settings.
type DiscoveredField ¶
type DiscoveredField struct {
// DottedKey is the lower-snake-case dotted path produced from yaml
// tags (e.g. "auth.cookie.enabled").
DottedKey string
// EnvVar is the value of the `env` struct tag on the leaf field, or
// "" if absent. Used by the test to flag fields that should plumb a
// TMI_* env var but don't.
EnvVar string
}
DiscoveredField is a leaf-level field found by reflection over Config. It is the input the build-time completeness gate compares against the keys actually emitted by GetMigratableSettings — see TestMigratableSettings_CoverEveryConfigField in migratable_discovery_test.go.
func DiscoverConfigFields ¶
func DiscoverConfigFields() []DiscoveredField
DiscoverConfigFields walks Config with reflection and returns every leaf field that should be a candidate for the migratable-settings emit set.
Fields skipped (by design):
- Unexported fields (reflection cannot read them).
- Map types (auth.oauth.providers, auth.saml.providers, content_oauth.providers): the per-instance key is dynamic, so enumeration cannot produce a fixed dotted path. These are walked by the per-provider helpers in migratable_settings.go.
- Slice-of-struct types (administrators): same reasoning — variable cardinality, dynamic keys.
- Fields with yaml tag "-" (explicitly excluded).
- Anonymous (embedded) struct fields — none in Config today.
The walker recurses into named struct fields. A leaf is anything that is NOT itself a struct (after dereferencing pointers).
type EmbeddingProfile ¶
type EmbeddingProfile struct {
Model string `json:"model"`
Endpoint string `json:"endpoint"`
Dimension int `json:"dimension"`
}
EmbeddingProfile is the shared, correctness-invariant embedding configuration. The same profile MUST be used to embed documents at ingest (by the tmi-chunk-embed worker) and to embed the user's query at search time (by the monolith). Disagreement makes vector search silently wrong.
The API key is deliberately NOT part of this struct: it is a secret, resolved independently on each side from its own secret source, never carried in a job envelope.
func (EmbeddingProfile) Validate ¶
func (p EmbeddingProfile) Validate() error
Validate returns an error if the profile is missing a required field.
type Flags ¶
Flags is the parsed result of ParseFlags.
func ParseFlagsExt ¶
ParseFlagsExt parses command line flags and returns the full Flags struct. --version / -v is a re-entrant, side-effect-free path: it can be invoked while a server is already running because it does not touch the database, listening sockets, or any shared on-disk state.
type GoogleDriveConfig ¶
type GoogleDriveConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_ENABLED"`
ServiceAccountEmail string `yaml:"service_account_email" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_SERVICE_ACCOUNT_EMAIL"`
CredentialsFile string `yaml:"credentials_file" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_CREDENTIALS_FILE"`
BrowserOAuthClientID string `yaml:"browser_oauth_client_id" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_BROWSER_OAUTH_CLIENT_ID"`
PickerDeveloperKey string `yaml:"picker_developer_key" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_PICKER_DEVELOPER_KEY"`
PickerAppID string `yaml:"picker_app_id" env:"TMI_CONTENT_SOURCE_GOOGLE_DRIVE_PICKER_APP_ID"`
}
GoogleDriveConfig holds Google Drive service account configuration.
The Browser*/Picker* fields are public, browser-safe values used to bootstrap the in-browser Google Picker for service-mode Drive sources. They are optional; when all three are set, the /config response advertises them via ContentProvider.picker_config so the client can render a real picker instead of URL-paste UX. None of these values authorize anything on their own — the user OAuth handshake happens client-side via Google Identity Services with PKCE; no client_secret is ever surfaced.
func (GoogleDriveConfig) HasPickerConfig ¶
func (c GoogleDriveConfig) HasPickerConfig() bool
HasPickerConfig returns true when all three browser-safe picker bootstrap values are set. The /config handler emits ContentProvider.picker_config only when all three are present — partial configuration is treated as unconfigured.
func (GoogleDriveConfig) IsConfigured ¶
func (c GoogleDriveConfig) IsConfigured() bool
IsConfigured returns true if Google Drive has the minimum required configuration.
func (GoogleDriveConfig) PickerConfig ¶
func (c GoogleDriveConfig) PickerConfig() map[string]string
PickerConfig returns the browser-safe picker bootstrap map suitable for the /config response. Returns nil when HasPickerConfig is false.
type GoogleWorkspaceConfig ¶
type GoogleWorkspaceConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_CONTENT_SOURCE_GOOGLE_WORKSPACE_ENABLED"`
PickerDeveloperKey string `yaml:"picker_developer_key" env:"TMI_CONTENT_SOURCE_GOOGLE_WORKSPACE_PICKER_DEVELOPER_KEY"`
PickerAppID string `yaml:"picker_app_id" env:"TMI_CONTENT_SOURCE_GOOGLE_WORKSPACE_PICKER_APP_ID"`
}
GoogleWorkspaceConfig holds settings for the delegated Google Workspace content source. OAuth-provider settings (client id/secret, endpoints, scopes) live under content_oauth.providers.google_workspace.
PickerDeveloperKey and PickerAppID are Google Cloud project values that the browser-side Google Picker JS needs; they are not secrets.
func (GoogleWorkspaceConfig) IsConfigured ¶
func (c GoogleWorkspaceConfig) IsConfigured() bool
IsConfigured returns true when the source is enabled and both picker inputs are non-empty.
type JWTConfig ¶
type JWTConfig struct {
Secret string `yaml:"secret" env:"TMI_JWT_SECRET"`
ExpirationSeconds int `yaml:"expiration_seconds" env:"TMI_JWT_EXPIRATION_SECONDS"`
SigningMethod string `yaml:"signing_method" env:"TMI_JWT_SIGNING_METHOD"`
RefreshTokenDays int `yaml:"refresh_token_days" env:"TMI_REFRESH_TOKEN_DAYS"` // Refresh token TTL in days (default: 7)
SessionLifetimeDays int `yaml:"session_lifetime_days" env:"TMI_SESSION_LIFETIME_DAYS"` // Absolute session lifetime in days (default: 7)
}
JWTConfig holds JWT configuration
type LoggingConfig ¶
type LoggingConfig struct {
// Level is the minimum log level to emit. One of: debug, info, warn, error.
Level string `yaml:"level" env:"TMI_LOG_LEVEL"`
// IsDev enables developer-friendly text formatting plus source file/line
// tagging on every record. Production deployments should set this false.
IsDev bool `yaml:"is_dev" env:"TMI_LOG_IS_DEV"`
// IsTest forces test-mode behavior (e.g., stable timestamps).
IsTest bool `yaml:"is_test" env:"TMI_LOG_IS_TEST"`
// LogDir is the directory holding the active log file (tmi.log) and
// rotated backups. Created on startup if missing. Default: "logs".
LogDir string `yaml:"log_dir" env:"TMI_LOG_DIR"`
// MaxAgeDays is the maximum number of days to retain rotated backup
// files before lumberjack deletes them. 0 = retain indefinitely
// (subject to MaxBackups). Default: 7.
MaxAgeDays int `yaml:"max_age_days" env:"TMI_LOG_MAX_AGE_DAYS"`
// MaxSizeMB is the maximum size of the active log file (logs/tmi.log)
// in megabytes before lumberjack rotates it. The active file is renamed
// with a timestamp suffix and a fresh tmi.log is opened. Default: 100.
MaxSizeMB int `yaml:"max_size_mb" env:"TMI_LOG_MAX_SIZE_MB"`
// MaxBackups is the maximum number of rotated backup files to retain.
// 0 = retain all (subject to MaxAgeDays). Default: 10.
MaxBackups int `yaml:"max_backups" env:"TMI_LOG_MAX_BACKUPS"`
// AlsoLogToConsole, when true, mirrors every log line to stdout in
// addition to the file. Useful in development. Default: true.
AlsoLogToConsole bool `yaml:"also_log_to_console" env:"TMI_LOG_ALSO_LOG_TO_CONSOLE"`
// CloudErrorThreshold is the number of consecutive cloud-sink write
// failures observed at 60-second poll intervals before a single Warn
// entry is emitted. Counter resets on a quiet interval. Set to 0 to
// disable the alarm. Default: 5.
CloudErrorThreshold int `yaml:"cloud_error_threshold" env:"TMI_LOG_CLOUD_ERROR_THRESHOLD"`
// Enhanced debug logging options
LogAPIRequests bool `yaml:"log_api_requests" env:"TMI_LOG_API_REQUESTS"`
LogAPIResponses bool `yaml:"log_api_responses" env:"TMI_LOG_API_RESPONSES"`
LogWebSocketMsg bool `yaml:"log_websocket_messages" env:"TMI_LOG_WEBSOCKET_MESSAGES"`
RedactAuthTokens bool `yaml:"redact_auth_tokens" env:"TMI_LOG_REDACT_AUTH_TOKENS"`
SuppressUnauthenticatedLogs bool `yaml:"suppress_unauthenticated_logs" env:"TMI_LOG_SUPPRESS_UNAUTH_LOGS"`
}
LoggingConfig holds configuration for the slog-based logger and its lumberjack-backed file rotator. Most knobs map directly onto gopkg.in/natefinch/lumberjack.v2; see the wiki page "Operator Guide: Logging & Log Rotation" for full operator-facing documentation.
type MicrosoftConfig ¶
type MicrosoftConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_CONTENT_SOURCE_MICROSOFT_ENABLED"`
TenantID string `yaml:"tenant_id" env:"TMI_CONTENT_SOURCE_MICROSOFT_TENANT_ID"`
ClientID string `yaml:"client_id" env:"TMI_CONTENT_SOURCE_MICROSOFT_CLIENT_ID"`
ApplicationObjectID string `yaml:"application_object_id" env:"TMI_CONTENT_SOURCE_MICROSOFT_APPLICATION_OBJECT_ID"`
PickerOrigin string `yaml:"picker_origin" env:"TMI_CONTENT_SOURCE_MICROSOFT_PICKER_ORIGIN"`
}
MicrosoftConfig holds Microsoft Graph (OneDrive-for-Business + SharePoint) content source configuration. The OAuth client_id/client_secret live under content_oauth.providers.microsoft; this struct holds the source-side values needed for picker initialization (client_id and tenant_id are surfaced to the browser via the picker-token endpoint) and the picker-grant call (application_object_id is the TMI Entra app's object id, used as the Graph permission grantee).
func (MicrosoftConfig) IsConfigured ¶
func (c MicrosoftConfig) IsConfigured() bool
IsConfigured returns true when all required fields are present and Enabled. PickerOrigin is optional — Experience 1 (paste-URL) works without it; only Experience 2 (picker) requires it. Operators must set it for picker UX.
type MigratableSetting ¶
type MigratableSetting struct {
Key string
Value string
Type string
Description string
Secret bool // true = mask value in API responses (kept for back-compat; mirrors Class.Secret)
Source string // "config" or "environment"
EnvVar string // TMI_* environment variable that overrides this setting ("" if none)
Class ConfigClass
// Explicit reports whether an operator actually supplied this value —
// the env var is set, or the key was present in the YAML file — as
// opposed to it being the struct default that every key carries.
//
// The settings service needs this to decide precedence. Source alone
// cannot: it reports "config" both for "written in the file" and for
// "nobody ever set this", and treating the latter as configured is what
// made every operational database value unreachable (#415).
Explicit bool
}
MigratableSetting represents a setting that can be migrated from config to database
func DefaultOperationalSettings ¶
func DefaultOperationalSettings() []MigratableSetting
DefaultOperationalSettings returns the operational-category settings from a default Config. It is the seed source for the DB-backed settings service. Bootstrap-category settings are intentionally excluded — they are file/env only and must never be written to the database.
func (MigratableSetting) IsSecret ¶
func (m MigratableSetting) IsSecret() bool
IsSecret reports whether this setting's value must never be shown in an API response or written to a log.
Consult this rather than either flag directly. The two are NOT equivalent and the difference is a live trap: Class.Secret is false for the whole `auth.oauth.providers.`, `auth.saml.providers.` and `content_oauth.providers.` subtrees (see prefixClassifications — a blanket true there would mis-mask non-secret sub-keys like .client_id), so for those subtrees secrecy is carried ONLY by the per-setting Secret flag that the provider helpers set on .client_secret, .sp_private_key and .idp_metadata_b64xml. The Class.Secret->Secret sync in GetMigratableSettings runs in one direction only, so checking Class.Secret alone silently treats a real OAuth client secret as public.
type Mutability ¶
type Mutability int
Mutability answers: can it change after startup?
const ( // MutabilityStatic: read once at boot; a change needs a restart. MutabilityStatic Mutability = iota // MutabilityHot: re-read at use time; a runtime edit takes effect at once. MutabilityHot )
func (Mutability) String ¶
func (m Mutability) String() string
type OAuthConfig ¶
type OAuthConfig struct {
CallbackURL string `yaml:"callback_url" env:"TMI_OAUTH_CALLBACK_URL"`
Providers map[string]OAuthProviderConfig `yaml:"providers"`
// ClientCallbackAllowList controls which client_callback URLs the
// /oauth2/authorize endpoint is willing to redirect to after a successful
// login. Each entry is either an exact URL or a wildcard pattern ending
// in "*". An empty list rejects any client_callback (fail-closed). Set
// via env TMI_OAUTH_CLIENT_CALLBACK_ALLOWLIST as a comma-separated list.
ClientCallbackAllowList []string `yaml:"client_callback_allowlist" env:"TMI_OAUTH_CLIENT_CALLBACK_ALLOWLIST"`
}
OAuthConfig holds OAuth configuration
type OAuthProviderConfig ¶
type OAuthProviderConfig struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Enabled bool `yaml:"enabled"`
Icon string `yaml:"icon"`
ClientID string `yaml:"client_id"`
ClientSecret string `yaml:"client_secret"`
AuthorizationURL string `yaml:"authorization_url"`
TokenURL string `yaml:"token_url"`
UserInfo []UserInfoEndpoint `yaml:"userinfo"`
Issuer string `yaml:"issuer"`
JWKSURL string `yaml:"jwks_url"`
Scopes []string `yaml:"scopes"`
AdditionalParams map[string]string `yaml:"additional_params"`
AuthHeaderFormat string `yaml:"auth_header_format,omitempty"`
AcceptHeader string `yaml:"accept_header,omitempty"`
}
OAuthProviderConfig holds configuration for an OAuth provider
type ObservabilityConfig ¶
type ObservabilityConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_OTEL_ENABLED"`
SamplingRate float64 `yaml:"sampling_rate" env:"TMI_OTEL_SAMPLING_RATE"`
PrometheusPort int `yaml:"prometheus_port" env:"TMI_OTEL_PROMETHEUS_PORT"`
}
ObservabilityConfig holds OpenTelemetry configuration
type OperatorConfig ¶
type OperatorConfig struct {
Name string `yaml:"name" env:"TMI_OPERATOR_NAME"`
Contact string `yaml:"contact" env:"TMI_OPERATOR_CONTACT"`
Jurisdiction string `yaml:"jurisdiction" env:"TMI_OPERATOR_JURISDICTION"`
}
OperatorConfig holds operator/maintainer information
type ProcessEnvVar ¶
type ProcessEnvVar struct {
// Name is the exact variable name or, for a Pattern, the documented
// shape with the operator-supplied part in angle brackets, e.g.
// "TMI_SECRET_<KEY>"; the text before the first '<' is the prefix the
// code scans for.
Name string
// Binary names the reader: "server", "rotator" or "workers" (chunkembed,
// extractor, worker-probe, component-controller).
Binary string
// Purpose is the one-line operator-facing description. No angle
// brackets, backticks or pipes — it is rendered into a Markdown table.
Purpose string
// Secret marks a value that must never be logged or echoed.
Secret bool
// Pattern marks Name as a prefix pattern rather than a fixed name.
Pattern bool
}
ProcessEnvVar documents one environment variable that a TMI binary reads straight from its process environment — outside the Config struct, the SettingDef registry and the config file. The registry cannot see these, so they are declared here by hand and rendered into config-reference.md (which doubles as the TMI_* allowlist) by GenerateReferenceMarkdown. The gate test in process_env_test.go fails when a TMI_* token appears in Go source without being declared here or in the registry (#810).
func ProcessEnvVars ¶
func ProcessEnvVars() []ProcessEnvVar
ProcessEnvVars returns a copy of the process-environment inventory, so callers cannot mutate it.
type RedisConfig ¶
type RedisConfig struct {
URL string `yaml:"url" env:"TMI_REDIS_URL"` // Connection string URL (redis://[:password@]host:port[/db]), takes precedence over individual fields; rediss:// also enables TLS
Host string `yaml:"host" env:"TMI_REDIS_HOST"`
Port string `yaml:"port" env:"TMI_REDIS_PORT"`
Password string `yaml:"password" env:"TMI_REDIS_PASSWORD"`
DB int `yaml:"db" env:"TMI_REDIS_DB"`
TLSEnabled bool `yaml:"tls_enabled" env:"TMI_REDIS_TLS_ENABLED"` // Connect with TLS; the server cert is verified against tls_ca_file only
TLSCAFile string `yaml:"tls_ca_file" env:"TMI_REDIS_TLS_CA_FILE"` // PEM CA that signed the Redis server certificate (required when TLS is on)
}
RedisConfig holds Redis configuration
type SAMLConfig ¶
type SAMLConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_SAML_ENABLED"`
Providers map[string]SAMLProviderConfig `yaml:"providers"`
}
SAMLConfig holds SAML configuration
type SAMLProviderConfig ¶
type SAMLProviderConfig struct {
ID string `yaml:"id"`
Name string `yaml:"name"`
Enabled bool `yaml:"enabled"`
Icon string `yaml:"icon"`
EntityID string `yaml:"entity_id"`
MetadataURL string `yaml:"metadata_url"`
MetadataXML string `yaml:"metadata_xml"`
ACSURL string `yaml:"acs_url"`
SLOURL string `yaml:"slo_url"`
SPPrivateKey string `yaml:"sp_private_key"`
SPPrivateKeyPath string `yaml:"sp_private_key_path"`
SPCertificate string `yaml:"sp_certificate"`
SPCertificatePath string `yaml:"sp_certificate_path"`
IDPMetadataURL string `yaml:"idp_metadata_url"`
IDPMetadataB64XML string `yaml:"idp_metadata_b64xml"` // Base64-encoded IdP metadata XML
AllowIDPInitiated bool `yaml:"allow_idp_initiated"`
ForceAuthn bool `yaml:"force_authn"`
SignRequests bool `yaml:"sign_requests"`
NameIDAttribute string `yaml:"name_id_attribute"`
EmailAttribute string `yaml:"email_attribute"`
NameAttribute string `yaml:"name_attribute"`
GivenNameAttribute string `yaml:"given_name_attribute"`
FamilyNameAttribute string `yaml:"family_name_attribute"`
GroupsAttribute string `yaml:"groups_attribute"`
}
SAMLProviderConfig holds configuration for a SAML provider. It is only ever a value of SAMLConfig.Providers, which overrideStructWithEnv hands to overrideSAMLProviders; that reads SAML_PROVIDERS_<ID>_<FIELD> (no TMI_ prefix), so env tags on these fields would never be consulted and there are none.
type SSRFConfig ¶
type SSRFConfig struct {
IssueURI SSRFURIConfig `yaml:"issue_uri"`
DocumentURI SSRFURIConfig `yaml:"document_uri"`
RepositoryURI SSRFURIConfig `yaml:"repository_uri"`
Timmy SSRFURIConfig `yaml:"timmy"`
Webhook SSRFURIConfig `yaml:"webhook"`
}
SSRFConfig holds SSRF protection settings for URI validation
type SSRFURIConfig ¶
SSRFURIConfig holds allowlist and scheme configuration for a single URI type
type SecretResolver ¶
type SecretResolver interface {
// ResolveVault returns the secret value stored at the given vault path.
ResolveVault(ctx context.Context, path string) (string, error)
}
SecretResolver dereferences a vault:// secret locator. It is implemented in a package that can import internal/secrets; internal/config cannot import internal/secrets directly (that would create an import cycle, because internal/secrets imports internal/config for config.SecretsConfig).
type SecretsConfig ¶
type SecretsConfig struct {
Provider string `yaml:"provider" env:"TMI_SECRETS_PROVIDER"` // "env" (default), "file", "vault", "aws"
// Directory holding one file per secret key (provider "file").
FileDir string `yaml:"file_dir" env:"TMI_SECRETS_FILE_DIR"`
// HashiCorp Vault (design only - implementation deferred)
VaultAddress string `yaml:"vault_address" env:"TMI_VAULT_ADDRESS"`
VaultToken string `yaml:"vault_token" env:"TMI_VAULT_TOKEN"`
VaultPath string `yaml:"vault_path" env:"TMI_VAULT_PATH"`
// AWS Secrets Manager
AWSRegion string `yaml:"aws_region" env:"TMI_AWS_REGION"`
AWSSecretName string `yaml:"aws_secret_name" env:"TMI_AWS_SECRET_NAME"`
}
SecretsConfig holds configuration for external secret providers
type ServerConfig ¶
type ServerConfig struct {
Port string `yaml:"port" env:"TMI_SERVER_PORT"`
Interface string `yaml:"interface" env:"TMI_SERVER_INTERFACE"`
BaseURL string `yaml:"base_url" env:"TMI_SERVER_BASE_URL"` // Public base URL for callbacks (auto-inferred if empty)
ReadTimeout time.Duration `yaml:"read_timeout" env:"TMI_SERVER_READ_TIMEOUT"`
WriteTimeout time.Duration `yaml:"write_timeout" env:"TMI_SERVER_WRITE_TIMEOUT"`
IdleTimeout time.Duration `yaml:"idle_timeout" env:"TMI_SERVER_IDLE_TIMEOUT"`
TLSEnabled bool `yaml:"tls_enabled" env:"TMI_SERVER_TLS_ENABLED"`
TLSCertFile string `yaml:"tls_cert_file" env:"TMI_SERVER_TLS_CERT_FILE"`
TLSKeyFile string `yaml:"tls_key_file" env:"TMI_SERVER_TLS_KEY_FILE"`
TLSSubjectName string `yaml:"tls_subject_name" env:"TMI_SERVER_TLS_SUBJECT_NAME"`
HTTPToHTTPSRedirect bool `yaml:"http_to_https_redirect" env:"TMI_SERVER_HTTP_TO_HTTPS_REDIRECT"`
TrustedProxies []string `yaml:"trusted_proxies" env:"TMI_TRUSTED_PROXIES"` // Comma-separated CIDRs/IPs for X-Forwarded-For validation
RateLimitPublicRPM int `yaml:"ratelimit_public_rpm" env:"TMI_RATELIMIT_PUBLIC_RPM"` // Requests/min per IP for public endpoints (default: 10)
DisableRateLimiting bool `yaml:"disable_rate_limiting" env:"TMI_DISABLE_RATE_LIMITING"` // Disable all rate limiting (dev/test only)
// RequireIfMatch controls optimistic-locking enforcement on PUT/PATCH
// (T14 / #385). When false (default this release), missing If-Match returns
// a Deprecation/Warning header but the write proceeds. When true, missing
// If-Match returns 428 Precondition Required. Flip to true in the next release.
RequireIfMatch bool `yaml:"require_if_match" env:"TMI_REQUIRE_IF_MATCH"`
CORS CORSConfig `yaml:"cors"`
}
ServerConfig holds HTTP server configuration
type SettingDef ¶
type SettingDef struct {
// Key is the canonical dotted setting key, e.g. "server.port".
Key string
// Class is the full classification: category, visibility, secrecy,
// mutability, consumers, delivery.
Class ConfigClass
// Type is the value's canonical type: "string", "bool", "int",
// "float", or "json".
Type string
// Description is human-readable and required; it feeds generated docs
// and the admin API.
Description string
// Default is the canonical string form of the compiled-in default.
// Required for operational settings, which have no config file to
// fall back to.
Default string
// YAMLPath, EnvVar and Get are populated only for settings currently
// delivered by config file or environment: all bootstrap settings, plus
// operational settings still in transition (see Transitional).
YAMLPath string
EnvVar string
// Get extracts this setting's current value from a loaded Config as a
// canonical string. Nil for settings with no config-file path.
Get func(*Config) string
// Transitional marks an operational setting that still has a config/env
// delivery path during the Phase A-E cutover described in the spec.
// Every Transitional entry is scheduled for removal in Phase E; the
// ratchet test in Task 9 prevents new ones from being added.
Transitional bool
// Seeded marks an operational setting that is written into the
// system_settings table on database initialisation. It is deliberately
// explicit rather than inferred: the set of seeded keys does not
// coincide with any other property of a setting, and inferring it wrong
// would either seed rows nobody set or stop seeding rows the server
// expects. Only operational settings may be Seeded.
Seeded bool
// OmitWhenEmpty reproduces the conditional emission of the pre-registry
// builders: this setting is left out of GetMigratableSettings() entirely
// when its value is empty, rather than emitted with an empty value.
//
// It is load-bearing, not cosmetic. DefaultOperationalSettings() feeds
// both SeedDefaults (which would otherwise write a row per omitted key on
// every fresh database) and #794's origin backfill (which uses the same
// set to decide seeded-vs-explicit, and therefore env-vs-database
// precedence on existing databases).
//
// Phase E removes this along with the rest of the transitional machinery.
OmitWhenEmpty bool
}
SettingDef is the single authoritative declaration of one configuration setting. Every setting the server can read — bootstrap or operational, config-delivered or database-seeded — has exactly one SettingDef.
Which fields are legal depends on Class.Category; ValidateSettingDefs enforces that (see setting_def_validation.go).
func AllSettingDefs ¶
func AllSettingDefs() []SettingDef
AllSettingDefs returns a copy of the registry, so callers cannot mutate it.
func DefFor ¶
func DefFor(key string) (SettingDef, bool)
DefFor returns the declaration for a setting key, and whether one exists.
func SeedableOperationalDefs ¶
func SeedableOperationalDefs() []SettingDef
SeedableOperationalDefs returns the operational settings that should be seeded into system_settings on database initialisation, sorted by key.
This is the single source for the seed list. DefaultSystemSettings() in api/models projects it, rather than maintaining a parallel list — the parallel list is what left rate_limit.requests_per_minute and rate_limit.requests_per_hour seeded but unclassified (#809).
Filtering on Seeded, not just CategoryOperational, is deliberate: the registry holds far more operational defs (config/env-delivered knobs like websocket.inactivity_timeout_seconds) than are ever written into system_settings. Seeded marks exactly the subset that is.
func (SettingDef) IsSecret ¶
func (d SettingDef) IsSecret() bool
IsSecret reports whether this setting's value must never appear in an API response or a log line.
This answers the question only for STATICALLY DECLARED settings. The per-provider keys under auth.oauth.providers., auth.saml.providers. and content_oauth.providers. are generated per configured provider and have no SettingDef; for those, Class.Secret is deliberately false (a blanket true would mis-mask non-secret sub-keys like .client_id) and secrecy is carried only by the per-setting Secret flag that the provider helpers set on .client_secret, .sp_private_key and .idp_metadata_b64xml. Never reach for this method to decide secrecy for a provider key — use MigratableSetting.IsSecret(), which ORs both flags.
type StampedConfig ¶
type StampedConfig struct {
Embedding EmbeddingProfile `json:"embedding"`
}
StampedConfig is the subset of operational configuration the monolith stamps into every job envelope. It carries only non-secret values; secrets are resolved from mounted secret sources by each consumer.
func (StampedConfig) Validate ¶
func (s StampedConfig) Validate() error
Validate returns an error if the stamped configuration is incomplete.
type StampedConfigProvider ¶
type StampedConfigProvider interface {
Get(ctx context.Context) (StampedConfig, error)
}
StampedConfigProvider is the single read point for stamped configuration. Both the monolith's job-envelope builder and the monolith's own Timmy query path read through this interface, which is what makes the shared-invariant guarantee structural rather than a matter of discipline.
type TimmyConfig ¶
type TimmyConfig struct {
Enabled bool `yaml:"enabled" env:"TMI_TIMMY_ENABLED"`
LLMProvider string `yaml:"llm_provider" env:"TMI_TIMMY_LLM_PROVIDER"`
LLMModel string `yaml:"llm_model" env:"TMI_TIMMY_LLM_MODEL"`
LLMAPIKey string `yaml:"llm_api_key" env:"TMI_TIMMY_LLM_API_KEY"`
LLMBaseURL string `yaml:"llm_base_url" env:"TMI_TIMMY_LLM_BASE_URL"`
// LLMMaxTokens is the maximum tokens a chat completion may generate.
// Anthropic's Messages API requires it on every request (#754 phase 2);
// OpenAI's does not. Zero means "use the provider backend's default"
// (internal/llm.anthropicDefaultMaxTokens for Anthropic; unset/provider
// default for OpenAI) — see DefaultTimmyConfig for the seeded value.
LLMMaxTokens int `yaml:"llm_max_tokens" env:"TMI_TIMMY_LLM_MAX_TOKENS"`
TextEmbeddingProvider string `yaml:"text_embedding_provider" env:"TMI_TIMMY_TEXT_EMBEDDING_PROVIDER"`
TextEmbeddingModel string `yaml:"text_embedding_model" env:"TMI_TIMMY_TEXT_EMBEDDING_MODEL"`
TextEmbeddingAPIKey string `yaml:"text_embedding_api_key" env:"TMI_TIMMY_TEXT_EMBEDDING_API_KEY"`
TextEmbeddingBaseURL string `yaml:"text_embedding_base_url" env:"TMI_TIMMY_TEXT_EMBEDDING_BASE_URL"`
EmbeddingDimension int `yaml:"embedding_dimension" env:"TMI_TIMMY_EMBEDDING_DIMENSION"`
TextRetrievalTopK int `yaml:"text_retrieval_top_k" env:"TMI_TIMMY_TEXT_RETRIEVAL_TOP_K"`
CodeEmbeddingProvider string `yaml:"code_embedding_provider" env:"TMI_TIMMY_CODE_EMBEDDING_PROVIDER"`
CodeEmbeddingModel string `yaml:"code_embedding_model" env:"TMI_TIMMY_CODE_EMBEDDING_MODEL"`
CodeEmbeddingAPIKey string `yaml:"code_embedding_api_key" env:"TMI_TIMMY_CODE_EMBEDDING_API_KEY"`
CodeEmbeddingBaseURL string `yaml:"code_embedding_base_url" env:"TMI_TIMMY_CODE_EMBEDDING_BASE_URL"`
CodeRetrievalTopK int `yaml:"code_retrieval_top_k" env:"TMI_TIMMY_CODE_RETRIEVAL_TOP_K"`
QueryDecompositionEnabled bool `yaml:"query_decomposition_enabled" env:"TMI_TIMMY_QUERY_DECOMPOSITION_ENABLED"`
RerankProvider string `yaml:"rerank_provider" env:"TMI_TIMMY_RERANK_PROVIDER"`
RerankModel string `yaml:"rerank_model" env:"TMI_TIMMY_RERANK_MODEL"`
RerankAPIKey string `yaml:"rerank_api_key" env:"TMI_TIMMY_RERANK_API_KEY"`
RerankBaseURL string `yaml:"rerank_base_url" env:"TMI_TIMMY_RERANK_BASE_URL"`
RerankTopK int `yaml:"rerank_top_k" env:"TMI_TIMMY_RERANK_TOP_K"`
MaxConversationHistory int `yaml:"max_conversation_history" env:"TMI_TIMMY_MAX_CONVERSATION_HISTORY"`
OperatorSystemPrompt string `yaml:"operator_system_prompt" env:"TMI_TIMMY_OPERATOR_SYSTEM_PROMPT"`
MaxMemoryMB int `yaml:"max_memory_mb" env:"TMI_TIMMY_MAX_MEMORY_MB"`
InactivityTimeoutSeconds int `yaml:"inactivity_timeout_seconds" env:"TMI_TIMMY_INACTIVITY_TIMEOUT_SECONDS"`
MaxMessagesPerUserPerHour int `yaml:"max_messages_per_user_per_hour" env:"TMI_TIMMY_MAX_MESSAGES_PER_USER_PER_HOUR"`
MaxSessionsPerThreatModel int `yaml:"max_sessions_per_threat_model" env:"TMI_TIMMY_MAX_SESSIONS_PER_THREAT_MODEL"`
MaxConcurrentLLMRequests int `yaml:"max_concurrent_llm_requests" env:"TMI_TIMMY_MAX_CONCURRENT_LLM_REQUESTS"`
ChunkSize int `yaml:"chunk_size" env:"TMI_TIMMY_CHUNK_SIZE"`
ChunkOverlap int `yaml:"chunk_overlap" env:"TMI_TIMMY_CHUNK_OVERLAP"`
LLMTimeoutSeconds int `yaml:"llm_timeout_seconds" env:"TMI_TIMMY_LLM_TIMEOUT_SECONDS"`
EmbeddingCleanupIntervalMinutes int `yaml:"embedding_cleanup_interval_minutes" env:"TMI_TIMMY_EMBEDDING_CLEANUP_INTERVAL_MINUTES"`
EmbeddingIdleDaysActive int `yaml:"embedding_idle_days_active" env:"TMI_TIMMY_EMBEDDING_IDLE_DAYS_ACTIVE"`
EmbeddingIdleDaysClosed int `yaml:"embedding_idle_days_closed" env:"TMI_TIMMY_EMBEDDING_IDLE_DAYS_CLOSED"`
// DumpExtractedTextToNote, when true, persists the markdown produced by
// each successful content extraction as a Note on the parent threat
// model. Dev/test inspection aid only — the server refuses to start
// with this flag enabled if Auth.BuildMode == "production".
DumpExtractedTextToNote bool `yaml:"dump_extracted_text_to_note" env:"TMI_TIMMY_DUMP_EXTRACTED_TEXT_TO_NOTE"`
}
TimmyConfig holds configuration for the Timmy AI assistant feature
func DefaultTimmyConfig ¶
func DefaultTimmyConfig() TimmyConfig
DefaultTimmyConfig returns configuration with sensible defaults
func (TimmyConfig) IsCodeIndexConfigured ¶
func (tc TimmyConfig) IsCodeIndexConfigured() bool
IsCodeIndexConfigured returns true if the code embedding provider and model are configured
func (TimmyConfig) IsConfigured ¶
func (tc TimmyConfig) IsConfigured() bool
IsConfigured returns true if the required LLM and text embedding providers are configured
func (TimmyConfig) IsRerankConfigured ¶
func (tc TimmyConfig) IsRerankConfigured() bool
IsRerankConfigured returns true if the reranker provider and model are configured
func (TimmyConfig) ValidateLLMMaxTokens ¶
func (tc TimmyConfig) ValidateLLMMaxTokens() error
ValidateLLMMaxTokens reports whether LLMMaxTokens is a valid value. Zero is not an error — it means "use the backend's default" (see DefaultTimmyConfig and internal/llm.anthropicDefaultMaxTokens) — only a negative value is rejected.
func (TimmyConfig) ValidateLLMProvider ¶
func (tc TimmyConfig) ValidateLLMProvider() error
ValidateLLMProvider reports whether LLMProvider is a recognized value. An empty LLMProvider is not an error here — that "not configured" state is IsConfigured's concern; this method only rejects an unrecognized non-empty value, which deserves its own message rather than being folded into "not configured."
type UserInfoEndpoint ¶
type UserInfoEndpoint struct {
URL string `yaml:"url" json:"url"`
Claims map[string]string `yaml:"claims" json:"claims,omitempty"`
}
UserInfoEndpoint represents a single userinfo endpoint and its claim mappings
type ValueKind ¶
type ValueKind int
ValueKind answers: is the stored value the secret itself, or a pointer to it?
The registry records the DEFAULT (inline) kind of a setting. A secret VALUE may still be a vault://, env://, or file:// reference at runtime: the same key is typically inline in dev and a reference in prod. Such references are dereferenced at startup, per value, by ResolveSecretValue (see secret_reference.go) — IsSecretReference inspects the value's scheme prefix, so no registry change is needed to support a reference value.
ValueKindReference in the REGISTRY is therefore reserved for a key that is ALWAYS a reference regardless of deployment; today no key is classified that way, so the "ValueKindReference => Secret" validation rule holds vacuously.
const ( // ValueKindInline means the field holds the actual value. This is the // zero value and a safe default for non-secret settings. A secret field // classified inline may still carry a vault://, env://, or file:// // reference value, resolved at load time by ResolveSecretValue. ValueKindInline ValueKind = iota // ValueKindReference means the field ALWAYS holds a locator (vault://..., // a file path, an env-var name) dereferenced at use time, in every // deployment. Only valid when Secret. Per-value references on an // otherwise-inline key do NOT require this — see the type doc above. ValueKindReference )
type Visibility ¶
type Visibility int
Visibility answers: who may read this setting through the API?
const ( // VisibilityInternal: server-side only, never in any API response. VisibilityInternal Visibility = iota // VisibilityAdminOnly: visible to admins via /admin config endpoints. VisibilityAdminOnly // VisibilityPublic: exposed on the unauthenticated /config endpoint. VisibilityPublic )
func (Visibility) String ¶
func (v Visibility) String() string
type WebSocketConfig ¶
type WebSocketConfig struct {
InactivityTimeoutSeconds int `yaml:"inactivity_timeout_seconds" env:"TMI_WEBSOCKET_INACTIVITY_TIMEOUT_SECONDS"`
}
WebSocketConfig holds WebSocket timeout configuration
type WebhookConfig ¶
type WebhookConfig struct {
AllowHTTPTargets bool `yaml:"allow_http_targets" env:"TMI_WEBHOOK_ALLOW_HTTP_TARGETS"` // Allow non-HTTPS webhook URLs (e.g., for intra-cluster communication)
}
WebhookConfig holds webhook configuration
Source Files
¶
- classification.go
- classification_registry.go
- classification_validation.go
- cli.go
- config.go
- confluence_config.go
- content_extractors.go
- content_oauth.go
- content_sources.go
- example_gen.go
- google_workspace_config.go
- migratable_discovery.go
- migratable_settings.go
- process_env.go
- reference_gen.go
- sanitize_url.go
- secret_reference.go
- seed_projection.go
- setting_def.go
- setting_def_validation.go
- setting_defs_auth.go
- setting_defs_content.go
- setting_defs_misc.go
- setting_defs_server.go
- stamped_config.go
- timmy.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Package bootstrap provides the minimal, environment-only configuration a TMI worker component needs to start and reach the point where it can accept a job.
|
Package bootstrap provides the minimal, environment-only configuration a TMI worker component needs to start and reach the point where it can accept a job. |