h-farm/laravel-encryptable
Transparently encrypt Eloquent model attributes at rest with a simple trait. Values are encrypted on save and decrypted on read, so your code keeps working with plain strings while the database stores only ciphertext. A drop-in Laravel package that's easy to configure.
Architecture fit
maize-tech/laravel-encryptable is a thin, trait-based attribute encryption layer for Eloquent: models opt in via a trait and an $encryptable property, and the package intercepts attribute get/set to encrypt on write and decrypt on read using Laravel's Crypt/encrypter contract. It is a column-level, application-side encryption tool — it complements, and does not replace, TLS in transit, database-at-rest encryption (TDE), or a KMS.
For our stack this is a good fit where the requirement is "specific PII fields must be unreadable to anyone with raw DB access" and where the fields are low-cardinality in queries (display-only). It is a poor fit where the requirement is "encrypt everything" or "search/filter/sort/aggregate on these fields" — the package produces non-deterministic ciphertext, so WHERE, LIKE, ORDER BY, GROUP BY, and UNIQUE constraints on encrypted columns stop behaving as expected unless we add our own blind-index columns.
It is also strictly a Laravel/Eloquent concern: any access path that bypasses the model (raw query builder, reporting/BI tools, direct DB sessions, other services sharing the schema) sees ciphertext. Architecture must treat the encrypted columns as owned exclusively by the Laravel app.
Integration feasibility
Low effort to adopt, moderate effort to adopt correctly.
fetch-metadata (CI hygiene only, no runtime impact).Technical risk
APP_KEY (or a configured custom key). Rotating APP_KEY without a re-encryption migration permanently destroys data. This is the classic failure mode for these packages and it deserves a documented runbook and, ideally, a dedicated key separate from APP_KEY.where() on an encrypted column — it just returns no rows. This is a correctness landmine that surfaces in production, not in code review.toArray()/toJson(), logging of model attributes, queue payloads, and Nova/Filament/Livewire bindings can all re-expose plaintext by design (that is the point of decryption) — needs explicit handling.VARCHAR(255) columns that "fit" today may truncate. Test with worst-case field content.Key questions
APP_KEY is interchangeable?Stack fit
Fits cleanly with a standard Laravel + Eloquent + MySQL/Postgres stack. No new runtime services, no daemon, no schema migration required for the package itself. It sits alongside our existing casts and accessors, so the integration point is the model layer — meaning adoption can be scoped per model rather than platform-wide.
It does not fit if we run multi-language services on the same tables, or if the encrypted columns feed a reporting pipeline. Those cases argue for a KMS/envelope-encryption design at the service boundary instead.
Migration path
composer.json and confirm the framework/PHP matrix resolves in a throwaway branch. Do not roll forward composer update on the main line first.$encryptable list, widen the columns, and ship behind a feature flag.Compatibility
composer.json in the tagged release.fetch-metadata major/minor bump does not change our workflow permissions or token scopes in CI.Sequencing
Run in this order, not in parallel: (a) dependency audit and lockfile update, (b) CI matrix expansion to include Laravel 13/PHP 8.5 if we want to claim support, (c) pilot model in staging with query audit, (d) key-management runbook written and reviewed, (e) production pilot, (f) expansion. Do not begin the pilot before the key rotation runbook exists.
Maintenance
Very low ongoing maintenance from the package side — no migrations, no config drift, no versioned schema. The real maintenance burden is ours: every new query touching an encrypted column is a potential silent bug, and every new developer needs to learn the constraint. Budget for a short internal doc and a code-review checklist item rather than ongoing engineering time.
Because 4.1.0 is a minor release, the incremental maintenance delta versus 4.0.0 is effectively zero. The upstream dependency bump is CI-only.
Support
Community-supported, single-maintainer OSS (maize-tech). No vendor SLA. Acceptable for a PII-at-rest hardening layer where the escape hatch is "we own our own wrapper trait" — but not acceptable as the sole control protecting regulated data without also relying on disk/TDE encryption and access controls.
Scaling
Negligible CPU cost per attribute operation — encryption is symmetric and local, and Eloquent already dominates request cost. It does not scale organizationally well in one specific way: encrypted columns cannot be indexed or filtered efficiently, so as data volume grows, any accidentally-shipped query on those fields becomes a full scan plus per-row decryption. Watch for that in slow-query logs after rollout.
Failure modes
APP_KEY rotation without migration → permanent, unrecoverable data loss. Highest-severity, highest-likelihood.Ramp-up
Half a day for a Laravel-fluent engineer to be productive; the package's surface is small. The ramp-up cost is concentrated in policy, not code: key ownership, which fields are in scope, and what query patterns are permitted. Those need a written decision before the first model is flipped, otherwise each team will make a different call and the encryption layer will be inconsistent across the estate.
How can I help you explore Laravel packages today?