How do I access the raw value of a Sensitive in EK9?
← Security and Sanitization · Ref: Q703
The reveal() method on Sensitive returns the raw secret value as a String. It is gated by the Privileged trait — only classes with 'with trait of Privileged' can call reveal().
PRIVILEGED TRAIT
The Privileged trait is a built-in marker trait with no methods. It acts as a compile-time gate:
HttpClient with trait of Privileged sendRequest() -> apiKey as Sensitive header <- apiKey.reveal()
WHY GATED?
Most code should never need the raw secret. Sensitive values should flow through the system redacted. Only infrastructure code (HTTP clients, database drivers, encryption) needs the actual value. The Privileged trait makes this boundary explicit and auditable.
WHO CANNOT CALL REVEAL?
- Classes without the Privileged trait → E11090 - Functions (cannot have traits) → E11090 - Dynamic classes without Privileged → E11090
WHO CAN CALL REVEAL?
- Regular classes with 'with trait of Privileged' - Named dynamic classes with 'trait of Privileged' - Unnamed dynamic classes with 'trait of Privileged'
AUDIT TRAIL
Searching the codebase for 'Privileged' identifies every point where secrets can be exposed. This makes security audits trivial compared to languages where any code can access secret values.
See Q701 for the Sensitive type overview. See Q702 for sensitiveGet() patterns. See Q704 for compile-time secret detection.
Example
defines module qa.security.privilegedreveal defines trait SecretConsumer consumeSecret() as abstract -> secret as Sensitive defines class <?- A class WITH Privileged can call reveal() to access raw secret value. Only infrastructure code like HTTP clients should do this. -?> HttpClient with trait of Privileged sendAuthenticatedRequest() -> token as Sensitive rawToken <- token.reveal() stdout <- Stdout() if rawToken? header <- `Bearer ${rawToken}` stdout.println(`Auth header set: ${length header} chars`) <?- A class WITHOUT Privileged can safely handle Sensitive values using redaction, comparison, and copy - but NOT reveal(). -?> SafeLogger logActivity() -> credential as Sensitive stdout <- Stdout() if credential? //$ always returns ***REDACTED*** - safe to log stdout.println("Activity with credential: " + $credential) else stdout.println("No credential for activity") default operator ? defines function <?- Named dynamic class with Privileged can call reveal(). -?> testNamedDynamicReveal() env <- EnvVars() secret <- env.sensitiveGet("DB_PASSWORD") extractor <- PasswordExtractor(secret) trait of Privileged as class getPassword() <- pwd as String? pwd: secret.reveal() default operator ? if extractor? result <- extractor.getPassword() stdout <- Stdout() if result? stdout.println(`Password extracted: ${length result} chars`) <?- Test that non-Privileged code can still work with Sensitive values. -?> testSafeOperations() stdout <- Stdout() env <- EnvVars() secret <- env.sensitiveGet("API_KEY") //All safe operations - no Privileged needed if secret? //Auto-promotes to ***REDACTED*** in string context stdout.println("Redacted: " + secret) backup <- Sensitive() if secret? backup :=: secret if backup? stdout.println("Backup created successfully") testPrivilegedReveal() env <- EnvVars() token <- env.sensitiveGet("AUTH_TOKEN") client <- HttpClient() if client? and token? client.sendAuthenticatedRequest(token) testSafeLogging() env <- EnvVars() credential <- env.sensitiveGet("SERVICE_KEY") logger <- SafeLogger() if logger? logger.logActivity(credential) defines program PrivilegedRevealDemo() stdout <- Stdout() stdout.println("Privileged trait and reveal() demonstrations") testNamedDynamicReveal() testSafeOperations() testPrivilegedReveal() testSafeLogging()
Common mistakes
E11090 — Only classes with the Privileged trait can call reveal() on Sensitive values. Removing 'with trait of Privileged' causes the reveal() call inside sendAuthenticatedRequest() to fail. See ek9 -h E11090 for details.
Incorrect:
HttpClient
Correct:
HttpClient with trait of Privileged
E11090 — Dynamic classes also need the Privileged trait to call reveal(). Removing 'trait of Privileged' causes the reveal() call inside getPassword() to fail. See ek9 -h E11090 for details.
Incorrect:
PasswordExtractor(secret) as class
Correct:
PasswordExtractor(secret) trait of Privileged as class
E50060 — Hardcoded API keys are detected at compile time. Load API keys from environment variables using sensitiveGet(). See ek9 -h E50060 for details.
Incorrect:
secret <- "sk_test_abcdefghijklmnopqrstuvwxyz"
Correct:
secret <- env.sensitiveGet("API_KEY")
E50060 — Private key material must not be hardcoded in source code. Load private keys from environment variables or files at runtime. See ek9 -h E50060 for details.
Incorrect:
token <- "-----BEGIN RSA PRIVATE KEY-----"
Correct:
token <- env.sensitiveGet("AUTH_TOKEN")
Other ways to ask this
- What is the Privileged trait in EK9?
- How does reveal() work on Sensitive values?
- How do I unwrap a secret when I need the actual value?
Coming from another language?
Java: No access control on secret values. Any code with a String reference can print, log, or serialize it. Rust: secrecy crate's expose_secret() can be called by any code — no trait gating. Python: No secret type at all. Go: No secret type. EK9: reveal() is compile-time gated by the Privileged trait. The compiler rejects reveal() calls from non-Privileged contexts.
Keywords: privileged, gate, sensitive, reveal, access, infrastructure, secret, raw, audit, security, trait, unwrap