Skip to content

Feat/secret from env - #85

Merged
moebiusband73 merged 6 commits into
mainfrom
feat/secret-from-env
Sep 3, 2026
Merged

Feat/secret from env#85
moebiusband73 merged 6 commits into
mainfrom
feat/secret-from-env

Conversation

@moebiusband73

Copy link
Copy Markdown
Member

No description provided.

moebiusband73 and others added 6 commits September 3, 2026 18:45
Secrets could previously only be resolved by each consumer inlining its own
os.Getenv fallback, which meant a secret always had to be reachable either from
the process environment or from a config file. SecretFromEnv adds a third
source with a fixed precedence -- $VAR, then the contents of the file named by
$VAR_FILE, then the config value -- so a secret can be mounted as a file by
Docker, Kubernetes or systemd LoadCredential and never appear in either.

SecretFromConfig covers repeated config sections (one sink, one receiver),
where no fixed variable name can address a single instance; there the instance
names its own sources through sibling config keys.

An unreadable or whitespace-only secret file is an error rather than a silent
fallback: an operator who sets $VAR_FILE intends the file to win, so falling
back to the config value would quietly start the process with a stale
credential.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Float type with JSON NaN support is defined in schema/float.go, not in
util, so the documented util.Float and util.NaN do not exist. Point at the
schema package instead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The NATS username and password could only come from the configuration file,
which forced every deployment to keep a plaintext credential on disk. They are
now resolved through util.SecretFromEnv, so $CC_NATS_USERNAME and
$CC_NATS_PASSWORD -- or the files named by their _FILE variants -- take
precedence over the configured values.

Resolution happens in resolveCredentials at connect time rather than in Init:
an explicitly passed config then behaves identically to the global one, Init
stays optional, and the plaintext never lands in the exported Keys, which an
application may re-marshal or dump.

The names carry a CC_ prefix because cc-lib is linked into several
applications, whose environments it must not silently claim names in.

The package had no tests and no CI workflow at all, so both are added here;
credential resolution is covered without needing a live NATS server.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The startup auth token could only come from the configuration file. It is now
resolved through util.SecretFromEnv, so $CC_STARTUP_AUTH_TOKEN, or the file
named by $CC_STARTUP_AUTH_TOKEN_FILE, takes precedence over the configured
value.

An unreadable secret file aborts CCStartup rather than falling back, so an
unauthenticated POST is never sent in place of an authenticated one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sink and receiver credentials could only come from the configuration file,
which forced a plaintext password onto disk for every InfluxDB, NATS, HTTP,
QuestDB and EECPT endpoint, and for every BMC reached over redfish or IPMI.

Sinks and receivers are configured as maps of named instances, so no fixed
environment variable name can address the credential of one particular
instance -- and deriving one from the instance name collides ("influx-1" and
"influx.1" both mangle to INFLUX_1). Each instance therefore names its own
sources, through sibling keys: "<key>_env" gives the name of an environment
variable and "<key>_file" the path of a file, either of which takes precedence
over the inline value.

The redfish and ipmi global default credentials are pointers, so nil already
means "unset". They additionally fall back to CC_REDFISH_USERNAME /
CC_REDFISH_PASSWORD and CC_IPMI_USERNAME / CC_IPMI_PASSWORD with no new
configuration keys at all. Per-host client_config entries still win, so
existing configurations are unaffected.

Resolution happens right after the strict decode and before each constructor's
required-field and basic-authentication checks, so a credential supplied from
the environment counts as configured.

Note for operators: because these packages decode with DisallowUnknownFields,
a configuration using the new sibling keys is rejected outright by an older
cc-lib rather than ignored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@moebiusband73
moebiusband73 merged commit f15a225 into main Sep 3, 2026
5 checks passed
@moebiusband73
moebiusband73 deleted the feat/secret-from-env branch September 3, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant