NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Sentinel for Laravel

What it detects

Sentinel detects; database grants and row-level security prevent. The two complement each other — Sentinel tells you that a sealed value, a seal or the history behind it was changed outside the application, and refuses to build on it until someone signs the change off.

Actors

IdActorCapabilitiesIn scope
A1DB writerINSERT/UPDATE/DELETE on any table incl. sentinel_*; no code execution; no access to env, config or APP_KEY.yes
A2DB writer + snapshot restoreA1 + replace tables or the whole database with an older snapshot.yes (needs anchors for full coverage)
A3Network/client attackerReplays, alters or duplicates HTTP requests.yes
A4Authorised insiderUses the app normally (e.g. acknowledges changes).audited (actor + reason + changed attributes in the MAC’d ledger); authorisation is the host’s job (Gate hook)
A5Holder of signing keys / APP_KEY (database-driver keys) / RCE / config writeCan forge anything.out of scope

Detection matrix

“Window” means the time since the last checkpoint — scheduled every minute by default.

#AttackDB only (no anchor)With ≥ 1 external anchorStatus / finding
1Change a sealed columnyesyesTampered (+ changed attributes via field tags)
2Change rows feeding a computed valueyesyesTampered
3Copy values + seal from another row / model / seal name / tenant scope / app contextyes (domain separation)yesTampered
4Re-point a seal row (sealable_id, seal)yesyesTampered
5Delete a seal rowyesyesMissing (seal_deleted when ledger history exists)
6Insert an unsealed row (strict seal)yesyesMissing (never_sealed); a lenient seal → Unsealed (by definition not a finding)
7Restore an older row + its older seal rowyes, while the newer ledger entries existyesStale (newer_version)
8#7 + delete the newer ledger entriesyes if those entries were already checkpointed (CheckpointMismatch); no inside the windowyes outside the windowledger findings
9Restore the whole database (incl. ledger + checkpoints) to an older snapshotnoyes (AnchorAhead) for any rollback past the last anchored checkpointledger finding
10Delete the checkpoint tail (+ entries)noyesAnchorAhead
11Rewrite a ledger entry or checkpointyes (entry MAC / checkpoint MAC + chain)yesEntryInvalid, CheckpointInvalid, ChainBroken
12Delete a sealable row without a tombstoneyes (sentinel:verify --ledger: live ledger head, row gone)yesEntityDeleted
13Rewrite the stored algorithm / key_id / ring on a seal rowyes (the algorithm comes from the key; ring allow-list; HKDF info binds ring + kid + algorithm)yesAlgorithmMismatch / UnknownKey / Tampered
14Database-driver key rows: swap ring/kid/algorithm, replace a public key, plant an envelope minted through an encrypted castyes (an AEAD envelope under its own APP_KEY-derived key binds every field and its row)yesKeyIntegrityException → UnknownKey
15Database-driver key rows: restore an older envelope (un-revoke a key)no (an authentic old ciphertext) — mitigation: list the kid in SENTINEL_REVOKED_KEYS, which always winsno—
16Changes made and rolled back entirely inside the windownono— (shorten the checkpoint interval)
17Replay a signed HTTP requestyes (created/expires window + nonce de-duplication)—replayed, too_old, expired
18Alter the body/headers of a signed request, or add a method overrideyes (content-digest + signature; the executed method must be the signed one)—digest_mismatch, invalid_signature, malformed
19Duplicate a POST (retry storm, double click)yes (replay / 409)—idempotency
20Reuse an idempotency key with another payloadyes (422)—idempotency
21Reuse a single-use URLyes (atomic consume)—403

What it does not guarantee

  • Sentinel detects; it does not prevent. A row may be tampered with between two verifications.
  • Rows written through the query builder, raw SQL or withoutSealing() are unsealed or stale until re-sealed. Strict seals report them.
  • Database-driver keys are only as safe as APP_KEY. The config driver keeps keys out of the database — recommended for the default ring.
  • Without an external anchor, a full-database rollback (#9) and checkpoint truncation (#10) are undetectable.
  • Verification proves integrity relative to the last authorised seal, not that the authorised value was correct.
  • Computed values that read related rows are only as fresh as the last re-seal of the owning model.

Out of scope by design: confidentiality (use Laravel’s encrypted casts), an attacker holding the signing keys, APP_KEY (for database-driver keys), the deployed code or the configuration, and intercepting query-builder or raw SQL writes — they are detected afterwards.

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 crypto

By 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.