How do I use credentials, URLs, usernames and passwords or tokens to connect to databases or services?
← Security and Sanitization · Ref: Q706
EK9 separates configuration from secrets using EnvVars:
env.get(name) Non-secret config (URLs, hostnames, ports)
env.sensitiveGet(name) Secrets (passwords, API keys, tokens)
DATABASE CONNECTION PATTERN
dbHost <- env.get("DB_HOST") dbPassword <- env.sensitiveGet("DB_PASSWORD")
Config as regular Strings, password as Sensitive (auto-redacts if logged).
DRIVER COMPONENT PATTERN
Driver uses Privileged trait to call reveal() at the connection boundary:
DatabaseDriver with trait of Privileged connect() -> password as Sensitive rawPassword <- password.reveal()
Secrets flow as protected Sensitive values. Only Privileged components access raw values.
See Q701 for Sensitive type. See Q702 for sensitiveGet(). See Q703 for Privileged/reveal(). See Q112 for services. See Q271 for EnvVars. See Q231 for DI program-application linking. See Q232 for component chains.
Example
defines module qa.security.credentialpreparation defines trait <?- Trait for any component that consumes database credentials. -?> DatabaseConsumer connectToDatabase() as abstract -> host as String port as String dbName as String username as String password as Sensitive defines class <?- A database driver component with the Privileged trait. Only this class can call reveal() to access the raw password at the actual connection boundary. -?> DatabaseDriver with trait of Privileged, DatabaseConsumer override connectToDatabase() -> host as String port as String dbName as String username as String password as Sensitive stdout <- Stdout() //Configuration values are regular Strings - safe to log stdout.println(`Connecting to ${host}:${port}/${dbName} as ${username}`) //The password is Sensitive - auto-promotes to ***REDACTED*** stdout.println(`Password status: ${password}`) //Only here, at the connection boundary, do we reveal the raw value rawPassword <- password.reveal() if rawPassword? //This is where the actual database driver call would go //e.g. connection <- jdbcDriver.connect(host, port, dbName, username, rawPassword) stdout.println("Database connection established") default operator ? <?- A service client component with the Privileged trait. Uses reveal() only at the HTTP request boundary. -?> ServiceClient with trait of Privileged callService() -> serviceUrl as String apiKey as Sensitive stdout <- Stdout() //URL is configuration - safe to log stdout.println(`Calling service at ${serviceUrl}`) //API key is Sensitive - auto-promotes to ***REDACTED*** stdout.println(`API key status: ${apiKey}`) //Reveal only at the HTTP boundary rawKey <- apiKey.reveal() if rawKey? //This is where the actual HTTP client call would go //e.g. response <- httpClient.get(serviceUrl, "Authorization", "Bearer " + rawKey) stdout.println("Service call completed") default operator ? defines constant dbPasswordKey <- "DB_PASSWORD" defines function <?- Demonstrates the full database credential preparation pattern. -?> testDatabaseCredentials() stderr <- Stderr() env <- EnvVars() //Configuration values loaded with get() - regular Strings dbHost <- env.get("DB_HOST") dbPort <- env.get("DB_PORT") dbName <- env.get("DB_NAME") dbUser <- env.get("DB_USER") //Password loaded with sensitiveGet() - Sensitive type dbPassword <- env.sensitiveGet(dbPasswordKey) if dbHost? and dbPort? and dbName? and dbUser? and dbPassword? driver <- DatabaseDriver() if driver? driver.connectToDatabase(host: dbHost, port: dbPort, dbName: dbName, username: dbUser, password: dbPassword) else stderr.println("Database not fully configured - check environment variables") <?- Demonstrates the service API credential preparation pattern. -?> testServiceCredentials() stderr <- Stderr() env <- EnvVars() //Service URL is configuration serviceUrl <- env.get("PAYMENT_API_URL") //API key is a secret apiKey <- env.sensitiveGet("PAYMENT_API_KEY") if serviceUrl? and apiKey? client <- ServiceClient() if client? client.callService(serviceUrl, apiKey) else stderr.println("Payment service not configured - check environment variables") <?- Shows that credentials remain protected throughout the chain. -?> testCredentialProtection() stdout <- Stdout() env <- EnvVars() secret <- env.sensitiveGet(dbPasswordKey) if secret? //Safe everywhere - auto-promotes to ***REDACTED*** stdout.println(`Direct: ${secret}`) msg <- "Credential: " + secret stdout.println(msg) //Can compare without revealing other <- env.sensitiveGet(dbPasswordKey) if other? if secret == other stdout.println("Credentials match (constant-time)") defines program CredentialPreparationDemo() stdout <- Stdout() stdout.println("Credential preparation for database and service connections") testDatabaseCredentials() testServiceCredentials() testCredentialProtection()
Common mistakes
E50060 — Database URLs with embedded passwords are detected at compile time. Separate host/port/name configuration from the password and load the password with sensitiveGet(). See ek9 -h E50060 for details.
Incorrect:
dbPassword <- "postgres://admin:s3cret@db.example.com/prod"
Correct:
dbPassword <- env.sensitiveGet(dbPasswordKey)
E11090 — The driver component needs the Privileged trait to call reveal() at the connection boundary. Removing Privileged causes the compiler to reject the reveal() call. See ek9 -h E11090 for details.
Incorrect:
DatabaseDriver with trait of DatabaseConsumer
Correct:
DatabaseDriver with trait of Privileged, DatabaseConsumer
E50060 — API keys for external services must not be hardcoded. Load them from environment variables using sensitiveGet(). See ek9 -h E50060 for details.
Incorrect:
apiKey <- "sk_live_abcdefghijklmnopqrstuvwxyz"
Correct:
apiKey <- env.sensitiveGet("PAYMENT_API_KEY")
E11080 — Cloud provider credentials must not be hardcoded. Load them from environment variables using sensitiveGet(). See ek9 -h E11080 for details.
Incorrect:
secret <- "AKIAIOSFODNN7EXAMPLE1"
Correct:
secret <- env.sensitiveGet(dbPasswordKey)
Other ways to ask this
- How do I securely configure a database connection in EK9?
- How do I pass API keys to an external service in EK9?
- How do I prepare credentials for a database driver in EK9?
Coming from another language?
Java: plain Strings for credentials, any code can log/leak. EK9: Sensitive values throughout, only Privileged components can reveal().
Keywords: service, database, port, connection, password, username, reveal, credential, component, sensitive, privileged, connect, token, url, prepare, driver, key, api, host, configure, jdbc