Doctrine
Credential references must carry domain context
Credential references must carry domain context
Doctrine
Every credential reference — Keychain entries, environment variables, code lookups, config files — MUST encode enough context for a reader to answer three questions without prior knowledge:
1. What provider domain? (Google AI Studio, Anthropic API, Supabase, Apple Developer, etc.) 2. What project or deployment? (camber-db, heartwood-db, orbit-mcp, etc.) 3. What scope? (read-only, admin, edge-function invocation, etc.)
A credential entry that requires the reader to already know the answer is a naked reference — it transfers risk from the writer to every future reader.
Rule
Keychain entries (security add-generic-password): the -s (service) field encodes the provider domain; the -a (account) field encodes the project and scope.
Format: service=<provider-domain>, account=<PROJECT>_<SCOPE>_<KEY_TYPE>
Examples of compliant entries:
service=google-ai-studio,account=CAMBER_GEMINI_API_KEYservice=anthropic,account=CAMBER_CLAUDE_API_KEYservice=supabase,account=CAMBER_DB_SERVICE_ROLE_KEYservice=apple-developer,account=HCB_DISTRIBUTION_CERT
Examples of naked refs (violations):
service=heartwood,account=GEMINI_API_KEY— heartwood is not a provider domain; which Google project? which API surface?service=openai,account=ANTHROPIC_API_KEY— wrong service bucket entirelyservice=anthropic,account=CAMBER_DB_URL— Supabase credential under Anthropic serviceservice=heartwood,account=METACULUS_API_TOKEN— Metaculus is not heartwood domain
Environment variables: must include project prefix. GEMINI_API_KEY alone is naked; CAMBER_GEMINI_API_KEY carries project context.
Code lookups: any function that reads a credential must reference the canonical Keychain path or env var name. Hardcoded secret values in code are a security violation, not just a naming violation.
CREDENTIAL_REGISTRY.md: remains the SSOT index. Every Keychain rename must be reflected here. The registry entry must include: provider domain, project, scope, Keychain service+account path, and last-verified date.
Anti-patterns
- Bare
API_KEYunder a generic service — forces the reader to already know what system it belongs to - Cross-contaminated service buckets (Anthropic key under OpenAI service)
- Provider-domain confusion (using a product name like
heartwoodas the Keychain service when the credential belongs to an external provider like Google) - Renaming the Keychain entry without updating all downstream code that reads from the old path
Relationship to ORA-2026-0044 (names carry contracts)
This doctrine is a specialization of ORA-2026-0044 for credentials. If names carry contracts, credential names carry security contracts. A naked credential reference is the security equivalent of naming a module utils — it tells you nothing about what it does, and forces every consumer to carry implicit knowledge the name should have encoded.
Origin
2026-04-26 shepherd audit of Keychain entries during CMB-1816 (credential hygiene + Gemini photo reclassification). Found 5 naked refs with cross-contaminated service buckets. The cost: sessions reading credentials had to guess which key belonged to which system, and one photo reclassification run (CMB-1791) fell back to Haiku because the Gemini key wasn't findable under its actual Keychain path.