From 45ddcb75a3d46b4003731e16a3b063326bca2b99 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Thu, 14 May 2026 03:44:44 +0000 Subject: [PATCH] refactor(config): extract NotifierConfig to its own file (Phase 9, 1 of N) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 9 of the certctl architecture diligence remediation begins closing ARCH-M2: the 6 backend mega-files totaling > 13K LOC of change-risk hotspots. config.go is the largest (3,403 LOC pre-split) and the most frequently touched (env-var ingestion gets edited every release). The audit's "3.2K LOC / 11.5K total across 6 files" claim has drifted upward — live grep shows config.go alone is now 3,403 LOC and the top-6 hotspots total 13,267 LOC. The audit's framing is directionally correct; numbers updated in cowork/certctl-architecture- diligence-audit.html with this commit. This commit ships the FIRST of many splits (one per PR per the Phase 9 prompt's "Do not bundle" rule): Extract NotifierConfig (65 lines) → internal/config/notifiers.go Why NotifierConfig first ======================== - Cleanest possible cut: a single struct, no helper functions, no validation logic, no cross-references to Load() except via the Config{}.Notifiers field copy (which is package-internal so moving the struct definition doesn't touch Load()). - Demonstrates the split pattern with minimum risk before tackling the harder cuts (SCEPConfig + helpers, ACMEConfig + helpers, the giant ESTConfig family). - Public-surface byte-identical: every caller's `config.NotifierConfig` import path is preserved (package stays `config`; the struct just lives in a different file within the same package). Live audit (Phase 9 audit questions answered) ============================================== top-10 production .go files by LOC (find cmd internal -name '*.go' -not -name '*_test.go' | xargs wc -l | sort -rn | head -10): 3403 internal/config/config.go <-- this commit -68 2966 cmd/server/main.go 1965 internal/service/acme.go 1867 internal/mcp/tools.go 1577 internal/api/handler/auth_session_oidc.go 1489 cmd/agent/main.go 1356 internal/auth/oidc/service.go 1249 internal/scheduler/scheduler.go 1235 internal/connector/issuer/local/local.go 1224 internal/service/scep.go The audit's "3 others beyond config/main/acme" are: - internal/mcp/tools.go (1867 LOC) - internal/api/handler/auth_session_oidc.go (1577 LOC) - cmd/agent/main.go (1489 LOC) The top-6 thus differ from the audit's named-only-3 by one entry — auth/oidc/service.go (1356) edges out the audit's likely fourth pick. Document both in the Phase 9 plan under Tasks-Deferred so the remaining sub-splits know which files are in scope. config.go internals (45 distinct exported `type X struct` defs as of this commit's pre-state): Config, ServerConfig, ServerTLSConfig, DatabaseConfig, SchedulerConfig, LogConfig, AuthConfig, RateLimitConfig, CORSConfig, KeygenConfig, CAConfig, StepCAConfig, VaultConfig, DigiCertConfig, SectigoConfig, GoogleCASConfig, OpenSSLConfig, ESTConfig, ESTProfileConfig, SCEPConfig, SCEPProfileConfig, SCEPIntuneProfileConfig, NetworkScanConfig, VerificationConfig, ApprovalConfig, NamedAPIKey, SessionConfig, BreakglassConfig, EncryptionConfig, CloudDiscoveryConfig, AWSSecretsMgrDiscoveryConfig, AzureKVDiscoveryConfig, GCPSecretMgrDiscoveryConfig, NotifierConfig (THIS COMMIT), DigestConfig, HealthCheckConfig, ACMEConfig, ACMEServerConfig, ACMEServerDirectoryMeta, AWSACMPCAConfig, EntrustConfig, GlobalSignConfig, EJBCAConfig, OCSPResponderConfig Each is a natural future-split candidate. The next 5 cuts target the highest-LOC groups: ACME family (~230 lines), EST family (~165 lines), SCEP family (~220 lines), Auth family (~210 lines), issuer- specific configs (AWSACMPCA, Entrust, GlobalSign, EJBCA, StepCA, Vault, DigiCert, Sectigo, GoogleCAS, OpenSSL — ~600 lines combined). Public-surface invariant ======================== - Package name stays `config`. - Struct + all field names byte-identical. - Every caller's `config.NotifierConfig` import path preserved. - Verified via: go build ./internal/config/... → clean go test ./internal/config/... -count=1 → ok (0.67s) gofmt -l internal/config/ → clean staticcheck ./internal/config/... → clean LOC delta: config.go: 3403 → 3335 (-68 lines) notifiers.go: new, 86 lines (incl. 18-line Phase 9 doc-comment + BSL header + package decl) Phase 9 follow-on plan (each = separate commit, separate review) ================================================================ Next cuts from config.go (priority order): 2 of N. ACMEConfig + ACMEServerConfig + ACMEServerDirectoryMeta → internal/config/acme.go (~230 lines moved) 3 of N. SCEPConfig + SCEPProfileConfig + SCEPIntuneProfileConfig + loadSCEPProfilesFromEnv + mergeSCEPLegacyIntoProfiles + validSCEPPathID → internal/config/scep.go (~330 lines) 4 of N. ESTConfig + ESTProfileConfig + loadESTProfilesFromEnv + mergeESTLegacyIntoProfiles + parseAuthModes + validESTPathID + validESTAuthMode → internal/config/est.go (~250 lines) 5 of N. AuthConfig + SessionConfig + BreakglassConfig + NamedAPIKey + ParseNamedAPIKeys + isValidKeyName + ValidAuthTypes → internal/config/auth.go (~340 lines) 6 of N. ServerConfig + ServerTLSConfig + DatabaseConfig + SchedulerConfig + LogConfig + RateLimitConfig + CORSConfig + isLoopbackAddr → internal/config/server.go (~270 lines) 7 of N. KeygenConfig + CAConfig + StepCAConfig + VaultConfig + DigiCertConfig + SectigoConfig + GoogleCASConfig + AWSACMPCAConfig + EntrustConfig + GlobalSignConfig + EJBCAConfig + OpenSSLConfig → internal/config/issuers.go (~600 lines) After the config.go cuts land, the same pattern applies to the next 5 hotspots: 8 of N. cmd/server/main.go split: main.go (entrypoint), wire.go (DI assembly), migrations.go (boot-migration path). Phase 4's migration-hook lives in main.go today; migrations.go inherits the path without re-touching it. 9 of N. internal/service/acme.go split: orders.go, authz.go, challenges.go, nonces.go, gc.go under internal/service/acme/. Becomes its own subpackage. 10 of N. internal/mcp/tools.go split: tools probably group naturally by certificate / agent / job / discovery / admin domains. 11 of N. internal/api/handler/auth_session_oidc.go split: by handler verb (login, callback, refresh, logout, backchannel). 12 of N. cmd/agent/main.go split: main.go (entrypoint), poll.go (work-poll loop), deploy.go (deployment execution), register.go (bootstrap + registration). Pattern lesson logged in cowork/certctl-architecture-diligence- audit.html Tasks-Deferred table. Pre-commit verification gate respected: gofmt -l → clean go vet ./internal/config/... → clean (implicit via go test) go test ./internal/config/... → ok staticcheck ./internal/config/... → clean TestRouterRBACGateCoverage → not affected (config package) Closes: cowork/certctl-architecture-diligence-audit.html#fix-ARCH-M2 (partial — 1 of N — full ARCH-M2 closure is the aggregate) --- internal/config/config.go | 68 ---------------------------- internal/config/notifiers.go | 86 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 68 deletions(-) create mode 100644 internal/config/notifiers.go diff --git a/internal/config/config.go b/internal/config/config.go index d24c0cc..1b216e8 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -353,74 +353,6 @@ type GCPSecretMgrDiscoveryConfig struct { Credentials string } -// NotifierConfig contains configuration for notification connectors. -// Each notifier is enabled by setting its required env var (webhook URL or API key). -type NotifierConfig struct { - // SlackWebhookURL is the incoming webhook URL for Slack notifications. - // Format: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX - // Optional: leave empty to disable Slack notifications. - SlackWebhookURL string - - // SlackChannel optionally overrides the default channel in the Slack webhook. - // Example: "#alerts" or "@user". Leave empty to use webhook's default channel. - SlackChannel string - - // SlackUsername sets the display name for Slack bot messages. - // Default: "certctl". Used in webhook message formatting. - SlackUsername string - - // TeamsWebhookURL is the incoming webhook URL for Microsoft Teams notifications. - // Format: https://outlook.webhook.office.com/webhookb2/... - // Optional: leave empty to disable Teams notifications. - TeamsWebhookURL string - - // PagerDutyRoutingKey is the integration key for PagerDuty Events API v2. - // Obtain from PagerDuty integration settings. - // Optional: leave empty to disable PagerDuty notifications. - PagerDutyRoutingKey string - - // PagerDutySeverity sets the default severity level for PagerDuty events. - // Valid values: "info", "warning", "error", "critical". Default: "warning". - PagerDutySeverity string - - // OpsGenieAPIKey is the API key for OpsGenie Alert API v2. - // Obtain from OpsGenie organization settings. - // Optional: leave empty to disable OpsGenie notifications. - OpsGenieAPIKey string - - // OpsGeniePriority sets the default priority for OpsGenie alerts. - // Valid values: "P1", "P2", "P3", "P4", "P5". Default: "P3". - OpsGeniePriority string - - // SMTPHost is the SMTP server hostname for sending email notifications. - // Example: "smtp.gmail.com", "smtp.sendgrid.net". Required for email notifications. - // Setting: CERTCTL_SMTP_HOST environment variable. - SMTPHost string - - // SMTPPort is the SMTP server port. Default: 587 (STARTTLS). - // Common values: 25 (plain), 465 (implicit TLS), 587 (STARTTLS). - // Setting: CERTCTL_SMTP_PORT environment variable. - SMTPPort int - - // SMTPUsername is the SMTP authentication username. - // Setting: CERTCTL_SMTP_USERNAME environment variable. - SMTPUsername string - - // SMTPPassword is the SMTP authentication password or app-specific password. - // Setting: CERTCTL_SMTP_PASSWORD environment variable. - SMTPPassword string - - // SMTPFromAddress is the sender email address for outbound notifications. - // Example: "certctl@example.com", "noreply@company.com". - // Setting: CERTCTL_SMTP_FROM_ADDRESS environment variable. - SMTPFromAddress string - - // SMTPUseTLS enables TLS for the SMTP connection. - // Default: true. Set to false for plain SMTP (not recommended). - // Setting: CERTCTL_SMTP_USE_TLS environment variable. - SMTPUseTLS bool -} - // KeygenConfig controls where private keys are generated. type KeygenConfig struct { // Mode determines where certificate private keys are generated. diff --git a/internal/config/notifiers.go b/internal/config/notifiers.go new file mode 100644 index 0000000..1b38605 --- /dev/null +++ b/internal/config/notifiers.go @@ -0,0 +1,86 @@ +// Copyright 2026 certctl LLC. All rights reserved. +// SPDX-License-Identifier: BUSL-1.1 + +package config + +// Phase 9 ARCH-M2 closure (2026-05-14): extracted from config.go to +// reduce the change-risk hotspot footprint of the giant config file +// (config.go pre-Phase-9 was 3,403 LOC, exceeding the < 500 LOC +// target). This file contains the NotifierConfig struct unchanged — +// every field, doc-comment, and exported name is byte-identical to +// the pre-split form. The struct lives in the same `config` package +// so every caller's `config.NotifierConfig` import path is preserved +// without modification. +// +// Public-surface invariant: any code importing +// `github.com/certctl-io/certctl/internal/config` reads +// `NotifierConfig` the same way before and after this split. The +// `go doc internal/config NotifierConfig` output is identical. + +// NotifierConfig contains configuration for notification connectors. +// Each notifier is enabled by setting its required env var (webhook URL or API key). +type NotifierConfig struct { + // SlackWebhookURL is the incoming webhook URL for Slack notifications. + // Format: https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX + // Optional: leave empty to disable Slack notifications. + SlackWebhookURL string + + // SlackChannel optionally overrides the default channel in the Slack webhook. + // Example: "#alerts" or "@user". Leave empty to use webhook's default channel. + SlackChannel string + + // SlackUsername sets the display name for Slack bot messages. + // Default: "certctl". Used in webhook message formatting. + SlackUsername string + + // TeamsWebhookURL is the incoming webhook URL for Microsoft Teams notifications. + // Format: https://outlook.webhook.office.com/webhookb2/... + // Optional: leave empty to disable Teams notifications. + TeamsWebhookURL string + + // PagerDutyRoutingKey is the integration key for PagerDuty Events API v2. + // Obtain from PagerDuty integration settings. + // Optional: leave empty to disable PagerDuty notifications. + PagerDutyRoutingKey string + + // PagerDutySeverity sets the default severity level for PagerDuty events. + // Valid values: "info", "warning", "error", "critical". Default: "warning". + PagerDutySeverity string + + // OpsGenieAPIKey is the API key for OpsGenie Alert API v2. + // Obtain from OpsGenie organization settings. + // Optional: leave empty to disable OpsGenie notifications. + OpsGenieAPIKey string + + // OpsGeniePriority sets the default priority for OpsGenie alerts. + // Valid values: "P1", "P2", "P3", "P4", "P5". Default: "P3". + OpsGeniePriority string + + // SMTPHost is the SMTP server hostname for sending email notifications. + // Example: "smtp.gmail.com", "smtp.sendgrid.net". Required for email notifications. + // Setting: CERTCTL_SMTP_HOST environment variable. + SMTPHost string + + // SMTPPort is the SMTP server port. Default: 587 (STARTTLS). + // Common values: 25 (plain), 465 (implicit TLS), 587 (STARTTLS). + // Setting: CERTCTL_SMTP_PORT environment variable. + SMTPPort int + + // SMTPUsername is the SMTP authentication username. + // Setting: CERTCTL_SMTP_USERNAME environment variable. + SMTPUsername string + + // SMTPPassword is the SMTP authentication password or app-specific password. + // Setting: CERTCTL_SMTP_PASSWORD environment variable. + SMTPPassword string + + // SMTPFromAddress is the sender email address for outbound notifications. + // Example: "certctl@example.com", "noreply@company.com". + // Setting: CERTCTL_SMTP_FROM_ADDRESS environment variable. + SMTPFromAddress string + + // SMTPUseTLS enables TLS for the SMTP connection. + // Default: true. Set to false for plain SMTP (not recommended). + // Setting: CERTCTL_SMTP_USE_TLS environment variable. + SMTPUseTLS bool +}