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: ClassMethods
Instance Method Summary collapse
-
#clear_transient_fields! ⇒ void
Clear all transient fields for this instance.
-
#transient_fields_cleared? ⇒ Boolean
Check if all transient fields have been cleared.
-
#transient_fields_summary ⇒ Hash
Returns a hash of transient field names and their redacted representations.
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.
182 183 184 185 186 187 |
# File 'lib/familia/features/transient_fields.rb', line 182 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
193 194 195 196 197 198 |
# File 'lib/familia/features/transient_fields.rb', line 193 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_summary ⇒ Hash
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.
211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/familia/features/transient_fields.rb', line 211 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 |