Skip to content

D47 — Per-KB SOPS: AgeKeyEnv (env wins), flat service_get(resolve_secrets), resolve requires rw

Decision. Connects KBSpec.SopsAgeKeyFile/SopsConfig.AgeKeyFile (D44) to the first real invocation of internal/sops. Decrypt/DecryptAll/ResolveRefs gain a variadic env ...string (same pattern as gitx.runGitEnv, D46: the caller's env wins). service_get gains the resolve_secrets parameter (default false): if true, it reads secrets_source from the Service's frontmatter and calls sops.Decrypt with the per-KB age key. OKF frontmatter supports only string/[]string, so structured per-ref secret_refs remain out of scope: resolve_secrets always decrypts the entire secrets_source. (Superseded in part by D166: DecryptAll was deleted. It never gained a caller — service_get resolves one secrets_source at a time through Decrypt — so the variadic env this entry gave it was only ever exercised on the other two.) service_get stays classified ReadOnly (D45) for the default path; with resolve_secrets: true the HTTP guard (mcpAccessGuard) forces needWrite=true as a special case, without touching the per-tool-name classification. Defense in depth: filepath.IsLocal(secretsSource) rejects path traversal on secrets_source before decryption. Rationale. Propagating the env instead of mutating os.Environ() prevents one KB's age key from contaminating requests on another in a multi-KB server (same reason as D46). The special case in the guard keeps all r/rw enforcement in one place, consistent with D45. Details: docs/skills-services-secrets.md §SOPS secrets, docs/transport-auth.md.