Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Session Replay Laravel Package

packstub/session-replay

Packstub Session Replay captures user session activity in Laravel applications and lets you replay those sessions step by step to reproduce bugs and diagnose issues. Simple to install and configure, it gives developers a clear view of what users experienced without guesswork.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • Conventional Laravel package shape: service provider, SessionReplay facade, config, migrations, Blade views, global + route middleware, an artisan command, and a provider stub published by session-replay:install. A Laravel-fluent team can reason about it without a specialist.
  • Deliberate layering: the recording/ingest/storage core imports nothing from Filament\ or Livewire\; panel UI is a separate sibling package. We can adopt the free package alone and serve the Blade viewer behind a gate, which is the right shape for an engineering-first rollout. The panel becomes a later, separable decision.
  • Sound server boundary: the browser sends pre-computed index metadata alongside events, so PHP never parses rrweb payloads. CPU cost stays flat per batch and a class of parser bugs disappears. That is a meaningful operating-cost property, not just a code-style preference.
  • Ingest identity is a ContextToken (user, tenant, impersonator, properties) signed with the app key at render time, so uploads need no session, cookie, or CSRF token. Correct for a fire-and-forget uploader; it does put the app key inside the ingest trust chain.
  • Access control is enforced as a data concern: AuthorizeViewer wraps viewer and data routes, with the recording bound as the gate argument. This is the single most important property to verify in our own review, because it is what lets us ship replay to support without shipping raw screens company-wide.
  • New in beta.5 — navigation as a snapshot boundary. A wire:navigate swap is now recorded as a fresh snapshot (recorder pauses on livewire:navigating, resumes on livewire:navigated, with a 5s fallback) instead of one diff that re-adds every node. Architecturally this is the right call: it aligns the recording model with how Livewire actually swaps the DOM, and it makes a recording navigable page-by-page in the player. It also means the chunk model is now one chunk row per page for navigate-driven apps, not just per page load — a storage-and-schema consequence we must model, not just a UX nicety.
  • Second change is a correctness win with a storage side-effect: stylesheet text arriving after serialization (late <link>, or one appended by wire:navigate) is now hashed and stored once rather than inlined on every page view. The vendor reports a Lab page record going 25 KB → 17 KB. Treat that as a directional claim to re-measure on our markup, not a planning number.
  • Note what did not change: no new config keys, no new routes, no middleware signature change, no CSP story. The release is recorder-side. That keeps the integration footprint stable and the rollback story trivial.

Integration feasibility

  • Path is unchanged: install → publish provider → define viewSessionReplay → configure a disk and the storage.connection database → schedule PruneCommand. Roughly half a day for a Laravel-fluent engineer on staging, with the caveats below. No front-end build or asset publishing; resources/dist is committed and served by ScriptController.
  • Front-end footprint is still one script tag from SessionReplay::recorder() carrying config and the signed token. It implies an inline script, so it interacts with our CSP — confirm a nonce or hash path in the spike, before committing to a date.
  • The dist bundle changed. Everything else being equal, our supply-chain diff at pin time is now non-trivial: new pause/resume logic, a setTimeout fallback, console-wrapper preservation. Budget the review, and re-check that the recorder's masking path is untouched by the navigation pause (our read is that it is, but "we read it" is not acceptance criteria).
  • Hard floor unchanged: PHP 8.3+, Laravel 12 and 13 only. Any estate on Laravel 10/11 or PHP 8.2 is blocked until upgraded. Treat that as a scoping gate at intake.
  • Status is now explicitly pre-1.0. beta.5 confirms the package is still in beta, so the upgrade-stability argument in our previous draft ("1.x freezes config keys, migration names, route names, chunk format") applies only after 1.0. Until then, recorder and chunk-format changes can land in any beta. Plan the integration as "pin a beta, accept re-verification on every bump," not "pin and forget."

Technical risk

  • Privacy dominates the risk register, not code quality. Replay captures the DOM. Masking/stripping lives in resources/js/lib/process.js and rrweb options, with a stated policy that privacy defaults only tighten within a major. That policy is quotable to security review but does not substitute for enumerating recorded routes, confirming maskAllInputs and text-masking defaults, and maintaining a hard exclusion list (auth, checkout, admin, health checks).
  • Unauthenticated ingest surface. Only a signed token is trusted. We must establish token lifetime and replay resistance, session-scoped rate limits (the design rejects IP keying), and the real gzip inflation ceiling. Single-inflate-with-ceiling is the right zip-bomb mitigation; verify the number.
  • Gate misconfiguration equals total disclosure. A permissive viewSessionReplay exposes every recording. Deny-by-default plus an integration test asserting 403 on every data route for an unprivileged user is mandatory acceptance criteria.
  • Governance transfers to us. Disk + database means we own encryption at rest, retention, DSAR/deletion, and viewer access auditing. PruneCommand and ReplaySessionStarted ship; an access audit trail still does not appear to, so gate logging is ours to build.
  • Beta churn is now the top technical risk after privacy. The recorder's chunk model changed materially in this release (page-granular chunks for navigate; hashed late stylesheets). Playback compatibility is claimed for older recordings — verify against a pre-beta.5 recording we make ourselves, because that is the exact scenario of "we upgrade mid-pilot and last week's tickets stop playing."
  • Navigation is now a capture state machine. A pause with a 5s fallback means there is a window where the recorder is not recording. If livewire:navigated never fires and the timeout path is slow, or if a third-party script blocks the swap, we get a silent gap. Console markers and SessionReplay.mark() survive, so the gap is diagnosable — but only if support knows to look for it. Add "recording gap around navigation" to the triage runbook.
  • Storage model shifts to per-page rows for Livewire apps. Chunk-row count now scales with pages visited, not batches. Cheap rows, but it changes row-count planning, prune duration, and the granularity at which we can delete a single page from a recording — which is incidentally good for targeted deletion/DSAR work, and worth calling out as a compliance upside.
  • Shared-snapshot savings do not apply to navigate-reached pages. Only a true page load can be served from snapshots.share_routes. Apps that lean on wire:navigate get less deduplication benefit than the config name implies; size the disk plan off measured data, not off the feature flag.
  • Maturity and bus factor. Still low-adoption, single-maintainer OSS — but the cadence is real: changelog-per-change, Pest/Pint/node --test coverage, and this release is validated against a cross-package Livewire navigation run. That is a positive maintenance signal, not a vendor SLA. MIT remains the fork escape hatch.
  • Supply chain. resources/dist is committed and executes on every recorded page from our domain. Diff it against resources/js at every pin, not just the first, and pin the rrweb version and license.
  • Version-skew hazard. Free package and Filament plugin share the Packstub\SessionReplay\ namespace via merged PSR-4 directories. This release's validation was in the Filament lab — one more reason to pin and upgrade the pair together if both are installed.
  • Release metadata. beta.5 resolves the earlier anomaly: the package is not stable, it is a beta with an active line. Re-confirm the tag and Packagist state at pin time, and record the exact beta in the risk log.

Key questions

  • What are the default masking settings, and can we enforce organisation-wide stricter values via config without patching the recorder?
  • What is the ContextToken lifetime — expiring, single-use, or bounded to a session id?
  • Is there an access audit trail for who watched which recording, and if not, what is the cheapest hook for one?
  • What is the exact gzip inflation ceiling, and what is the batch rate limit per session?
  • Does the emitted script tag support a CSP nonce or hash, and does the player load anything from a third-party origin?
  • Which routes are recorded by default, and is there a first-class exclusion config or only recordWhen?
  • New: what is recorded during the livewire:navigating → livewire:navigated window, and what exactly is lost if the 5s fallback fires — is there a marker in the timeline indicating a gap?
  • New: does the 5s fallback timer interact with a second navigation arriving mid-window, and can it leave the recorder paused permanently?
  • New: is the navigation path tested outside the Filament lab — i.e. plain Livewire 3 with our own layouts and third-party scripts wrapping console?
  • New: is the chunk schema/format change in beta.5 additive, or does it require a migration of existing rows — and are recordings made on earlier betas guaranteed playable by the beta.5 viewer and future 1.0 viewers?
  • New: do per-page chunk rows change what PruneCommand can target, and is there a supported path for deleting a single page or a single recording on DSAR request?

Integration Approach

Stack fit

  • Requires Laravel 12/13 on PHP 8.3+. Fit is binary: compliant estates can adopt in days; non-compliant estates get a prerequisite upgrade, which changes the roadmap slot.
  • Best fit is a server-rendered Laravel monolith, and materially better now for Livewire apps. The recorder detects Livewire, marks failed Livewire requests, and as of beta.5 records wire:navigate transitions as clean per-page snapshots. That is exactly the class of bug our support queue struggles to reproduce, and the navigate path is where Livewire apps actually break.
  • Partial fit extends to Livewire-driven "SPA-feel" navigation — but only that. Poor fit remains for genuine SPA front ends (Inertia/React/Vue with client routing), native mobile, and anything needing funnel/heatmap analytics. Say that out loud in the proposal so replay doesn't get scoped into growth work it cannot do.
  • The Filament panel remains a separate decision, and the shared-namespace coupling means it is a joint decision, not a later one, if we install both.

Migration path

  • Nothing to migrate from an existing system. It is additive: new tables, new disk paths, new routes, one script tag. No existing data model changes, which keeps the change reviewable and reversible by removing the middleware and the tag.
  • In-package upgrade path is the live question, not legacy migration. Moving from an earlier beta to beta.5 changes recorder output and playback handling; the vendor states pre-existing recordings keep playing as they are. Verify that on our own pilot data before the first bump, and make "upgrade the package" a scheduled, tested change rather than a composer update side-effect.
  • Backfill is neither possible nor needed; recordings start at deploy. Value accrues after the first week of traffic, not on day one.
  • The one-way door is data retention: once recordings exist they are our data, subject to our retention policy and any DSAR obligations. Decide the retention window before the first recorded session, not after. beta.5 makes per-page deletion more plausible as an answer here — confirm it before relying on it.

Compatibility

  • CSP remains the likeliest blocker — inline script plus player assets served from our own domain. Confirm nonce/hash support in a spike.
  • If we run the Filament plugin too, the two packages must be pinned and upgraded as a pair because of the shared namespace; beta.5 was validated in that lab, so a free-package-only install is the less-travelled path and deserves the extra test pass.
  • Cross-version playback is a compatibility axis we did not previously track: recordings span the beta we recorded on and the beta we play them back on. Add a smoke test with a fixture recording made on the pinned version and replayed after each upgrade.
  • Languages ship for en, de, es, ro, ru; a new string is a contribution in all five, not a fork. If our viewer is not in those languages, budget for translation as an upstream contribution.
  • Verify the pinned rrweb version against the licenses we accept and any recorded-page third-party asset policy.

Sequencing

  1. Spike (1–2 days): install on a workbench/internal app, confirm CSP path, dump the default masking config, verify 403 behaviour on data routes. If the target app is Livewire, exercise a wire:navigate flow end to end and inspect the resulting chunk rows and page boundaries.
  2. Privacy review gate: enumerate recorded routes, publish an exclusion list, get written sign-off on masking values and retention. No production traffic before this.
  3. Storage baseline: measure real bytes-per-page and chunks-per-session on our own markup, with and without wire:navigate, and re-derive the disk plan. Do not carry the vendor's 25 KB → 17 KB Lab figure into a capacity commitment.
  4. Internal pilot: one internal admin app or internal-only tenant, gate limited to the engineering/support group. This is where the blast radius of a beta dependency is a few engineers.
  5. Support workflow integration: wire the replay link into exception reports and support tickets, using the Context correlation already in log lines. This is the step that produces the measurable outcome.
  6. Evaluate panel: only if ops already lives in Filament, add packstub/filament-session-replay pinned to the same version.
  7. Widen: gate expansion to a broader support role, with audit logging in place first — and only after the pinned upgrade path has been exercised at least once without playback regressions.

Operational Impact

Maintenance

  • We take on: a filesystem disk, a table set, a retention policy, a scheduled PruneCommand, and periodic dependency bumps. Small, well-understood surface for a Laravel team, but permanently ours — there is no vendor to escalate to.
  • Pin the exact beta and own the upgrade cadence. Pre-1.0, chunk format and recorder behaviour can change between minor betas, as this release demonstrates. Each bump is a reviewed change with a replay smoke test, not a routine update. Once 1.0 lands, re-evaluate whether 1.x's stated freezing of config keys, migration names, route names, and chunk format actually holds, then simplify the process.
  • New moving part to own: the navigation pause/resume and its 5s fallback. It is a small state machine in the browser, but it now sits between the user and the recording, and its failure mode is a gap rather than an error.
  • If we ever fork, we own the fork. Budget it as a real cost in the risk log, not a theoretical option.

Support

  • No commercial support, no security questionnaire answered by a vendor, no named maintainer with a response commitment. Our own security review must produce the answers — extra TPM work up front, and an argument for piloting on a low-sensitivity surface first.
  • Counterweight improved slightly: release cadence is active and the changelog is per-change and specific, which raises confidence that defects get addressed. It still does not constitute a commitment. Plan for "an engineer reads the source" as the escalation path and staff accordingly.
  • Support triage gains a new item: distinguishing a real gap in a recording from a bug the user experienced. The recorder preserves console messages and SessionReplay.mark() across a navigation swap, which is our hook — write the runbook line now.
  • Support the package earns us: the "why did this break?" workflow. The pitch to the business is support time saved, so define the measurement before the pilot — tickets with replay attached vs. tickets without, and time-to-reproduce.

Scaling

  • Disk-plus-index is honest and simple; it is not a warehouse. Volume planning is a storage lifecycle exercise: bytes per session × sessions × retention. Revise the model for chunk-per-page rows on Livewire apps — row count scales with pages visited, and PruneCommand duration scales with rows, not just bytes.
  • The dedup fix reduces bytes for pages with late-arriving stylesheets, but snapshots.share_routes savings do not extend to navigate-reached pages. Net effect on disk is app-specific; measure it.
  • No hot-tier querying, no session search at scale, no funnels or retention curves. Route those requests elsewhere explicitly, or replay will be asked to become an analytics product it is not.
  • Disk growth remains the binding constraint, not database row count — but row count is now the thing that makes pruning slow. Monitor both; make prune a monitored job, not a hope.

Failure modes

  • Gate too permissive → total disclosure of every recording. Highest-severity failure. Mitigation: deny-by-default, 403 assertion tests on all data routes, audit logging.
  • Masking regression → sensitive field captured in a DOM snapshot, then retained on disk. Mitigation: exclusion list, pinned version, re-review of masking config on every upgrade (a recorder change is exactly when this risk peaks), and a documented process for purging a recording.
  • Navigation capture gap → events between pause and resume lost, or the recorder stranded if the 5s fallback fires and livewire:navigated never arrives. Mitigation: verify marker/console continuity in the spike, test the timeout path deliberately, document the symptom in the triage runbook.
  • Cross-version playback failure → recordings from an older beta don't render after an upgrade, which looks like data loss to support. Mitigation: fixture-recording smoke test on every bump; confirm the vendor's backward-compatibility claim on our own data.
  • Disk fills → ingest failures and possibly wider app impact if the disk is shared. Mitigation: dedicated disk, alerting at threshold, prune monitored.
  • Zip bomb / oversized upload → mitigation is single-inflate-with-ceiling; verify the ceiling and test it.
  • Version skew between core and Filament plugin → class collisions. Mitigation: pin together, upgrade together, or don't install both.
  • Beta churn → an upgrade lands mid-pilot and changes behaviour nobody scheduled for. Mitigation: pin, stage, smoke test, and time-box the pilot to a single known beta.
  • Maintainer abandonment → no new releases, no fixes. Mitigation: MIT fork rights, pinned version, and a documented owner inside our team.

Ramp-up

  • Engineer: half a day to install and configure on staging; one to two days including the CSP spike, the 403 test harness, and — for a Livewire target — the wire:navigate and cross-version playback checks.
  • Privacy/security review: the real schedule driver. Budget a week of calendar time for route enumeration, masking sign-off, and retention policy. Do not let it become the critical path discovered at launch.
  • Support users: near-zero. The viewer is a player page with a timeline and markers; beta.5 makes it better — jumping to any page without replaying prior ones is a genuine triage speedup on Livewire apps — but training is still "click the replay link in the ticket."
  • TPM: own the go/no-go on the privacy gate, the retention window, the pilot success metric, the version pin, and now the upgrade cadence for a pre-1.0 dependency. Those five decisions are the whole job — everything else is an engineer's afternoon.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk