What is the Sensitive type in EK9?
← Security and Sanitization · Ref: Q701
The Sensitive type is a built-in type that wraps secret values with automatic protection against accidental exposure.
AUTO-REDACTION
The $ and $$ operators on a set Sensitive always return '***REDACTED***'. This means secrets cannot leak through:
stdout.println($apiKey) Shows ***REDACTED*** msg <- `Key: ${apiKey}` Interpolates as ***REDACTED*** jsonField: $apiKey Serializes as ***REDACTED***
CONTROLLED CONSTRUCTION
The only way to create a set Sensitive is EnvVars.sensitiveGet():
env <- EnvVars() secret <- env.sensitiveGet("API_KEY")
There is no String constructor visible to EK9 code. Sensitive() creates an unset value.
CONSTANT-TIME EQUALITY
The == and <> operators use MessageDigest.isEqual() internally to prevent timing attacks on secret comparison.
COPY SUPPORT
The :=: copy operator transfers secret values between Sensitive variables.
See Q702 for sensitiveGet() patterns. See Q703 for the Privileged trait and reveal(). See Q704 for compile-time secret detection. See Q218 for security best practices. See Q272 for defense in depth.
Example
defines module qa.security.sensitivetype defines class <?- A class without Privileged cannot call reveal(). It can receive, store, compare, and redact Sensitive values. -?> SecretLogger logRedacted() -> credential as Sensitive stdout <- Stdout() if credential? //$ always returns "***REDACTED***" - safe to log stdout.println("Credential present: " + $credential) else stdout.println("No credential provided") default operator ? defines function testDefaultConstructor() stdout <- Stdout() //Default constructor creates an unset Sensitive secret <- Sensitive() if not secret? stdout.println("Default Sensitive is unset") testSensitiveGet() stdout <- Stdout() env <- EnvVars() //sensitiveGet() is the ONLY way to create a set Sensitive apiKey <- env.sensitiveGet("API_KEY") if apiKey? //Promotes to ***REDACTED*** automatically stdout.println("Got key (redacted): " + apiKey) else stdout.println("API_KEY not configured") testRedactionInInterpolation() stdout <- Stdout() env <- EnvVars() dbPassword <- env.sensitiveGet("DB_PASSWORD") if dbPassword? //Interpolation auto-promotes to ***REDACTED*** msg <- `Database password: ${dbPassword}` stdout.println(msg) testConstantTimeEquality() stdout <- Stdout() env <- EnvVars() key1 <- env.sensitiveGet("TEST_KEY") key2 <- env.sensitiveGet("TEST_KEY") if key1? and key2? if key1 == key2 stdout.println("Keys match (constant-time comparison)") testCopyOperator() stdout <- Stdout() env <- EnvVars() original <- env.sensitiveGet("SECRET_TOKEN") backup <- Sensitive() if original? backup :=: original if backup? stdout.println("Secret copied to backup") testPassToLogger() env <- EnvVars() secret <- env.sensitiveGet("API_KEY") logger <- SecretLogger() if logger? logger.logRedacted(secret) defines program SensitiveTypeDemo() stdout <- Stdout() stdout.println("Sensitive type demonstrations") testDefaultConstructor() testSensitiveGet() testRedactionInInterpolation() testConstantTimeEquality() testCopyOperator() testPassToLogger()
Common mistakes
E50060 — Sensitive has no String constructor visible to EK9 code. The only way to create a set Sensitive is via EnvVars.sensitiveGet(). See ek9 -h E50060 for details.
Incorrect:
secret <- Sensitive("my-api-key")
Correct:
secret <- Sensitive()
E08090 — Hardcoded cloud provider credentials are detected at compile time. Use EnvVars.sensitiveGet() to load secrets from environment variables instead. See ek9 -h E08090 for details.
Incorrect:
apiKey <- "AKIAIOSFODNN7EXAMPLE1"
Correct:
apiKey <- env.sensitiveGet("API_KEY")
E50060 — Hardcoded API keys are detected at compile time. Load API keys from environment variables using EnvVars.sensitiveGet(). See ek9 -h E50060 for details.
Incorrect:
original <- "sk_test_abcdefghijklmnopqrstuvwxyz"
Correct:
original <- env.sensitiveGet("SECRET_TOKEN")
E08090 — Database URLs with embedded passwords are detected at compile time. Store the connection string in an environment variable. See ek9 -h E08090 for details.
Incorrect:
dbPassword <- "postgres://admin:s3cret@db.example.com/prod"
Correct:
dbPassword <- env.sensitiveGet("DB_PASSWORD")
Other ways to ask this
- How does EK9 protect secret values at runtime?
- How do I prevent secrets from being logged in EK9?
- What is EK9's equivalent of a secret wrapper?
Coming from another language?
Java: No language-level secret type. Secrets stored as plain Strings can be logged, serialized, or leaked through any code path. Libraries like Vault provide runtime wrappers. Python: No secret type; plain strings with no redaction. Rust: secrecy crate provides Secret<T> but any code can call expose_secret() with no restriction. Go: No secret type; plain strings. EK9: Built-in Sensitive type with automatic redaction, no String constructor, constant-time equality, and trait-gated reveal().
Keywords: type, password, log, leak, protect, credential, wrap, safe, token, secret, sensitive, runtime, redact