Module: Familia::Features::EncryptedFields::ModelClassMethods

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

Instance Method Summary collapse

Instance Method Details

#encrypted_field(name, aad_fields: [], algorithm: nil) ⇒ Object

Define an encrypted field that transparently encrypts/decrypts values

Encrypted fields are stored as JSON objects containing the encrypted ciphertext along with cryptographic metadata. Values are automatically encrypted on assignment and decrypted on access.

Examples:

Basic encrypted field

class Vault < Familia::Horreum
  feature :encrypted_fields
  encrypted_field :secret_key
end

Encrypted field with additional authentication

class Document < Familia::Horreum
  feature :encrypted_fields
  field :doc_id, :owner_id
  encrypted_field :content, aad_fields: [:doc_id, :owner_id]
end

Encrypted field bound to dynamic entropy

class Secret < Familia::Horreum
  feature :encrypted_fields
  encrypted_field :payload, key_material: ->(rec) { rec.passphrase }
end

Field pinned to a specific write algorithm

class Ledger < Familia::Horreum
  feature :encrypted_fields
  # Keep writing AES-256-GCM even after rbnacl makes XChaCha20 the
  # default, so older nodes can still decrypt during a fleet rollout.
  encrypted_field :entry, algorithm: 'aes-256-gcm'
end

Parameters:

  • name (Symbol)

    Field name

  • aad_fields (Array<Symbol>) (defaults to: [])

    Additional fields to include in authentication

  • key_material (Proc, nil)

    Optional proc mixed into key derivation as extra entropy. It is called with the record instance and must return the entropy as a String (a RedactedString is unwrapped automatically); return nil to contribute no entropy for that record. Because the returned value participates in key derivation rather than authentication, supplying the wrong material yields a derivation mismatch (garbage/failed decrypt), not an auth-tag error. The value is not persisted, so it must be reproducible at decrypt time.

  • algorithm (String, nil) (defaults to: nil)

    Optional per-field write-algorithm pin. When nil (default), writes use the registry's default provider -- the highest priority available, i.e. XChaCha20-Poly1305 whenever rbnacl is loaded, else AES-256-GCM. When set to a registered algorithm identifier ('aes-256-gcm' or 'xchacha20poly1305'), every write from this field is encrypted with that algorithm regardless of the default. Reads are always driven by the stored envelope's own algorithm, so pinning is safe to add, change, or remove without breaking ciphertext already at rest. This is the supported lever for a reader-before-writer format migration: deploy rbnacl fleet-wide so every node can read XChaCha20, while keeping writes pinned to 'aes-256-gcm' until all readers are confirmed capable, then remove the pin. An unregistered algorithm raises Familia::EncryptionError on first write.

  • kwargs (Hash)

    Additional field options



337
338
339
340
341
342
343
344
345
346
347
348
# File 'lib/familia/features/encrypted_fields.rb', line 337

def encrypted_field(name, aad_fields: [], algorithm: nil, **)
  @encrypted_fields ||= []
  @encrypted_fields << name unless @encrypted_fields.include?(name)

  # Add to field_groups if the group exists
  if field_groups&.key?(:encrypted_fields)
    field_groups[:encrypted_fields] << name
  end

  field_type = EncryptedFieldType.new(name, aad_fields: aad_fields, algorithm: algorithm, **)
  register_field_type(field_type)
end

#encrypted_field?(field_name) ⇒ Boolean

Check if a field is encrypted

Parameters:

  • field_name (Symbol)

    The field name to check

Returns:

  • (Boolean)

    true if field is encrypted, false otherwise



363
364
365
# File 'lib/familia/features/encrypted_fields.rb', line 363

def encrypted_field?(field_name)
  encrypted_fields.include?(field_name.to_sym)
end

#encrypted_fieldsArray<Symbol>

Returns list of encrypted field names defined on this class

Returns:

  • (Array<Symbol>)

    Array of encrypted field names



354
355
356
# File 'lib/familia/features/encrypted_fields.rb', line 354

def encrypted_fields
  @encrypted_fields || []
end

#encryption_infoHash

Get encryption algorithm information

Returns:

  • (Hash)

    Hash containing encryption algorithm details



371
372
373
374
375
376
377
378
379
# File 'lib/familia/features/encrypted_fields.rb', line 371

def encryption_info
  provider = Familia::Encryption.manager.provider
  {
    algorithm: provider.algorithm,
    key_size: provider.key_size,
    nonce_size: provider.nonce_size,
    tag_size: provider.auth_tag_size,
  }
end