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
-
#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.
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
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_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.
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 |