ADR-0006 — Secrets are references only¶
- Status: Accepted — MANDATORY (TIP §66 Decision 6)
- Date: 2026-08-17
- Relates to: TIP §11.2, §17.4, §36, §37, §60 · BRD FR-SEC-001…005, FR-BIND-003, BR-004
Context¶
The portable config is designed to be committed to version control, shared between team members, diffed in pull requests, and stored in a hosted control plane. Every one of those is a place a secret must not be.
Making secret storage possible but discouraged does not work. If a value field exists on a secret binding, it will be populated — by a user in a hurry, by an import path, or by a test fixture that gets copied. The only reliable prevention is for the field not to exist.
Decision¶
A secret is a reference. There is no representation of a secret value in any persisted artifact.
interface SecretBinding {
source: "secret";
name: string;
provider?: "environment" | "vault-reference";
// No `value` field exists. This absence is the control.
}
Supporting rules:
StaticBindingmust rejectsensitive: true. A sensitive static value is a secret binding by another name..env.examplemay contain variable names and safe defaults, never real values, and never real-looking placeholders (TIP §74).- Generated
.gitignoreexcludes local secret files. - Resolution happens at runtime through
SecretResolver—EnvironmentSecretProviderat V1, cloud providers later. The portable config stays provider-agnostic; deployment decides the provider. - CLI flags must not carry secret values: process argument lists are readable by other processes on the host (TIP §32).
- Redaction runs before persistence, logging, and telemetry emission — never after. It is driven by the binding graph (authoritative) plus sensitive-name matching (heuristic backstop), not by name heuristics alone.
- No secret reaches a span attribute, metric label, or log field (TIP §89.3).
Consequences¶
Positive. The config is safe to commit, diff, and review, which is what makes governance workflows possible. A leaked config exposes structure, not credentials. Cloud secret providers slot in without a schema change. The invariant is checkable by machine.
Negative. The live playground cannot avoid handling a real credential in memory, so that path needs its own explicit policy (FR-SEC-005, OQ-05) rather than inheriting this one. Users cannot store a credential "just for testing", which is friction that will occasionally be reported as a bug.
Enforcement¶
config-schematype-level:SecretBindinghas novalue;StaticBinding.sensitiveis typedfalse.- Unit test: attempting a literal on a secret binding is rejected (
BND-003). secret-leakagesuite (TIP §86): run a tool call with a sentinel secret; assert the sentinel is absent from exported config, logs, traces, spans, and error messages. Blocking in CI.- Secret scanning over fixtures in the PR pipeline (TIP §49).
- Task
P0-W11-T01builds redaction in Phase 0 — before there is anything to leak — because retrofitted redaction is how secrets escape.