Implementing Page-Level User Interaction Recording and Replay for Insurance Compliance Using rrweb, Puppeteer, and FFmpeg
This article details a compliance‑driven solution that records, replays, and merges user interaction videos across multiple insurance web pages by leveraging rrweb for event capture, Puppeteer for automated playback, and FFmpeg for video stitching, while addressing performance, storage, and privacy challenges.
On June 30, 2020, the China Banking and Insurance Regulatory Commission issued a notice requiring internet insurance sales to be fully traceable, meaning every page view and user action must be recorded and stored for later verification, preferably as video evidence.
Implementation Challenges
The requirement calls for page‑level recording of the entire user journey, which cannot be satisfied by simple button‑level event tracking. Traditional click‑stream analytics are insufficient because regulators demand a complete visual replay of the page interactions.
Technical Components
rrweb – a library that records DOM mutations, mouse movements, scrolls, inputs, and other interactions as a stream of events that can later be replayed.
Puppeteer – a Node.js API for controlling Chrome, used to automate the replay of rrweb events and capture the resulting video.
FFmpeg – a multimedia toolkit employed to concatenate the individual video fragments generated from each page into a single compliance video.
Technical Implementation
Data Collection
rrweb records user behavior on each visited page and stores the JSON payload on the server at a configurable frequency.
Multiple pages generate multiple data fragments, all of which are linked to the insurance order.
When the order is successfully created, the fragments are associated with that order for later processing.
Video Recording
On the server, Puppeteer launches a headless (or visible) browser, replays the rrweb event stream for each fragment, and records the playback.
After each fragment finishes, the video file is downloaded and stored.
When all fragments are recorded, FFmpeg merges them and transcodes the result into a single compliance video.
Video Upload
The final video is uploaded to Alibaba Cloud OSS.
A callback notifies the insurer to download and archive the video.
Core Code Snippets
Lock‑free High‑Concurrency Storage
To handle near‑real‑time data ingestion, the solution stores large JSON fragments in MongoDB while keeping indexing information in MySQL. The pseudo‑code below shows a double‑check pattern with a distributed lock to avoid duplicate inserts.
fetch mysql data
if not exist
// avoid duplicate initialization, compete for distributed lock
lock userId
fetch mysql data
// double check
if not exist
insert mongo document generate objectId
insert mysql data
return
// mysql index exists, update mongo document
fetch mongo document with objectId
document add data
update mongo document with objectIdImprovements include reducing lock contention, separating MongoDB and MySQL writes, and using MongoDB $push to make the update atomic.
Puppeteer‑Driven Recording
The following snippet launches Chrome, navigates to the playback page, waits for a custom finish flag, triggers rrweb‑player to stop recording, and finally downloads the video.
const options = {
ignoreHTTPSErrors: true,
headless: false,
defaultViewport: null,
args: [
'--enable-usermedia-screen-capturing',
'--allow-http-screen-capture',
'--auto-select-desktop-capture-source=yqg-puppetcam',
'--load-extension=' + __dirname,
'--disable-extensions-except=' + __dirname,
'--disable-infobars',
`--window-size=${width},${height}`,
],
}
const browser = await puppeteer.launch(options);
const [page] = await browser.pages();
await page._client.send('Emulation.clearDeviceMetricsOverride');
await page.goto(playUrl, {waitUntil: ['load', 'domcontentloaded']});
await page.setBypassCSP(true);
await page.waitForSelector('.finish-flag', {timeout: 0});
await page.evaluate(filename => {
window.postMessage({type: 'SET_EXPORT_PATH', filename}, '*');
window.postMessage({type: 'REC_STOP'}, '*');
}, exportname);
await page.waitForSelector('html.downloadComplete', {timeout: 0});
await page.close();
await browser.close();Encountered Issues
rrweb does not capture iframes or PDFs, which are common for insurance agreements. The workaround converts PDFs to image sequences and treats iframe content as plain text.
When navigating between pages, rrweb must be re‑initialized to avoid missing input values; this is currently handled in a Vue‑Router setup.
Missing event data can cause playback stalls, so error‑compensation mechanisms are added to keep the pipeline robust.
Performance Impact
On the most complex insurance page, rrweb’s emit method takes less than 100 ms on initial load and only a few milliseconds for subsequent DOM changes, which is well within acceptable limits and does not noticeably affect user experience.
Extending to Live Streaming
By continuously feeding rrweb data to the player, a near‑real‑time “live” view of the user’s actions can be provided to insurance advisors, enabling them to guide users during the purchase process. Sensitive fields are masked using the blockClass and custom insertStyleRules options.
new rrwebPlayer({
target: vm.$refs['rrweb-player'],
props: {
events: data,
insertStyleRules: [
`.hide-info-in-live {${this.hideClass()}}`
],
}
});
hideClass() {
const input = document.createElement('input');
const style = window.getComputedStyle(input);
if (style.webkitTextSecurity !== undefined) {
return 'text-security: disc!important;-webkit-text-security: disc!important;-moz-text-security: disc!important;';
} else {
return 'opacity: 0!important;';
}
}Conclusion
After evaluating several alternatives, the team chose rrweb‑based event capture combined with Puppeteer‑driven replay and FFmpeg stitching because it balances performance, video quality, and regulatory compliance. The solution not only satisfies the new traceability mandate but also provides valuable capabilities for debugging, process replication, and real‑time assistance.
The open‑source community around rrweb has already seen broad adoption in the insurance sector, and the authors hope the project continues to grow, driving further innovation at the intersection of insurance and technology.
Yang Money Pot Technology Team
Enhancing service efficiency with technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.