All events live in RoundlyConsulting\Sentinel\Events and carry scalars only — types, ids, seal names, statuses, attribute names, key ids — never values, key material or request bodies, so they are safe for queued listeners and logs. Events tied to a write are dispatched after commit; the others synchronously. Sentinel::fake() dispatches none.
| Event | When | Payload |
|---|---|---|
ModelSealed | after commit | sealable type/id, seal, version, key id, SealEvent, actor |
TamperDetected | sync | sealable type/id, seal, VerificationStatus, reason, changed attributes, context, key id |
TamperAcknowledged | after commit | sealable type/id, seal, version, previous status, changed attributes, reason, actor |
SealRemoved | after commit | sealable type/id, seal, deleted/unsealed, previous status |
SealingSuspended | sync | reason, actor |
KeyGenerated, KeyRotated, KeyRevoked, KeyRetired | after commit | ring, key id, algorithm (+ previous key id / reason and actor) |
KeyImported | after commit | ring, key id, algorithm, whether it signs, actor |
KeyIntegrityViolated | sync | ring, key id, driver |
LedgerCheckpointed | after commit | connection, seq, entries, root |
LedgerIntegrityViolated | sync | connection, LedgerFindings |
AnchorPublishFailed | sync | anchor, connection, seq, error |
IdempotentRequestReplayed, IdempotencyRejected | sync | method, route, status (+ IdempotencyRejection) |
HttpSignatureRejected | sync | SignatureRejection, key id, method, path |
Helpers
| Method | On | Returns |
|---|---|---|
model(): ?Model | ModelSealed, TamperDetected, TamperAcknowledged, SealRemoved | The model, morph-map aware, loaded with verify-on-retrieve suspended, soft-deleted rows included; null after a hard delete. |
changedColumns(): list<string> | TamperDetected, TamperAcknowledged | Changed columns without the a: prefix — ['amount']; empty when unknown. |
changedComputed(): list<string> | TamperDetected, TamperAcknowledged | Changed computed fields without the c: prefix — ['lines']. |
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Sentinel\Events\LedgerIntegrityViolated;
use RoundlyConsulting\Sentinel\Events\TamperDetected;
Event::listen(function (TamperDetected $event): void {
if ($event->context->value !== 'retrieve') {
Notification::route('slack', config('services.slack.security'))
->notify(new DataTamperingAlert($event->sealableType, $event->sealableId, $event->seal, $event->status));
}
$invoice = $event->model(); // loads even the tampered row
$columns = $event->changedColumns(); // ['amount']
});
Event::listen(fn (LedgerIntegrityViolated $event) => report(new RuntimeException(
"Ledger integrity violated on {$event->connection}: ".count($event->findings).' finding(s)',
)));Every finding is also logged at warning level to verification.log_channel (names only), so a log alert works without a listener.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.