v1.2.0
Latest
Per-column adoption flow for apps that already use Laravel's encrypted cast on a field and now want that specific column protected by Sealcraft instead. This is a column-scoped migration, not a whole-app migration -- the rest of your app keeps using Crypt / encrypted as usual.
Short version
- Back up the database
- Install Sealcraft, configure a KEK provider, run migrations
- Write a one-off Artisan command that reads each encrypted column via
Crypt::decryptand re-assigns via theEncryptedcast - Run during a maintenance window
- Keep
APP_KEYaround for at least one full backup cycle in case of rollback
The migration command
use App\Models\Patient;
use Illuminate\Support\Facades\Crypt;
class MigratePatientsToSealcraft extends Command
{
protected $signature = 'migrate:patients-to-sealcraft {--dry-run}';
public function handle(): int
{
Patient::query()->chunkById(500, function ($chunk): void {
foreach ($chunk as $patient) {
$plain = Crypt::decrypt($patient->getRawOriginal('ssn'));
if ($this->option('dry-run')) {
$this->line("Would migrate patient {$patient->id}");
continue;
}
$patient->ssn = $plain; // re-assignment triggers the Encrypted cast
$patient->save();
}
});
return self::SUCCESS;
}
}
Model config during migration
For the migration window, temporarily mark the column with both behaviors disabled:
- Remove the Laravel
'encrypted'cast (don't let it try to decrypt the new Sealcraft ciphertext) - Add the Sealcraft
Encrypted::classcast - Use
getRawOriginal()in the migration command to read the raw column before the cast kicks in
After migration
- Rotate
APP_KEYonly after you have a clean backup that no longer containsAPP_KEY-encrypted data - Run
sealcraft:auditto confirm every row has a DataKey - Run a
sealcraft:rotate-kekat the end to confirm all DEKs are wrapped under the current KEK version
Rollback
Keep APP_KEY and its backups until you are certain the migration is complete. If rollback is needed:
- Revert the cast back to
'encrypted' - Restore from the last pre-migration backup
APP_KEYstill works because you kept it
This is why step one is "back up the database."