Module: Familia::Features::TransientFields

Defined in:
lib/familia/features/transient_fields.rb

Overview

TransientFields is a feature that provides secure handling of sensitive runtime data that should never be persisted to Redis/Valkey. Unlike encrypted fields, transient fields exist only in memory and are automatically wrapped in RedactedString objects for security.

Transient fields are ideal for:

  • API keys and tokens that change frequently
  • Temporary passwords or passphrases
  • Session-specific secrets
  • Any sensitive data that should never touch persistent storage
  • Debug or development secrets that need secure handling

All transient field values are automatically wrapped in RedactedString instances which provide:

  • Automatic redaction in logs and string representations
  • Secure memory management with explicit cleanup
  • Safe access patterns through expose blocks
  • Protection against accidental value exposure

Example:

class ApiClient < Familia::Horreum feature :transient_fields

field :endpoint          # Regular persistent field
transient_field :token   # Transient field (not persisted)
transient_field :secret, as: :api_secret  # Custom accessor name

end

client = ApiClient.new( endpoint: 'https://api.example.com', token: ENV['API_TOKEN'], secret: ENV['API_SECRET'] )

# Regular field persists client.save client.endpoint # => "https://api.example.com"

# Transient fields are RedactedString instances puts client.token # => "[REDACTED]"

# Access the actual value safely client.token.expose do |token| response = HTTP.post(client.endpoint, headers: { 'Authorization' => "Bearer #token" } ) # Token value is only available within this block end

# Explicit cleanup when done client.token.clear!

Security Features:

RedactedString automatically protects sensitive values:

  • String representation shows "[REDACTED]" instead of actual value
  • Inspect output shows "[REDACTED]" instead of actual value
  • Hash values are constant to prevent value inference
  • Equality checks work only on object identity

Safe Access Patterns:

# ✅ Recommended: Use .expose block client.token.expose do |token| # Use token directly without creating copies HTTP.auth("Bearer #token") # Safe end

# ✅ Direct access (use carefully) raw_token = client.token.value # Remember to clear original source if needed

# ❌ Avoid: These create uncontrolled copies token_copy = client.token.value.dup # Creates copy in memory interpolated = "Bearer #clientclient.token" # Creates copy via to_s

Memory Management:

# Clear individual fields client.token.clear!

# Check if cleared client.token.cleared? # => true

# Accessing cleared values raises error client.token.value # => SecurityError: Value already cleared

⚠️ Important Security Limitations:

Ruby provides NO memory safety guarantees for cryptographic secrets:

  • No secure wiping: .clear! is best-effort only
  • GC copying: Garbage collector may duplicate secrets
  • String operations: Every manipulation creates copies
  • Memory persistence: Secrets may remain in memory indefinitely

For highly sensitive applications, consider external secrets management (HashiCorp Vault, AWS Secrets Manager) or languages with secure memory handling.

Defined Under Namespace

Modules: ModelClassMethods

Instance Method Summary collapse

Instance Method Details

#clear_transient_fields!void

This method returns an undefined value.

Clear all transient fields for this instance

This method iterates through all defined transient fields and calls clear! on each RedactedString instance. Use this for cleanup when the object is no longer needed.

Examples:

Clear all secrets when done

client = ApiClient.new(token: 'secret', api_key: 'key123')
# ... use client ...
client.clear_transient_fields!
client.token.cleared?  # => true


191
192
193
194
195
196
# File 'lib/familia/features/transient_fields.rb', line 191

def clear_transient_fields!
  self.class.transient_fields.each do |field_name|
    field_value = instance_variable_get("@#{field_name}")
    field_value.clear! if field_value.respond_to?(:clear!)
  end
end

#transient_fields_cleared?Boolean

Check if all transient fields have been cleared

Returns:

  • (Boolean)

    true if all transient fields are cleared, false otherwise



202
203
204
205
206
207
# File 'lib/familia/features/transient_fields.rb', line 202

def transient_fields_cleared?
  self.class.transient_fields.all? do |field_name|
    field_value = instance_variable_get("@#{field_name}")
    field_value.nil? || (field_value.respond_to?(:cleared?) && field_value.cleared?)
  end
end

#transient_fields_summaryHash

Returns a hash of transient field names and their redacted representations

This method is useful for debugging and logging as it shows which transient fields are defined without exposing their actual values.

Examples:

Check transient field status

client.transient_fields_summary
# => { token: "[REDACTED]", api_key: "[REDACTED]" }

Returns:

  • (Hash)

    Hash with field names as keys and "[REDACTED]" as values



220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/familia/features/transient_fields.rb', line 220

def transient_fields_summary
  self.class.transient_fields.each_with_object({}) do |field_name, summary|
    field_value = instance_variable_get("@#{field_name}")
    summary[field_name] = if field_value.nil?
                            nil
                          elsif field_value.respond_to?(:cleared?) && field_value.cleared?
                            '[CLEARED]'
                          else
                            '[REDACTED]'
                          end
  end
end