Skip to content

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:

  1. The actual value is sent to the plugin for cloud operations
  2. When persisted to formae's database, the value is hashed (SHA-256)
  3. CLI output masks the value with asterisks
  4. 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:

  1. First apply: Value is set to the generated password
  2. Subsequent applies: The original value is preserved, even if random.password() would generate a different value
  3. 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:

  1. Your plugin receives plain values: The value, visibility, and strategy metadata is stripped before your plugin sees the properties
  2. Return plain values: Your plugin returns normal JSON—formae adds metadata back as needed
  3. 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.