harness: config layer + model routing v0 (tier map, no heuristics)
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
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
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>"
|
||||
}
|
||||
Reference in New Issue
Block a user