Values
The formae.Value class wraps string values with special behaviors for secrets and immutability. Use it when a property needs to be hidden from output or preserved across applies.
When to Use Values
Use formae.Value for:
- Secrets: Passwords, API keys, tokens that should never appear in logs or CLI output
- Generated values: Random IDs or passwords that should remain stable after initial creation
- Sensitive configuration: Any value that shouldn't be visible to operators
Creating Values
Use the formae.value() helper function:
import "@formae/formae.pkl"
// Basic value (same as using a plain string)
name = formae.value("my-resource")
// Secret value - never displayed
apiKey = formae.value("sk-secret-key").opaque
// Stable value - set once, never changes
instanceId = formae.value(random.id(16)).setOnce
// Combined - secret that never changes
password = formae.value(random.password(24)).opaque.setOnce
Visibility: Clear vs Opaque
The visibility property controls whether values appear in CLI output and logs.
Clear (Default)
Standard values that appear normally:
description = formae.value("My application server")
Output in CLI:
+ description: "My application server"
Opaque
Secret values that are masked in all output:
secretString = formae.value("super-secret-password").opaque
Output in CLI:
+ secretString: (opaque value)
What happens internally:
- The actual value is sent to the plugin for cloud operations
- When persisted to formae's database, the value is hashed (SHA-256)
- CLI output masks the value with asterisks
- Logs never contain the plaintext value
Strategy: Update vs SetOnce
The strategy property controls whether values can change after initial creation.
Update (Default)
Values can change on every apply:
description = formae.value(properties.desc.value)
If properties.desc.value changes between applies, the resource is updated.
SetOnce
Values are set once and preserved forever:
// Generate a random password on first apply, keep it stable
secretString = formae.value(random.password(12)).setOnce
Behavior:
- First apply: Value is set to the generated password
- Subsequent applies: The original value is preserved, even if
random.password()would generate a different value - The PKL expression is re-evaluated, but the result is discarded in favor of the existing value
This is critical for: - Database passwords that shouldn't rotate unexpectedly - API keys that would break integrations if changed - Any identifier that external systems depend on
Common Patterns
AWS Secrets Manager Secret
import "@aws/secretsmanager/secret.pkl"
import "@formae/ext/random.pkl"
new secret.Secret {
label = "database-password"
name = "prod/database/password"
description = "Production database master password"
// Generate 24-char password, never display it, never change it
secretString = formae.value(random.password(24, true)).opaque.setOnce
}
Referencing Opaque Values
When one resource references another's opaque value, the opacity is preserved:
local dbSecret = new secret.Secret {
label = "db-password"
secretString = formae.value("my-password").opaque
}
new dbinstance.DBInstance {
label = "database"
masterUsername = "admin"
// Reference the secret - opacity is maintained
masterUserPassword = dbSecret.res.secretString
}
The database instance receives the actual password value, but it remains masked in all formae output.
Conditional Secrets
// Use provided password or generate one
secretString = if (properties.password.value != null)
formae.value(properties.password.value).opaque
else
formae.value(random.password(16)).opaque.setOnce
What Plugin Authors Need to Know
You don't handle Value specially. The formae core handles all Value processing:
- Your plugin receives plain values: The
value,visibility, andstrategymetadata is stripped before your plugin sees the properties - Return plain values: Your plugin returns normal JSON—formae adds metadata back as needed
- No special handling needed: The visibility and strategy logic is entirely in formae's core
The only time you might see Value metadata is if you're debugging raw properties during development.
Summary
| Modifier | Effect |
|---|---|
.opaque |
Value is masked in CLI, hashed in database |
.setOnce |
Value is preserved after first apply |
.opaque.setOnce |
Both: secret that never changes |
Use formae.value() for any sensitive or stable values. For regular properties, plain strings work fine.