SIEM for Laravel
Outbox and semantics
Outbox and delivery semantics
The transactional outbox
dispatch() writes the outbox row on the caller's own database connection. Wrap it
in your transaction and the guarantee is simple:
- If your transaction commits, the
pendingrow is durably there and will be delivered (retried until it succeeds or is dead-lettered). - If your transaction rolls back, the row rolls back with it — no orphan delivery for an event that never really happened.
This is what makes delivery at-least-once rather than best-effort. A SIEM outage cannot roll back your business write, and your business write cannot be recorded without also recording the intent to stream it.
DB::transaction(function () use ($event, $streams) {
// business write ...
app(StreamDispatcher::class)->dispatch($event, $streams);
});
Be honest about the guarantees
Delivery is at-least-once and unordered. Do not assume exactly-once or ordered delivery — the design deliberately does not promise them:
- Duplicates are possible. A batch can be delivered and then fail to be marked
(worker crash, connection reset after the SIEM accepted it); the row stays
pendingand is retried. Two workers can also claim overlapping rows. - Order is not guaranteed. Batches, retries, and multiple streams interleave.
- The dedup key is the event id. Each
SiemEventcarries a stableid. Use it (not content equality) to deduplicate on the consuming side. Any downstream processing must be idempotent.
What this package does not do
- It does not de-duplicate for you — the consumer (or the SIEM) must.
- It does not guarantee ordering or exactly-once — see above.
- It does not itself produce a hash-chained, tamper-evident audit trail; that is
the job of the audit binding (in
cboxdk/laravel-id) that writes into this outbox. This package is the transport, not the ledger.