harness.toml loading via a stdlib-only TOML subset parser (tables, bare
keys, strings/ints/bools, multi-line arrays; anything richer fails loudly).
Secrets are refs only (env:/file:/literal:, bw: reserved) and are redacted
from every error path. Model routing v0: static class -> tier alias ->
concrete model map per the DESIGN model-selection layer; requests carry the
resolved concrete model and unknown classes fail hard so routing stays
auditable. Table-driven tests cover the parser, validation, key refs, and
routing decisions.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
// Secret key references. Values are never logged and never stored by the
|
|
// harness; only their resolved bytes reach the outgoing HTTP headers.
|
|
//
|
|
// Formats:
|
|
//
|
|
// env:NAME environment variable (must be set and non-empty)
|
|
// file:PATH file whose trimmed contents are the key
|
|
// literal:VALUE inline key (last resort; still never logged)
|
|
// bw:REF bitwarden item (reserved; lands with the bw wrapper, phase 3)
|
|
func CheckKeyRef(ref string) error {
|
|
switch {
|
|
case ref == "":
|
|
return fmt.Errorf("empty key reference")
|
|
case strings.HasPrefix(ref, "env:"):
|
|
if len(ref) <= 4 {
|
|
return fmt.Errorf("key ref %q: env: needs a variable name", redact(ref))
|
|
}
|
|
case strings.HasPrefix(ref, "file:"):
|
|
if len(ref) <= 5 {
|
|
return fmt.Errorf("key ref %q: file: needs a path", redact(ref))
|
|
}
|
|
case strings.HasPrefix(ref, "literal:"):
|
|
if len(ref) <= 8 {
|
|
return fmt.Errorf("key ref: literal: value is empty")
|
|
}
|
|
case strings.HasPrefix(ref, "bw:"):
|
|
return fmt.Errorf("key ref bw: not implemented yet (bitwarden wrapper lands in build phase 3)")
|
|
default:
|
|
return fmt.Errorf("key ref must use env:, file:, literal:, or bw: prefixes (unrecognized ref redacted)")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ResolveKeyRef resolves a key reference to its value. Never log the result.
|
|
func ResolveKeyRef(ref string) (string, error) {
|
|
if err := CheckKeyRef(ref); err != nil {
|
|
return "", err
|
|
}
|
|
switch {
|
|
case strings.HasPrefix(ref, "env:"):
|
|
v := os.Getenv(ref[4:])
|
|
if v == "" {
|
|
return "", fmt.Errorf("environment variable %s is not set", ref[4:])
|
|
}
|
|
return v, nil
|
|
case strings.HasPrefix(ref, "file:"):
|
|
data, err := os.ReadFile(ref[5:])
|
|
if err != nil {
|
|
return "", fmt.Errorf("read key file: %w", err)
|
|
}
|
|
v := strings.TrimSpace(string(data))
|
|
if v == "" {
|
|
return "", fmt.Errorf("key file %s is empty", ref[5:])
|
|
}
|
|
return v, nil
|
|
case strings.HasPrefix(ref, "literal:"):
|
|
return ref[8:], nil
|
|
}
|
|
return "", fmt.Errorf("unreachable key ref branch")
|
|
}
|
|
|
|
// redact masks everything after a recognized prefix so malformed refs can
|
|
// be reported without echoing a possibly-pasted secret. Refs without a
|
|
// recognizable prefix are never echoed at all.
|
|
func redact(ref string) string {
|
|
if i := strings.IndexByte(ref, ':'); i >= 0 && len(ref) > i+1 {
|
|
return ref[:i+1] + "****"
|
|
}
|
|
return "<redacted>"
|
|
}
|