Configuration Reference
Generated by make docs-sync from tools/gendocs/config.
Source of truth is the koanf:"..." struct tags + doc comments in
internal/config/*.go. Do not edit by hand — make docs-sync re-runs
the generator and git diff will catch drift.
Defaults are seeded by internal/config/config.go::defaultConfig() (and
the per-section applyXxxDefaults helpers). The reference below names
the type + the doc comment; the runtime default is in the seed.
Top-level structure
The root config is a single YAML document. The Config struct
(internal/config/config.go) lists every top-level section; each
section has its own struct in the same package.
Sections covered below:
serverloggingstoragehealthnatsagentsecurityidentitysecretseventsclustergitopswebhookmetricstracingprofilingblueprints
server
ServerConfig configures the HTTP and gRPC listeners.
| Key | Type | Description |
|---|---|---|
server.cors | CORSConfig | |
server.grpcport | int | |
server.host | string | |
server.httpport | int | |
server.tls | TLSConfig |
server.cors
CORSConfig configures the HTTP CORS middleware. CORS lives outside the auth chain so OPTIONS preflight returns 204 before rate-limit is consulted (PROJECT-DETAILS §4.4 acceptance criterion).
| Key | Type | Description |
|---|---|---|
server.cors.allowedheaders | []string | |
server.cors.allowedmethods | []string | |
server.cors.allowedorigins | []string | |
server.cors.enabled | bool |
server.tls
TLSConfig configures TLS termination for the listeners. Three modes: - Enabled=false: plain TCP. Insecure; only appropriate for dev loopback or behind a TLS-terminating ingress. - Enabled=true + CertFile/KeyFile both set: file-sourced cert/key. Conventional production setup with externally-provisioned PKI. - Enabled=true + CertFile/KeyFile both empty: identity-provider sourced. The embedded provider (Epic 09) issues a server SVID for spiffe://<td>/server/control-plane and refreshes it on each signing-CA rotation. Requires Identity.Enabled=true. Both modes enforce TLS 1.3 minimum + mTLS request semantics (ClientAuth = tls.VerifyClientCertIfGiven so API-key clients can still authenticate over TLS without a peer cert).
| Key | Type | Description |
|---|---|---|
server.tls.certfile | string | |
server.tls.enabled | bool | |
server.tls.keyfile | string |
logging
LoggingConfig configures the structured logger.
| Key | Type | Description |
|---|---|---|
logging.format | string | |
logging.level | string |
storage
StorageConfig configures the persistence backend.
| Key | Type | Description |
|---|---|---|
storage.driver | string | |
storage.dsn | string |
health
HealthConfig configures the kscore-server liveness/readiness surface. PROJECT-DETAILS §4.4: /health/ready respects a startup grace period to avoid false-not-ready signals during boot.
| Key | Type | Description |
|---|---|---|
health.checktimeout | time.Duration | |
health.startupgraceperiod | time.Duration |
nats
NATSConfig configures the v1.0 NATS transport. Endpoints (Task 2/3) supersedes URLs for richer config (priority, weight, tags). Both are accepted for ergonomics: simple deployments list URLs; HA deployments use Endpoints. They are mutually exclusive in external mode — populating both is rejected at Validate.
| Key | Type | Description |
|---|---|---|
nats.bootstrap | BootstrapConfig | |
nats.circuitbreaker | CircuitBreakerConfig | |
nats.clustername | string | |
nats.credential | string | |
nats.dedup | DedupConfig | |
nats.embedded | EmbeddedNATSConfig | |
nats.endpoints | []EndpointConfig | |
nats.jetstream | JetStreamConfig | |
nats.maxreconnectdelay | time.Duration | MaxReconnectDelay caps the exponential-backoff curve. 30s default — fast enough that a recovered CP draws agents back inside half a minute, slow enough that a flapping CP doesn’t get hammered once base*2^N reaches the cap. |
nats.maxreconnects | int | |
nats.mode | NATSMode | |
nats.reconnectjitter | float64 | ReconnectJitter is the ±factor symmetric jitter applied to each backoff delay. 0.2 default (20%) — fleet-scale reconnect-storm protection against 1000-agent lock-step reconnects (Epic 06 risk). AWS decorrelated jitter is the gold standard at >500-agent scale; tracked in ROADMAP. |
nats.reconnectwait | time.Duration | ReconnectWait is the base of the exponential-backoff curve. Epic 06 task 10 replaced the constant-delay nats.ReconnectWait with nats.CustomReconnectDelay using reconnectDelay(attempt, ReconnectWait, MaxReconnectDelay, ReconnectJitter) — so this field still names the floor, but actual delays grow per attempt up to MaxReconnectDelay with ±ReconnectJitter spread. |
nats.token | string | |
nats.urls | []string |
nats.bootstrap
BootstrapConfig governs the v1.0 server-side bootstrap registration handler (Epic 05 task 9). Defaults to disabled — operators opt in by setting Enabled=true and populating PSKs. A “default on with no PSKs” stance would be a footgun (looks permissive, actually rejects everything); explicit opt-in surfaces the dependency. PSKs are config-listed in v1.0; consumed PSKs are tracked in- memory so a server restart wipes the consumption record. Operators are expected to rotate PSKs (issue fresh ones per agent, don’t reuse). v1.x will persist consumption in the state DB and add an API endpoint to issue PSKs.
| Key | Type | Description |
|---|---|---|
nats.bootstrap.enabled | bool | |
nats.bootstrap.psks | []BootstrapPSK |
nats.bootstrap.psks
BootstrapPSK describes one operator-issued bootstrap credential. Secret is hex-encoded so config files stay copy-pasteable; the validator hex-decodes once at construction. ExpiresAt is the hard cutoff after which Validate rejects the PSK.
| Key | Type | Description |
|---|---|---|
nats.bootstrap.psks.agentid | string | |
nats.bootstrap.psks.expiresat | time.Time | |
nats.bootstrap.psks.secret | string |
nats.circuitbreaker
CircuitBreakerConfig configures the per-endpoint circuit breaker (Epic 05 task 7). State machine: closed → open → half-open → closed. PROJECT-DETAILS §4.2. v1.0 wiring: each endpoint owns one breaker. ConnectionManager drives transitions via the disconnect/reconnect callbacks already in place from task 2; Health degrades to error when every endpoint is OPEN. Active dial-time eviction (skipping OPEN endpoints when nats.go picks the next reconnect target) is deferred to v0.x — it requires replacing nats.go’s native multi-URL failover with a per-endpoint dial loop, which is a substantial refactor for marginal v1.0 benefit.
| Key | Type | Description |
|---|---|---|
nats.circuitbreaker.enabled | bool | |
nats.circuitbreaker.failurethreshold | int | |
nats.circuitbreaker.halfopenmaxattempts | int | |
nats.circuitbreaker.openduration | time.Duration | |
nats.circuitbreaker.successthreshold | int |
nats.dedup
DedupConfig configures producer-side message dedup (Epic 05 task 6). Defaults follow PROJECT-DETAILS §4.2: 5m window, large but bounded entry cap. Per-subject overrides let operators shrink the window for low-RTT subjects (heartbeats) or enlarge it for high- retry ones.
| Key | Type | Description |
|---|---|---|
nats.dedup.cleanupinterval | time.Duration | |
nats.dedup.enabled | bool | |
nats.dedup.maxentries | int | |
nats.dedup.persubjectoverrides | []SubjectOverride | |
nats.dedup.windowduration | time.Duration |
nats.dedup.persubjectoverrides
SubjectOverride applies a non-default WindowDuration to subjects matching Prefix (longest prefix wins; equality counts as a prefix match).
| Key | Type | Description |
|---|---|---|
nats.dedup.persubjectoverrides.prefix | string | |
nats.dedup.persubjectoverrides.windowduration | time.Duration |
nats.embedded
EmbeddedNATSConfig configures the in-process nats-server/v2 instance started when Mode == NATSModeEmbedded. JetStream is gated by JetStreamConfig.Enabled — there is no separate embedded toggle so the two flags can never disagree.
| Key | Type | Description |
|---|---|---|
nats.embedded.host | string | |
nats.embedded.maxconnections | int | |
nats.embedded.maxmemory | int64 | |
nats.embedded.port | int |
nats.endpoints
EndpointConfig is the structured form of a single NATS endpoint consumed by ConnectionManager. Priority orders the connect-attempt list (higher first). Weight is reserved for post-v1.0 load distribution and ignored today. Tags are operator labels passed through to EndpointSnapshot for observability.
| Key | Type | Description |
|---|---|---|
nats.endpoints.priority | int | |
nats.endpoints.tags | []string | |
nats.endpoints.url | string | |
nats.endpoints.weight | int |
nats.jetstream
JetStreamConfig governs JetStream enablement and per-stream defaults. v1.0 streams (commands + events) inherit StreamMaxAge, StreamMaxBytes, StreamMaxMsgs, and StreamReplicas; per-stream overrides are reserved for v1.x. Enabled is the single switch — when true, the embedded server (if running) starts with JetStream and Manager auto-creates the commands/events streams. When false, no streams are touched and the embedded server runs without JetStream.
| Key | Type | Description |
|---|---|---|
nats.jetstream.enabled | bool | |
nats.jetstream.maxstorage | int64 | |
nats.jetstream.storedir | string | |
nats.jetstream.streammaxage | time.Duration | |
nats.jetstream.streammaxbytes | int64 | |
nats.jetstream.streammaxmsgs | int64 | |
nats.jetstream.streamreplicas | int |
agent
AgentConfig governs the kscore-agent daemon (Epic 06). Loaded from the same config file as the server’s sections (per PROJECT-DETAILS §4.6, agent’s config file lists agent.* alongside nats.* and security.*); the kscore-server binary ignores this section. AgentID is generally set by Task 6’s bootstrap engine and persisted to disk; for v1.0’s pre-bootstrap world it must be supplied via config. ClusterName is shared with the rest of the system via NATSConfig.ClusterName — the agent reads it from there.
| Key | Type | Description |
|---|---|---|
agent.bootstrappsk | string | BootstrapPSK is the hex-encoded pre-shared key this agent presents at boot to register with the control plane. When non-empty, the agent publishes a bootstrap.register envelope at Start before the heartbeat loop begins. The server-side PSK list lives at nats.bootstrap.psks. Leave empty for v0.1- style deployments where the operator pre-provisions agent rows out-of-band. |
agent.commandtimeout | time.Duration | |
agent.heartbeatinterval | time.Duration | |
agent.id | string | |
agent.labels | map[string]string | |
agent.metadatainterval | time.Duration |
security
SecurityConfig governs the agent-side SecurityEnforcer (Epic 06 task 4/5) and the control-plane-side command signer. Both binaries read the same security.* section so the HMAC secret, default policy, and command/principal rules stay in lockstep. HMACSecret is hex-encoded so config files stay copy-pasteable; the binary decodes once at startup. Empty HMACSecret disables the HMAC check (escape hatch for fresh agents pre-bootstrap; v1.x can require non-empty when bootstrap-derived keys land). PROJECT-DETAILS §4.6 lists security.{authorization, command_filter} as the agent’s top-level security keys; v1.0 flattens them into the fields below.
| Key | Type | Description |
|---|---|---|
security.commandallowglobs | []string | |
security.commandallowregexes | []string | |
security.commanddenyglobs | []string | |
security.commanddenyregexes | []string | |
security.defaultpolicy | string | |
security.envvarallowlist | []string | |
security.hmacsecret | string | |
security.maxargsbytes | int | |
security.principalallowlist | []string |
identity
IdentityConfig drives the embedded identity provider boot in kscore-server (Epic 09 task 12). All fields have v0.1 defaults that work for a single-server dev / external-tester install; production multi-CP deployments override TrustDomain + StoragePath.
| Key | Type | Description |
|---|---|---|
identity.enabled | bool | Enabled toggles the embedded identity provider on/off. v0.1 defaults to true so a fresh kscore-server boots with a working CA + join-token endpoint. Operators who don’t want identity (e.g. running their own SPIRE) set this false. |
identity.encryption_key | string | EncryptionKey turns on encryption-at-rest for the CA private keys under StoragePath. Empty (the default) keeps the plaintext FileCAStorage surface — fully backward-compatible. When set, it is a scheme-prefixed master-key source resolved by masterkey.Resolve — env:VAR_NAME, file:/path/to/keyfile, or inline:<hex|base64> — and the provider uses EncryptedFileCAStorage. Migrate an existing plaintext directory with kscore-identity ca encrypt before enabling this. |
identity.join_tokens_in_memory | bool | JoinTokensInMemory forces the provider to use an in-memory JoinTokenStore even when a state.Store is otherwise available. Defaults false — the v0.1 production path wires the state-backed store so tokens survive restarts. Useful for development / CI when the test harness doesn’t want join-token persistence to bleed across runs. |
identity.key_type | string | KeyType selects the asymmetric algorithm for the root + signing CAs. Defaults to “ecdsa-p256” (per identity.KeyTypeECDSAP256). Other valid values: “ecdsa-p384”, “rsa-2048”, “rsa-4096”. |
identity.storage_path | string | StoragePath is the directory that holds the CA cert + key files. Defaults to “./.kscore/identity” — operators are expected to override in production (e.g. “/var/lib/kscore/identity”). |
identity.trust_domain | string | TrustDomain is the SPIFFE trust domain. Defaults to identity.DefaultTrustDomain (“kscore.local”). Validation happens inside identity.NewSPIFFEID at construction time. |
secrets
SecretsConfig drives the Epic 10 secrets boot in kscore-server. The shape mirrors PROJECT-DETAILS §4.11’s YAML example exactly: secrets: enabled: true default_backend: file backends: - name: file type: encrypted_file file: { path: /var/lib/keystone/secrets.bin, master_key: env:KSCORE_SECRETS_MASTER } - name: vault type: vault vault: { address: https://vault.internal
, auth_method: approle, … } routing: - prefix: secret/ backend: vault - prefix: kv/ backend: file cache: { enabled: true, max_entries: 10000, default_ttl: 5m } lease: { poll_interval: 30s, jitter: 0.1, default_strategy: lazy } Enabled defaults to false — operators opt in. A stock kscore-server without secrets.enabled: true runs the rest of the stack unchanged; the gRPC SecretsService returns codes.Unavailable + REST returns 503 when called.
| Key | Type | Description |
|---|---|---|
secrets.audit | SecretsAuditConfig | |
secrets.backends | []SecretsBackendConfig | |
secrets.cache | SecretsCacheConfig | |
secrets.default_backend | string | |
secrets.enabled | bool | |
secrets.lease | SecretsLeaseConfig | |
secrets.routing | []SecretsRouteConfig |
secrets.audit
SecretsAuditConfig drives the audit-emission infrastructure (Epic 10 task 11). Wires LogAuditor (always) + BufferedAuditor (when BufferSize > 0) + an optional SamplingAuditor wrapping the log auditor when SamplingFraction < 1.0. Epic 12’s AuditStore will plug in here once it ships.
| Key | Type | Description |
|---|---|---|
secrets.audit.buffer_size | int | BufferSize is the in-memory ring-buffer capacity (the PROJECT-DETAILS §4.12 “Auditor circular buffer”). 0 disables the buffer; positive enables. Default 10000. |
secrets.audit.sampling_fraction | float64 | SamplingFraction is the probability each successful event reaches the log auditor (in [0, 1]). 1.0 = no sampling (default). Failures + cap-refusals always emit regardless. |
secrets.backends
SecretsBackendConfig is one operator-declared backend instance. Exactly one of [File] / [Vault] must be set and must match [Type].
| Key | Type | Description |
|---|---|---|
secrets.backends.file | *SecretsFileBackendConfig | |
secrets.backends.name | string | |
secrets.backends.type | string | |
secrets.backends.vault | *SecretsVaultBackendConfig |
secrets.backends.file
SecretsFileBackendConfig drives the encrypted-file backend (internal/secrets/file). MasterKey is the scheme-prefixed value resolved by masterkey.Resolve — env:VAR_NAME, file:/path/to/keyfile, or inline:<hex\|base64>.
| Key | Type | Description |
|---|---|---|
secrets.backends.file.master_key | string | |
secrets.backends.file.path | string |
secrets.backends.vault
SecretsVaultBackendConfig drives the Vault backend (internal/secrets/vault).
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.address | string | |
secrets.backends.vault.approle | *SecretsVaultAppRoleConfig | |
secrets.backends.vault.auth_method | string | |
secrets.backends.vault.kubernetes | *SecretsVaultK8sConfig | |
secrets.backends.vault.ldap | *SecretsVaultLDAPConfig | |
secrets.backends.vault.mounts | []SecretsVaultMountConfig | |
secrets.backends.vault.namespace | string | |
secrets.backends.vault.timeout | time.Duration | |
secrets.backends.vault.tls | SecretsVaultTLSConfig | |
secrets.backends.vault.token | string |
secrets.backends.vault.approle
SecretsVaultAppRoleConfig — RoleID + SecretID. SecretID may itself be a wrapping token (per Vault’s response-wrapping flow).
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.approle.mount | string | |
secrets.backends.vault.approle.role_id | string | |
secrets.backends.vault.approle.secret_id | string | |
secrets.backends.vault.approle.secret_id_is_wrapping_token | bool |
secrets.backends.vault.kubernetes
SecretsVaultK8sConfig — Kubernetes ServiceAccount-based auth.
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.kubernetes.mount | string | |
secrets.backends.vault.kubernetes.role | string | |
secrets.backends.vault.kubernetes.token_path | string |
secrets.backends.vault.ldap
SecretsVaultLDAPConfig — LDAP username/password auth.
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.ldap.mount | string | |
secrets.backends.vault.ldap.password | string | |
secrets.backends.vault.ldap.username | string |
secrets.backends.vault.mounts
SecretsVaultMountConfig declares the KV engine version for one Vault mount path — see internal/secrets/vault.MountConfig.
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.mounts.kv_version | int | |
secrets.backends.vault.mounts.path | string |
secrets.backends.vault.tls
SecretsVaultTLSConfig configures HTTPS to Vault.
| Key | Type | Description |
|---|---|---|
secrets.backends.vault.tls.ca_cert | string | |
secrets.backends.vault.tls.client_cert | string | |
secrets.backends.vault.tls.client_key | string | |
secrets.backends.vault.tls.insecure | bool | |
secrets.backends.vault.tls.server_name | string |
secrets.cache
SecretsCacheConfig drives the [secrets.SecretCache]. Enabled defaults to true when secrets are enabled — operators rarely want to disable caching, but explicit enabled: false falls back to the no-op cache.
| Key | Type | Description |
|---|---|---|
secrets.cache.default_ttl | time.Duration | |
secrets.cache.enabled | bool | |
secrets.cache.max_entries | int | |
secrets.cache.sweep_interval | time.Duration |
secrets.lease
SecretsLeaseConfig drives the [secrets.LeaseManager].
| Key | Type | Description |
|---|---|---|
secrets.lease.default_strategy | string | |
secrets.lease.jitter | float64 | |
secrets.lease.poll_interval | time.Duration | |
secrets.lease.renew_timeout | time.Duration |
secrets.routing
SecretsRouteConfig is one row in the broker’s routing table.
| Key | Type | Description |
|---|---|---|
secrets.routing.backend | string | |
secrets.routing.prefix | string |
events
EventsConfig drives the Epic 11 events boot in kscore-server. events: enabled: true publisher: enabled: true buffer_size: 1000 flush_timeout: 100ms store_first: true subscriber: enabled: true dedup_size: 1000 Enabled defaults to true — events are foundational v1.0 infrastructure and most deployments running NATS expect them on. Operators explicitly disable via events.enabled: false. When disabled, the gRPC EventService returns codes.Unavailable + REST returns 503; the JetStreamPublisher / Subscriber are skipped at boot. Validation requires NATS JetStream to be enabled when events is enabled — the publisher / subscriber both need a JetStream context.
| Key | Type | Description |
|---|---|---|
events.enabled | bool | |
events.publisher | EventsPublisherConfig | |
events.retention | EventsRetentionConfig | |
events.subscriber | EventsSubscriberConfig |
events.publisher
EventsPublisherConfig drives the JetStreamPublisher (Epic 11 task 3). - Enabled: gates the publisher specifically. Defaults true when parent Events is enabled. Setting false runs Events with the NoopPublisher (subscriber + REST list/get still work). - BufferSize / FlushTimeout: async-pipeline knobs from task 3. Defaults match the package’s Default* constants. - StoreFirst: when true (default), publisher persists to the EventStore before NATS publish; failures abort. When false, publishes go straight to NATS (no audit trail). Most deployments leave this on.
| Key | Type | Description |
|---|---|---|
events.publisher.buffer_size | int | |
events.publisher.enabled | bool | |
events.publisher.flush_timeout | time.Duration | |
events.publisher.store_first | bool |
events.retention
EventsRetentionConfig drives the Epic 11 task 8 retention enforcer — the hourly scheduler that calls events.EventStore.ApplyRetention to bound store growth. Defaults to enabled with a built-in catch-all policy matching the §4.9 JetStream stream defaults (7 days / 1M events). Operators either keep the catch-all and add per-type overrides, or set events.retention.enabled: false to opt out entirely. events: retention: enabled: true interval: 1h jitter: 0.1 policies: - type: "" # catch-all max_age: 168h # 7 days max_count: 1000000 - type: agent.heartbeat max_age: 24h - type: job.output max_age: 720h # 30 days
| Key | Type | Description |
|---|---|---|
events.retention.enabled | bool | |
events.retention.interval | time.Duration | |
events.retention.jitter | float64 | |
events.retention.policies | []EventsRetentionPolicy |
events.retention.policies
EventsRetentionPolicy is one row in the retention table. Type empty is the catch-all rule (applies to every event type not matched by a more-specific policy). Each policy must have at least one limit (MaxAge > 0 OR MaxCount > 0) — zero-zero is rejected by validation as a no-op typo.
| Key | Type | Description |
|---|---|---|
events.retention.policies.max_age | time.Duration | |
events.retention.policies.max_count | int | |
events.retention.policies.type | string |
events.subscriber
EventsSubscriberConfig drives the JetStreamSubscriber (Epic 11 task 4). - Enabled: gates the subscriber specifically. Defaults true. Setting false makes SubscribeEvents return Unavailable. - DedupSize: bounded ID dedup ring (task 4). Defaults 1000.
| Key | Type | Description |
|---|---|---|
events.subscriber.dedup_size | int | |
events.subscriber.enabled | bool |
cluster
ClusterConfig drives the Epic 13 clustering/HA boot in kscore-server (PROJECT-DETAILS §4.15). cluster: enabled: false node: name: "" # empty → derived from etcd.name advertise_addr: "" # host:port peers dial (server↔server) etcd: mode: embedded # embedded | external name: kscore-1 data_dir: ./data/etcd client_urls: ["http://127.0.0.1:2379 "] peer_urls: ["http://127.0.0.1:2380 "] endpoints: [] # external mode only lease_ttl_seconds: 15 dial_timeout: 5s auto_sync_interval: 5m membership: heartbeat_interval: 5s key_prefix: /kscore/cluster election: session_ttl_seconds: 3 recampaign_delay: 1s shard: virtual_nodes: 150 rebalance_cooldown: 5s health: check_interval: 5s failure_threshold: 3 latency_window: 100 failover: cooldown: 10s agent_batch: 100 job_batch: 50 recovery: connect_timeout: 5s connect_retries: 3 fencing: mode: read_only # strict | read_only | graceful coordination: listen_addr: "" # host:port mTLS listener bind; empty → none heartbeat_interval: 5s heartbeat_timeout: 2s failure_threshold: 3 retry_max: 4 retry_base_delay: 100ms retry_max_delay: 2s shutdown: timeout: 30s Enabled defaults to false: the single-node path stays the default and clustering is strictly opt-in. When disabled, no etcd is started and the ClusterService/CoordinationService are not registered (later Epic 13 tasks). The wiring that translates this into a running internal/cluster.EtcdClient lands with a later Epic 13 task; Task 1 ships the config surface + validation.
| Key | Type | Description |
|---|---|---|
cluster.coordination | ClusterCoordinationConfig | |
cluster.election | ClusterElectionConfig | |
cluster.enabled | bool | |
cluster.etcd | ClusterEtcdConfig | |
cluster.failover | ClusterFailoverConfig | |
cluster.fencing | ClusterFencingConfig | |
cluster.health | ClusterHealthConfig | |
cluster.membership | ClusterMembershipConfig | |
cluster.node | ClusterNodeConfig | |
cluster.recovery | ClusterRecoveryConfig | |
cluster.shard | ClusterShardConfig | |
cluster.shutdown | ClusterShutdownConfig |
cluster.coordination
ClusterCoordinationConfig is the operator-facing server↔server CoordinationService/Client config (Epic 13 tasks 12/13). The runtime equivalents are the dedicated mTLS coordination listener + controlplane.CoordinationClientConfig; boot wiring maps onto them.
| Key | Type | Description |
|---|---|---|
cluster.coordination.failure_threshold | int | FailureThreshold is the consecutive heartbeat failures after which a peer is marked unreachable. |
cluster.coordination.heartbeat_interval | time.Duration | HeartbeatInterval is how often each peer is NodeHeartbeat’d. |
cluster.coordination.heartbeat_timeout | time.Duration | HeartbeatTimeout bounds each heartbeat RPC. |
cluster.coordination.listen_addr | string | ListenAddr is the host:port the dedicated mTLS coordination gRPC listener binds (the server↔server channel, separate from the agent/operator surface). Distinct from node.advertise_addr (what peers dial): ListenAddr is the local bind (e.g. “0.0.0.0:9443”) while advertise_addr is the routable address recorded in the member record. Empty ⇒ no coordination listener is started (this node serves no server↔server channel); when set it must be a valid host:port. |
cluster.coordination.retry_base_delay | time.Duration | RetryBaseDelay / RetryMaxDelay bound the exponential backoff. |
cluster.coordination.retry_max | int | RetryMax is the max attempts per coordination RPC. |
cluster.coordination.retry_max_delay | time.Duration |
cluster.election
ClusterElectionConfig is the operator-facing leader-election config (Epic 13 task 3). The runtime equivalent is cluster.ElectionConfig; boot wiring (later task) maps onto it. The election session lease is separate from the membership lease: it is tuned to the “first/new leader < 3s” failover SLO, not the membership 3×-heartbeat anti-flap rule. SLO verification + any tuning is Epic 13 task 18.
| Key | Type | Description |
|---|---|---|
cluster.election.recampaign_delay | time.Duration | ReCampaignDelay is how long TransferLeadership waits after resigning before re-campaigning, so a peer reliably takes over rather than the resigner immediately winning again. |
cluster.election.session_ttl_seconds | int | SessionTTLSeconds is the etcd concurrency-session TTL; a dead leader’s lock expires within ~this long, so it bounds failover time. §4.15 SLO target default 3s. |
cluster.etcd
ClusterEtcdConfig is the operator-facing etcd backend config. internal/cluster owns the runtime equivalent (cluster.EtcdConfig); boot wiring (later task) maps this onto it.
| Key | Type | Description |
|---|---|---|
cluster.etcd.auto_sync_interval | time.Duration | |
cluster.etcd.client_urls | []string | |
cluster.etcd.data_dir | string | |
cluster.etcd.dial_timeout | time.Duration | |
cluster.etcd.endpoints | []string | |
cluster.etcd.lease_ttl_seconds | int | |
cluster.etcd.mode | string | |
cluster.etcd.name | string | |
cluster.etcd.peer_urls | []string | |
cluster.etcd.tls | TLSConfig |
cluster.failover
ClusterFailoverConfig is the operator-facing failover config (Epic 13 task 8). The runtime equivalent is cluster.FailoverManagerConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.failover.agent_batch | int | AgentBatch is the agent-reassignment batch size. §4.15 default 100. |
cluster.failover.cooldown | time.Duration | Cooldown is the minimum spacing between failover episodes (rapid flapping members coalesce). §4.15 default 10s — distinct from the shard rebalance cooldown (5s). |
cluster.failover.job_batch | int | JobBatch is the job-reassignment batch size. §4.15 default 50. |
cluster.fencing
ClusterFencingConfig is the operator-facing split-brain fencing config (Epic 13 task 11). The runtime equivalent is cluster.FencingManagerConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.fencing.mode | string | Mode is how hard a fenced (minority / stale-epoch) node blocks operations: “strict” (block all), “read_only” (allow reads, block writes) or “graceful” (finish in-flight, block new). §4.15 acceptance (“minority blocks writes, reads continue”) = read_only, the default. |
cluster.health
ClusterHealthConfig is the operator-facing health-monitor config (Epic 13 task 7). The runtime equivalent is cluster.HealthMonitorConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.health.check_interval | time.Duration | CheckInterval is how often the registered checkers run. |
cluster.health.failure_threshold | int | FailureThreshold is the consecutive-failure count at which a checker is considered failing. §4.15 default 3. |
cluster.health.latency_window | int | LatencyWindow is how many recent check durations are kept per checker for P50/P99. |
cluster.membership
ClusterMembershipConfig is the operator-facing membership config (Epic 13 task 2). The runtime equivalent is cluster.MembershipConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.membership.heartbeat_interval | time.Duration | HeartbeatInterval is how often a member refreshes its observable liveness. §4.15 default 5s. |
cluster.membership.key_prefix | string | KeyPrefix roots the etcd keyspace for this cluster’s member records (and, later, shard/leader keys). |
cluster.node
ClusterNodeConfig identifies this server within the cluster (Epic 13 boot wiring). Distinct from cluster.etcd.name (the etcd member name): Name is this node’s stable Keystone member identity, recorded in the membership record and reused across restarts so RecoveryManager can reclaim this node’s shards.
| Key | Type | Description |
|---|---|---|
cluster.node.advertise_addr | string | AdvertiseAddr is the host:port peers use to reach this node’s server↔server coordination channel. It is recorded in the member record now; the CoordinationClient that dials it is a later Epic 13 task. Empty is allowed (no peer can dial this node yet); when set it must be a valid host:port. |
cluster.node.name | string | Name is this member’s stable cluster identity. Empty → derived from cluster.etcd.name at boot. |
cluster.recovery
ClusterRecoveryConfig is the operator-facing restart-recovery config (Epic 13 task 10). The runtime equivalent is cluster.RecoveryManagerConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.recovery.connect_retries | int | ConnectRetries is how many times CONNECTING retries the probe before recovery fails. |
cluster.recovery.connect_timeout | time.Duration | ConnectTimeout bounds each etcd reachability probe during the CONNECTING phase. |
cluster.shard
ClusterShardConfig is the operator-facing consistent-hash + rebalance config (Epic 13 tasks 4/6). The runtime equivalents are the cluster.HashRing vnode count and the ShardManager cooldown; boot wiring (later task) maps onto them.
| Key | Type | Description |
|---|---|---|
cluster.shard.rebalance_cooldown | time.Duration | RebalanceCooldown is the minimum spacing between topology-driven rebalances; rapid member join/leave flaps coalesce into one rebalance after the window. §4.15 default 5s. |
cluster.shard.virtual_nodes | int | VirtualNodes is the number of ring points per member. More vnodes = smoother key distribution + less rebalancing churn, at a higher ring-rebuild cost. §4.15 default 150. |
cluster.shutdown
ClusterShutdownConfig is the operator-facing graceful-shutdown config (Epic 13 task 14). The runtime equivalent is cluster.GracefulShutdownConfig; boot wiring (later task) maps onto it.
| Key | Type | Description |
|---|---|---|
cluster.shutdown.timeout | time.Duration | Timeout bounds the DEREGISTERING phase (in-flight drain + member-key removal). §4.15 default 30s. |
gitops
GitOpsConfig drives the Epic 16 GitOps integration. Task 1 ships the inbound webhook receiver sub-config only; verification, rollback and outbound webhooks extend this struct in later tasks. gitops: webhook: enabled: false addr: “:8081” path: “/webhooks” max_body_bytes: 1048576 sources: github: method: hmac # none|hmac|bearer secret: ${KSCORE_GITOPS_WEBHOOK_SOURCES_GITHUB_SECRET} gitlab: method: bearer secret: … Enabled defaults to false: the receiver opens a network port and (from task 4) re-emits onto the event bus, so it is opt-in rather than on-by-default infrastructure. Operators turn it on with gitops.webhook.enabled: true.
| Key | Type | Description |
|---|---|---|
gitops.webhook | GitOpsWebhookConfig |
gitops.webhook
GitOpsWebhookConfig is the inbound webhook receiver block. Addr defaults to “:8081” — distinct from the main REST API on server.httpport (:8080); colliding the two would wedge boot.
| Key | Type | Description |
|---|---|---|
gitops.webhook.addr | string | |
gitops.webhook.enabled | bool | |
gitops.webhook.max_body_bytes | int64 | |
gitops.webhook.path | string | |
gitops.webhook.sources | map[string]GitOpsSourceAuthConfig | Sources maps a provider (github|gitlab|argocd|flux) to its inbound authentication. A provider absent from the map is authenticated open ([webhook.NoneAuthenticator]) and flagged by [Config.ProductionWarnings]. v1.0: one secret per source; rotation requires a restart (PROJECT-DETAILS §4.13). |
webhook
WebhookConfig drives the outbound webhooks subsystem (Epic 16 tasks 11..18 / PROJECT-DETAILS §4.14). The top-level webhook key is owned by outbound for v1.0; inbound non-GitOps webhooks are v1.x (the GitOps inbound block lives at gitops.webhook). webhook: outbound: enabled: false max_retries: 3 retry_backoff: 1s timeout: 10s max_payload_size: 1048576 delivery_retention: 168h max_concurrent_deliveries: 32 refresh_interval: 30s Enabled defaults to false (opt-in; the same posture as gitops.webhook.enabled).
| Key | Type | Description |
|---|---|---|
webhook.outbound | WebhookOutboundConfig |
webhook.outbound
WebhookOutboundConfig is the outbound-webhooks block. - MaxRetries / RetryBackoff drive the task-14 RetryQueue. - Timeout caps each task-13 Dispatcher.Deliver call. - MaxPayloadSize bounds the JSON event payload — over-size events are recorded as one synthetic failed delivery. - DeliveryRetention is the task-9 / task-11 retention enforcer horizon (auto-invocation = post-v1.0 dot release per §4.14). - MaxConcurrentDeliveries / RefreshInterval drive the task-12 Manager (fan-out cap and subscription-cache reload cadence).
| Key | Type | Description |
|---|---|---|
webhook.outbound.delivery_retention | time.Duration | |
webhook.outbound.enabled | bool | |
webhook.outbound.max_concurrent_deliveries | int | |
webhook.outbound.max_payload_size | int64 | |
webhook.outbound.max_retries | int | |
webhook.outbound.refresh_interval | time.Duration | |
webhook.outbound.retry_backoff | time.Duration | |
webhook.outbound.timeout | time.Duration |
metrics
MetricsConfig configures the Prometheus exposition surface. PROJECT-DETAILS §4.16: /metrics is on the main HTTP server, unauthenticated, no rate-limit — same posture as /health/*.
| Key | Type | Description |
|---|---|---|
metrics.enabled | bool | Enabled toggles registration of the /metrics handler. Default true (epic line 14: “default true”); set false in deployments where the operator routes scrape traffic differently. |
metrics.path | string | Path is the URL path the handler mounts on. Default “/metrics”. Operators who reverse-proxy under a non-standard prefix override this so the inner mount matches the proxy’s rewrite. |
tracing
TracingConfig configures the Epic 17 task 4 OTel trace pipeline. Disabled by default — 100% sampling adds 5-10% latency at scale (PROJECT-DETAILS §4.16 gotcha), so operators opt in explicitly.
| Key | Type | Description |
|---|---|---|
tracing.batchsize | int | BatchSize is the sdktrace batch processor MaxExportBatchSize. Default 512. |
tracing.enabled | bool | Enabled toggles the whole pipeline. Default false. When false, internal/tracing.New returns a noop TracerProvider. |
tracing.endpoint | string | Endpoint is the collector URL (or addr:port for OTLP gRPC). Required when Exporter is not stdout; ignored for stdout. |
tracing.exporter | string | Exporter selects the span sink. One of stdout / otlp_grpc / otlp_http / zipkin. Default stdout — operator-visible without needing a collector. |
tracing.flushinterval | time.Duration | FlushInterval is the sdktrace batch processor BatchTimeout — the longest a span will wait before being exported. Default 5s. |
tracing.insecure | bool | Insecure disables TLS for OTLP exporters. Stdout/Zipkin ignore this. Default false (TLS required) so production-wired collectors don’t accidentally negotiate plaintext. |
tracing.queuesize | int | QueueSize is the sdktrace batch processor MaxQueueSize. Default 2048. Must be >= BatchSize. |
tracing.ratelimitpersecond | int | RateLimitPerSecond bounds the rate_limiting sampler’s accepts. Default 100. Above-rate spans return Drop. Honors parent decisions per OTel convention. |
tracing.sampler | string | Sampler selects the sampling strategy. Default probabilistic at SampleRate. Adaptive is intentionally NOT supported in v1.0 — see ROADMAP entry for the v2.x+ adaptive-sampling work. |
tracing.samplerate | float64 | SampleRate is the [0,1] sampling fraction for the probabilistic sampler and the probabilistic fallback inside parent_based. Default 0.1 (PROJECT-DETAILS §4.16 line 1126). |
tracing.servicename | string | ServiceName is the resource attribute service.name. Default “kscore-server”; per-binary wiring may override (e.g. “kscore-agent” in the agent runtime). |
profiling
ProfilingConfig configures the opt-in pprof endpoint. PROJECT-DETAILS §4.16 — “Default off; opt-in via profiling.enabled=true. Listen port default 6060.” Default Host is 127.0.0.1: pprof can leak heap state and bottleneck under CPU profile load, so operators who flip Enabled must explicitly widen the bind to reach the LAN.
| Key | Type | Description |
|---|---|---|
profiling.blockprofilerate | int | BlockProfileRate is passed to runtime.SetBlockProfileRate on Start. Default 0 means “no block profile” (the runtime default). Non-zero N samples blocking events whose duration exceeds N nanoseconds. Same overhead caveat as MutexProfileFraction. |
profiling.enabled | bool | Enabled toggles the whole pprof listener. Default false. When false the server is never constructed. |
profiling.host | string | Host is the bind address. Default “127.0.0.1” (localhost-only). Operators who want LAN reachability set this to “0.0.0.0” and accept the security responsibility — pprof has no auth and is not appropriate for public exposure. |
profiling.mutexprofilefraction | int | MutexProfileFraction is passed to runtime.SetMutexProfileFraction on Start. Default 0 means “no mutex profile” (the runtime default). Non-zero N records 1/N mutex contention events. Has non-trivial overhead; leave at 0 unless actively debugging contention. |
profiling.port | int | Port is the bind port. Default 6060 (conventional Go pprof). Range [1, 65535]; zero is rejected when Enabled. |
profiling.shutdowntimeout | time.Duration | ShutdownTimeout caps the graceful-shutdown wait when the server is asked to stop. Default 5 seconds. |
blueprints
BlueprintsConfig wires the optional v1.0 BlueprintService. Epic 19 task 2b — until the gate-v1.0 ROADMAP item “Remote / distributed blueprint apply wiring” lands, the server-side BlueprintService applies blueprints against the server’s local stdlib StateRunner (the same convergence path kscore-blueprint uses today). CatalogPath enables ListBlueprints / GetBlueprint / ApplyBlueprint over gRPC; empty disables them (clients reach Unavailable).
| Key | Type | Description |
|---|---|---|
blueprints.catalogpath | string |