v1.2.0

Every DEK is bound to an encryption context -- a (type, id) pair plus optional scalar attributes. The context controls which DEK decrypts which row, and gets cryptographically bound to the ciphertext as AAD.

What canonicalization guarantees

The context is serialized to stable UTF-8 bytes:

  • NFC-normalized strings
  • Byte-sorted keys
  • Escaped separators
  • 4KB size cap

This means the same logical context always produces the same bytes, regardless of array ordering or subtle Unicode variants. A cross-context decrypt attempt -- intentional or accidental -- fails authentication.

Configuration

Configure a model's context with the $sealcraft array:

protected array $sealcraft = [
    'strategy' => 'per_row',     // 'per_group' (default) | 'per_row'
    'type'     => 'patient',     // context type name
    'column'   => 'patient_id',  // per_group: id column; per_row: row-key column
];

Only keys that differ from defaults need to be set. See each strategy page for its specific config shape.

Strategies

Sealcraft supports three context strategies. Pick per model.

  • Per-group -- one DEK per tenant / user / patient. Default. Best for multi-tenant SaaS.
  • Per-row -- one DEK per record. Best for vault-style rows where each row is an independent security boundary.
  • Delegated context -- child records share a parent's DEK. The HIPAA primitive for one-shot crypto-shred.

Modifier

  • Per-column override -- route one attribute to a different context than the rest of the model via cast parameters. Layers over any strategy.

Changing context

Changing the context column on an existing row is a security-sensitive event. Sealcraft's default behavior is to auto-reencrypt on save:

$patient->user_id = $newOwner->id;
$patient->save();  // auto-decrypts with old DEK, re-encrypts with new DEK

Two events fire:

  • ContextReencrypting (pre, cancellable by returning false)
  • ContextReencrypted (post, for audit log)

To require an explicit migration command instead, set:

SEALCRAFT_AUTO_REENCRYPT=false

Then any uncoordinated context change throws InvalidContextException. Run sealcraft:reencrypt-context when you mean to move a row between contexts. Wire the events to your SIEM regardless.