Skip to main content

The delivery proof

Every pushed change notice writes an append-only legal_notices row (unless you turn durable_medium.proof off): who was informed, when, on which medium, the exact text sent, its hash, and whether the notice carried its mandatory content.

That is the evidence a durable-medium notice actually went out (CJEU C-375/15) — audit it like any model:

What the row proves, and since when

The row is written by a listener on NotificationSent, so it exists because the mail channel accepted the message — not because the sweep reached the subject in its own loop. Before 0.19.0 it was written at enqueue time, which meant a dead worker or a refusing transport produced a green run and a proof row for a notice nobody received. If your ledger predates 0.19.0, rows from before the upgrade attest that the notice was queued.

It is written only when the mail channel hands back the message it sent. The channel sends nothing to a subject without a mail address, or when a MessageSending listener cancels the message, and fires NotificationSent all the same. No row is written then: the attempt counts as failed, the subject is tried again, and after notifications.max_attempts it is reported as unreachable. Without a row, a deemed-consent window never closes on that subject.

A transport that refuses the message throws, Laravel reports that as NotificationFailed, and the attempt counts as failed in the same way. On a synchronous queue the exception surfaces inside legal-consent:dispatch-notices itself. There it ends that subject's notice on that channel and nothing more: the subject's other channels are still sent, and the sweep goes on with the next subject.

Any other error ends the run, a queue that cannot be reached for example. The sweep records a chunk's notices as on their way before it hands them over, and the records of the notices it had not handed over by then go back to what they were, so the next run serves those subjects rather than waiting out notifications.requeue_after_minutes and counting an attempt that never happened.

It does not attest receipt. NotificationSent means the mailer took the message; there is deliberately no delivered_at, because a column claiming more than the package can observe would be worse than no column.

use Pushery\LegalConsent\Models\LegalNotice;

$proof = LegalNotice::query()->where('document_key', 'terms')->latest('sent_at')->get();
$deficient = LegalNotice::query()->where('mandatory_content_ok', false)->exists(); // should be false

Retention​

The proof is append-only and holds personal data, so legal-consent:prune covers it on the same retention rule as the consent ledger: superseded and orphaned rows past the period go, while the newest notice per subject and document is kept — it is what proves the change behind their current standing was lawfully announced.

The prune sweep is off by default. See Retention.