OpenReplay, nagrywanie sesji na własnym serwerze
OpenReplay nagrywa sesje użytkowników w przeglądarce i odtwarza je razem z dziennikiem konsoli, zapytaniami sieciowymi, stanem magazynu i metrykami wydajności. Tracker jest na licencji MIT, ale serwer już nie: domyślną licencją repozytorium jest AGPL v3, a katalog ee chodzi na osobnej licencji zastrzeżonej, której treści w repozytorium nie ma.
Co OpenReplay nagrywa, a czego nie nagrywa
Nagranie sesji w OpenReplay nie jest filmem. Tracker rejestruje początkowy stan drzewa DOM, a potem strumień jego zmian, zdarzenia myszy, przewijanie, zmiany rozmiaru okna i wpisy do pól formularzy. Odtwarzacz odbudowuje z tego stronę w przeglądarce operatora. Konsekwencja jest praktyczna: nagranie waży ułamek tego, co ważyłoby wideo, ale zależy od arkuszy stylów i zasobów, które muszą być dostępne w chwili odtwarzania. Jeśli obrazek zniknął z serwera, w odtworzonej sesji będzie dziura.
Do tego dochodzą cztery strumienie diagnostyczne. Pierwszy to konsola przeglądarki razem z nieobsłużonymi wyjątkami; tracker rozbiera stos wywołań paczką error-stack-parser-es w przypiętej wersji 2.0.1. Drugi to zapytania sieciowe, przechwytywane przez opakowanie fetch, XMLHttpRequest oraz opcjonalnie wskazanych instancji Axiosa. Trzeci to metryki wydajności, liczone przez zależność web-vitals w zakresie ^5.3.0. Czwarty to stan magazynu, dostępny przez osobne wtyczki do Reduxa, Vuexa, NgRx i Zustanda.
Osobną funkcją jest co-browsing, czyli podgląd i przejęcie kontroli nad żywą sesją. Siedzi we wtyczce @openreplay/tracker-assist, która ciągnie socket.io-client w zakresie ^4.8.1 oraz fflate. To nie jest część rdzenia trackera i trzeba ją zainstalować świadomie.
Czego OpenReplay nie robi: nie zbiera zdarzeń serwerowych, nie jest magazynem logów, nie liczy retencji kohort. Odpowiada na pytanie „co widział ten jeden użytkownik, zanim zgłosił błąd", a nie na pytanie „jak zachowuje się populacja". To rozróżnienie wraca w sekcji o porównaniu z innymi narzędziami.
Licencja serwera to AGPL v3, licencja trackera to MIT
To jest punkt, na którym najłatwiej się pomylić, bo jedno repozytorium zawiera trzy różne reżimy licencyjne naraz.
Plik LICENSE w korzeniu repozytorium openreplay/openreplay zaczyna się od linii „Copyright (c) 2021-2025 Asayer, Inc dba OpenReplay" i nie jest ani czystym MIT, ani czystym Apache. To dokument zbiorczy o długości 694 linii, który we wstępie wymienia cztery reguły. Zawartość katalogu ee/ podlega licencji zdefiniowanej w pliku ee/LICENSE. Komponenty zewnętrzne zachowują swoje licencje. Niektóre katalogi są na MIT. Wszystko poza tym domyślnie wpada pod GNU Affero General Public License w wersji 3. Dalej w pliku wklejony jest pełny tekst MIT, a po nim pełny tekst AGPL v3. W sprawach spornych plik podaje adres license@openreplay.com.
Dwie rzeczy w tym opisie są nieprecyzyjne i sprawdziłem obie. Po pierwsze, wstęp odsyła do ee/LICENSE, a takiego pliku w gałęzi głównej nie ma: raw.githubusercontent.com zwraca dla niego 404, natomiast ee/LICENSE.md zwraca 200. Odnośnik w głównej licencji wskazuje na nieistniejącą nazwę pliku. Po drugie, zdanie o katalogach na MIT nie wymienia ani jednego katalogu, a jedynym plikiem licencyjnym w podkatalogu, jaki udało mi się znaleźć, jest właśnie ee/LICENSE.md. Katalog tracker/ w repozytorium istnieje, ale nie ma w nim pliku LICENSE. Innymi słowy: obietnica MIT dla części kodu nie jest w repozytorium przypisana do konkretnych ścieżek.
Plik ee/LICENSE.md ma kilka zdań. Nazywa się „The OpenReplay Enterprise license", nosi notę „Copyright (c) 2022 Asayer, Inc." i mówi tyle, że aby licencjonować edycję Enterprise i korzystać z jej dodatkowych funkcji, trzeba zgodzić się na OpenReplay Enterprise License Agreement, a w tym celu należy napisać na sales@openreplay.com. Samej umowy w repozytorium nie ma. Kod z katalogu ee/ jest więc publicznie widoczny, ale warunki jego użycia są poza repozytorium i wymagają kontaktu handlowego. Taki układ, rdzeń otwarty plus katalog zastrzeżony w tym samym drzewie, spotyka się także w SuperTokens i w Dokployu.
Trzecie źródło, czyli zawartość opublikowanej paczki, jest w przypadku trackera spójne. W archiwum @openreplay/tracker w wersji 18.1.3 leży plik LICENSE o długości dziewiętnastu linii, zawierający tekst MIT z notą „Copyright (c) 2022 Asayer, Inc". Słowo „AGPL" nie występuje w tym pliku ani razu. Pole license w rejestrze npm ma wartość MIT. Paczka zawiera realny kod, nie tylko metadane: 176 plików, 5 360 028 bajtów po rozpakowaniu. Deklaracja, plik w paczce i faktyczna zawartość zgadzają się ze sobą.
Dla agencji rozstrzygające jest jedno pytanie: czy wolno oferować OpenReplay klientom jako usługę hostowaną. AGPL v3 tego nie zabrania, w odróżnieniu od licencji typu SSPL czy BSL. Sekcja 13 AGPL nakłada natomiast obowiązek, o którym łatwo zapomnieć: jeśli udostępniasz zmodyfikowaną wersję programu przez sieć, musisz zaoferować użytkownikom tej instancji dostęp do odpowiadającego jej kodu źródłowego. Uruchomienie niezmodyfikowanego OpenReplaya dla własnej firmy nie rodzi żadnych zobowiązań publikacyjnych. Uruchomienie wersji z własnymi łatkami jako usługi dla klientów już tak. I niezależnie od tego katalog ee/ zostaje poza zasięgiem bez umowy z producentem.
Wersje, paczka npm i stan rodziny wtyczek
Bieżąca wersja trackera to 18.1.3, opublikowana 12 sierpnia 2026 roku. Poprzednie wydania to 18.1.2 z 29 lipca, 18.1.0 z 2 lipca i 18.0.0 z 15 kwietnia 2026. W rejestrze jest 357 wersji, a obok znacznika latest żyją także beta wskazujący na 19.0.0-beta.0 oraz legacy wskazujący na 17.2.10. Serwer numeruje się osobno: ostatnie wydanie oznaczone tagiem w repozytorium to v1.27.0 z 20 sierpnia 2026 roku. Kanał wydań jest przy tym zaśmiecony automatycznymi tagami main-backup-<data>, których w ostatnich dwóch tygodniach było kilkanaście; szukając prawdziwej wersji serwera, trzeba je odfiltrować.
Sam tracker jest cięższy, niż sugeruje kategoria „skrypt analityczny". Zbudowany moduł ESM dist/lib/index.js ma 431 305 bajtów, po kompresji gzip około 98 kilobajtów. To rozmiar porównywalny z niejedną biblioteką interfejsu i przy budżecie wydajnościowym trzeba go wliczyć.
# rdzeń trackera, wydanie z 12 sierpnia 2026
npm install @openreplay/tracker@18.1.3
# co naprawdę deklaruje opublikowana paczka
npm view @openreplay/tracker@18.1.3 license dependencies dist.unpackedSize
# wtyczka do co-browsingu, jedyna z rodziny wydana w 2026 roku
npm install @openreplay/tracker-assist@11.0.20
# zakres zależności równorzędnej sprawdzony przed instalacją
npm view @openreplay/tracker-assist@11.0.20 peerDependenciesZakresy zależności równorzędnych w tej rodzinie sprawdziłem po kolei i tutaj akurat jest czysto. Każda wtyczka deklaruje @openreplay/tracker przedziałem otwartym w górę: >=15.0.0-0 w tracker-assist, >=14.0.0 w tracker-graphql, >=13.0.0 w tracker-redux, >=12.0.0 w tracker-zustand, >=3.4.8 w tracker-vuex i tracker-ngrx. Żadna z nich nie wejdzie w konflikt z rdzeniem 18.1.3. To sytuacja odwrotna do tej, którą widać na przykład w rodzinie Opika, gdzie pakiety integracyjne przypinają rdzeń zakresem sprzed bieżącej wersji głównej.
Prawdziwy problem tej rodziny leży gdzie indziej i nazywa się porzuceniem. @openreplay/tracker-profiler stoi na 3.0.1 z 13 września 2022 roku, @openreplay/tracker-ngrx na 3.4.9 z tego samego dnia, @openreplay/tracker-vuex na 4.0.3 z 27 września 2022, a @openreplay/tracker-axios na 3.6.2 z 16 grudnia 2022. Ta ostatnia deklaruje zależność równorzędną axios: "0.x", podczas gdy bieżący Axios ma numer 1.19.0. Instalacja tej wtyczki w projekcie z aktualnym Axiosem kończy się ostrzeżeniem o niespełnionej zależności równorzędnej albo, przy npm install bez flag, błędem rozwiązywania drzewa. Przechwytywanie Axiosa działa dziś przez opcję network.axiosInstances w rdzeniu, więc osobna wtyczka nie jest potrzebna, ale nikt jej nie oznaczył jako wycofanej.
Podobnie @openreplay/tracker-zustand w wersji 1.1.1 z 2 kwietnia 2024 ciągnie zustand w zakresie ^4.5.2, a Zustand jest już w wersji 5.0.15. @openreplay/tracker-graphql 4.1.0 z 22 lipca 2024 wciąga @apollo/client w zakresie ^3.9.5 jako zwykłą zależność, nie równorzędną, co oznacza drugą kopię klienta Apollo w drzewie, jeśli projekt ma już własną.
Anonimizacja: to domyślna konfiguracja decyduje
Nagrywanie sesji jest przetwarzaniem danych osobowych i domyślne ustawienia trackera rozstrzygają, czy do nagrania trafi hasło albo numer karty. Poniższe wartości odczytałem z rozpakowanej paczki 18.1.3, nie z dokumentacji.
Tracker ma trzy poziomy sanityzacji, wspólne dla węzłów tekstowych i pól formularza: Plain o wartości 0, Obscured o wartości 1 i Hidden o wartości 2. Poziom Obscured zamienia każdy znak niebędący białym znakiem na gwiazdkę, więc odtwarzacz pokazuje kształt i długość treści, ale nie treść. Poziom Hidden usuwa węzeł z nagrania.
| Opcja | Wartość domyślna | Czego dotyczy |
|---|---|---|
defaultInputMode | 1 (Obscured) | wartości wszystkich pól formularza |
obscureInputNumbers | true | pola z ciągiem czterech cyfr, gdy tryb to Plain |
obscureInputEmails | true | pola typu email lub zawierające znak małpy, gdy tryb to Plain |
obscureInputDates | false | pola typu date, gdy tryb to Plain |
obscureTextEmails | true | adresy pocztowe w zwykłym tekście strony |
obscureTextNumbers | false | cyfry w zwykłym tekście strony |
privateMode | false | maskowanie wszystkiego poza wyjątkami |
network.capturePayload | false | ciała zapytań i odpowiedzi |
network.ignoreHeaders | cookie, set-cookie, authorization | nagłówki pomijane w nagraniu |
Z tej tabeli wynika kilka wniosków, które trzeba znać przed wdrożeniem, a nie po incydencie.
Pola formularzy są domyślnie maskowane, bo defaultInputMode ma wartość Obscured. Pola typu password idą dalej: w kodzie modułu wejściowego stoi jawny warunek node.type === 'password', który wymusza poziom Hidden niezależnie od innych ustawień. Hasło w polu o poprawnym typie nie trafi do nagrania w żadnej konfiguracji.
Trzy heurystyki obscureInput* bywają rozumiane odwrotnie do tego, co robią. W kodzie są sprawdzane wyłącznie w gałęzi inputMode === InputMode.Plain, czyli działają dopiero wtedy, gdy sam zdejmiesz domyślne maskowanie. Jeśli ustawisz defaultInputMode na 0, żeby widzieć, co użytkownicy wpisują w koszyku, ochroną zostaje test /\d\d\d\d/ na wartości pola. Numer karty go przejdzie, bo ma cztery cyfry pod rząd. Trzycyfrowy kod CVV w polu typu text już nie: nie ma czterech cyfr obok siebie, więc trafi do nagrania otwartym tekstem.
Węzły tekstowe są w gorszej sytuacji niż pola. Adresy pocztowe w treści strony są maskowane domyślnie, ale liczby nie. Saldo konta, numer zamówienia, PESEL czy numer telefonu wyrenderowany jako tekst wpada do nagrania bez zmian, dopóki nie ustawisz obscureTextNumbers: true albo nie oznaczysz kontenera atrybutem.
Przy okazji: w opublikowanym module wartość domyślna obscureTextEmails jest zadeklarowana dwa razy i w dwóch miejscach inaczej. Klasa Sanitizer ma we własnych domyślnych ustawieniach true, a lista domyślnych opcji aplikacji ma false. Skutek zależy od tego, który obiekt trafia do konstruktora sanitizera, a trafia do niego surowy obiekt opcji użytkownika, nie scalona lista aplikacji. W praktyce działa więc wartość true z klasy Sanitizer. Jeżeli zależy Ci na przewidywalności, ustaw tę opcję jawnie zamiast liczyć na domyślną.
Ostatnia rzecz z tej sekcji: opcja respectDoNotTrack istnieje, ale nie ma wartości domyślnej. Kod czyta ją prosto z obiektu opcji, więc dopóki jej nie ustawisz, nagłówek Do Not Track przeglądarki jest ignorowany.
Konfiguracja trackera, którą można pokazać inspektorowi
Poniższa konfiguracja domyka luki opisane wyżej. Wszystkie nazwy pól pochodzą z plików deklaracji typów w paczce 18.1.3.
import Tracker from '@openreplay/tracker'
const tracker = new Tracker({
projectKey: process.env.NEXT_PUBLIC_OR_PROJECT_KEY as string,
ingestPoint: 'https://openreplay.firma.internal/ingest',
respectDoNotTrack: true,
// 0 = Plain, 1 = Obscured, 2 = Hidden
defaultInputMode: 1,
obscureInputNumbers: true,
obscureInputEmails: true,
obscureInputDates: true,
obscureTextEmails: true,
obscureTextNumbers: true,
network: {
capturePayload: false,
failuresOnly: false,
sessionTokenHeader: false,
ignoreHeaders: ['cookie', 'set-cookie', 'authorization', 'x-api-key']
}
})
await tracker.start({ userID: 'user-4821' })Dwa szczegóły z tego bloku. Pole ingestPoint domyślnie wskazuje na https://api.openreplay.com/ingest, więc przy własnym serwerze trzeba je nadpisać, inaczej dane pojadą do chmury producenta. Wartość defaultInputMode podaje się liczbą, bo obiekt InputMode nie jest reeksportowany z głównego wejścia paczki; z modułu sanitizer eksportowany jest tylko SanitizeLevel, przydatny w funkcji domSanitizer.
Poziom pojedynczych elementów ustawia się atrybutami w HTML. Aktualne nazwy to data-openreplay-obscured i data-openreplay-hidden. Starsze data-openreplay-masked i data-openreplay-htmlmasked nadal działają, ale tracker wypisuje przy nich ostrzeżenie o wycofaniu. Atrybut data-openreplay-unmask ma znaczenie wyłącznie przy włączonym privateMode, a data-openreplay-label podstawia czytelną etykietę pola zamiast selektora.
<div data-openreplay-obscured>Saldo: 12 480,00 PLN</div>
<section data-openreplay-hidden>
<iframe src="https://platnosci.example.com/karta"></iframe>
</section>
<input type="text" name="cvv" data-openreplay-hidden />
<input type="email" name="email" data-openreplay-label="adres e-mail" />Gdy reguła jest zbyt złożona na atrybuty, zostaje domSanitizer, czyli funkcja wywoływana dla każdego elementu i zwracająca poziom. Wyższy poziom zawsze wygrywa z ustawieniami i z atrybutami. Analogicznym mechanizmem po stronie sieci jest network.sanitizer, który dostaje obiekt z polami status, method, url, request i response, może je zmienić, a zwrócenie null usuwa całe zapytanie z nagrania.
import { SanitizeLevel } from '@openreplay/tracker'
const tracker = new Tracker({
projectKey: process.env.NEXT_PUBLIC_OR_PROJECT_KEY as string,
domSanitizer: (node: Element) => {
if (node.closest('[data-pii]')) return SanitizeLevel.Hidden
if (node.classList.contains('kwota')) return SanitizeLevel.Obscured
return SanitizeLevel.Plain
},
network: {
capturePayload: true,
sanitizer: (data) => {
if (data.url.includes('/api/payments')) return null
data.url = data.url.replace(/token=[^&]+/, 'token=***')
if (data.request.body) data.request.body = '[usunięte]'
return data
}
}
})Po zmianie atrybutów w czasie działania strony trzeba wywołać tracker.resanitize(el). Bez argumentu metoda przechodzi całe drzewo dokumentu i jej koszt rośnie liniowo z jego rozmiarem, więc lepiej podać najwyższy zmieniony element. Do sprawdzenia bieżącego stanu służy tracker.checkSanitization(el), zwracające poziom albo undefined, gdy element nie jest śledzony.
Koszt: własny serwer kontra plan hostowany
Producent podaje jeden zestaw wymagań minimalnych i powtarza go na obu stronach wdrożeniowych: 2 wirtualne rdzenie, 8 GB pamięci RAM, 50 GB dysku i architektura x86. Dokumentacja jest przy tym jednoznaczna, że poniżej tych wartości usługi backendu po prostu nie wystartują. Instrukcja dla Dockera sugeruje instancję klasy t3.large lub równoważną i opisuje ten zestaw jako wystarczający dla niskiego i umiarkowanego ruchu. Instrukcja dla Kubernetesa podaje te same liczby, ale opisuje je jako wystarczające dla ruchu umiarkowanego. Rozbieżność jest w słowach, nie w liczbach, i przy planowaniu warto przyjąć wersję ostrożniejszą.
Ile miejsca zajmują same nagrania, producent nie podaje. Ani strona wdrożeniowa, ani cennik nie zawierają przelicznika w rodzaju „X megabajtów na sesję". Dysk 50 GB jest w dokumentacji progiem uruchomienia usług, nie prognozą pojemności. Jedyna liczba, o którą można zaczepić szacunek, pochodzi z porównania planów hostowanych: instancja klasy O1 dostaje 120 GB dysku, a górny pułap dla planu Dedicated to 4 TB. Bez oficjalnego przelicznika każde przełożenie tego na liczbę sesji byłoby zgadywaniem, więc przed wdrożeniem produkcyjnym zmierz to sam na kilkuset sesjach z własnej aplikacji.
Cennik hostowany pobrany 22 sierpnia 2026 roku wymienia trzy warianty wdrożenia. Self-Hosted jest darmowy i sprowadza się do uruchomienia kodu otwartego na swoim sprzęcie. Dedicated to zarządzana instancja od 199 dolarów miesięcznie, rozliczana godzinowo po 0,276 dolara za godzinę. Serverless jest opisany jako rozliczenie od liczby nagranych sesji. Ceny wariantu Serverless nie da się odczytać: ta zakładka renderuje się dopiero w JavaScripcie, a w surowym HTML nie ma ani stawki za sesję, ani progu darmowego, więc nie podam liczby, której nie widziałem.
Arytmetyka planu Dedicated wymaga uwagi. Stawka 0,276 dolara za godzinę razy 720 godzin daje 198,72 dolara, co po zaokrągleniu zgadza się z kwotą 199 dolarów. Ale miesiąc ma średnio 730 godzin, a przy takim mnożeniu wychodzi 201,48 dolara. Podana cena miesięczna zakłada więc miesiąc trzydziestodniowy i w rozliczeniu godzinowym bywa nieco wyższa.
Konfiguracja O1 za 199 dolarów w regionie Wirginia Północna to 8 GB RAM, 2 wirtualne rdzenie i 120 GB dysku, czyli minimum wdrożeniowe plus większy dysk. Producent sam rekomenduje zaczynać od O2. Do tego kilka warunków z sekcji pytań na stronie cennika: zatrzymana instancja nadal jest rozliczana za dysk, wariant Bring-Your-Own-Cloud ma stałą stawkę miesięczną niezależną od regionu, okres próbny trwa siedem dni bez karty, a migracja między planem otwartym a hostowanym nie jest jeszcze obsługiwana i wymaga kontaktu z zespołem. Darmowego planu w chmurze na tej stronie nie ma; darmowy jest wyłącznie wariant samodzielnie hostowany.
OpenReplay a PostHog, Sentry i Axiom
Te cztery narzędzia bywają wrzucane do jednego worka „obserwowalność frontendu", a odpowiadają na różne pytania i mają różne jednostki danych.
| Narzędzie | Na jakie pytanie odpowiada | Jednostka danych | Kiedy sięgasz |
|---|---|---|---|
| OpenReplay | co dokładnie widział i klikał ten użytkownik | sesja przeglądarki | zgłoszenie „u mnie nie działa" bez kroków odtworzenia |
| PostHog | jak zachowuje się populacja użytkowników | zdarzenie produktowe | lejki, retencja, testy A/B, nagranie jako dodatek |
| Sentry | co się zepsuło i w której linii kodu | wyjątek ze stosem wywołań | alarmy o błędach i regresjach po wdrożeniu |
| Axiom | co się działo w systemie w danym oknie czasu | surowe zdarzenie w logu | zapytania po logach z całej infrastruktury |
PostHog ma nagrania sesji jako jedną z funkcji obok analityki produktowej i to zmienia punkt ciężkości. Jeśli głównym zadaniem jest lejek zakupowy albo test A/B, a nagranie ma być dowodem w konkretnej sprawie, PostHog wystarczy i oszczędza jedną integrację. Jeśli głównym zadaniem jest diagnostyka, wtedy dziennik konsoli, podgląd zapytań sieciowych i stan magazynu sprzężone z osią czasu odtwarzania są w OpenReplayu wyraźnie bogatsze.
Sentry i OpenReplay się uzupełniają. Sentry powie, że w wydaniu 4.2.1 wybucha ten sam wyjątek u czterystu osób, i pokaże stos wywołań. OpenReplay pokaże, co te osoby robiły przez trzydzieści sekund przed wybuchem. Tego drugiego z samego raportu błędu nie da się odtworzyć.
Axiom działa na innym poziomie abstrakcji: to magazyn zdarzeń z językiem zapytań, a nie odtwarzacz. Do korelacji ruchu na froncie z zachowaniem usług backendowych nadaje się lepiej, ale nikt w nim nie obejrzy sesji.
Warto też zaznaczyć, czego OpenReplay nie zastąpi. Nie jest narzędziem analityki ruchu i nie zdejmie z listy Plausible, jeśli potrzebujesz prostych statystyk odwiedzin bez ciasteczek. Wbudowany moduł analityczny w trackerze jest dodatkiem, nie zamiennikiem.
Przy wyborze narzędzia w tej kategorii warto znać los najbliższego konkurenta. Highlight.io oferował to samo połączenie nagrań, błędów i dzienników, z wariantem do uruchomienia u siebie, ale został wchłonięty: domena przekierowuje dziś w całości na stronę LaunchDarkly, ostatnie wydanie obrazów pochodzi z sierpnia 2025 roku, a biblioteka przeglądarkowa wychodzi dalej wyłącznie jako wewnętrzna zależność cudzego produktu. To dobra ilustracja ryzyka, które przy narzędziu z otwartym kodem i własnym hostingiem jest mniejsze, ale nie znika: kod zostaje, natomiast rozwój i obrazy kontenerów mogą się zatrzymać.
Typowe błędy przy wdrażaniu
Pierwszy: pozostawienie domyślnego ingestPoint. Przy samodzielnym hostowaniu bez nadpisania tego pola tracker wysyła sesje na api.openreplay.com, czyli dokładnie tam, gdzie wdrożenie na własnym serwerze miało ich nie wysyłać.
Drugi: obniżenie defaultInputMode do zera w imię „lepszej diagnostyki" bez przejrzenia formularzy. Od tego momentu ochroną są tylko heurystyki, a te przepuszczają wartości krótsze niż cztery cyfry.
Trzeci: założenie, że numery na stronie są maskowane tak samo jak w polach. Nie są. obscureTextNumbers ma domyślnie false i kwoty, numery zamówień czy identyfikatory w treści strony trafiają do nagrania w całości.
Czwarty: włączenie network.capturePayload bez napisania funkcji network.sanitizer. Ciała odpowiedzi z API to najkrótsza droga do tego, żeby cała tabela klientów wylądowała w nagraniu sesji.
Piąty: instalowanie wtyczek z rodziny bez sprawdzenia daty wydania. Cztery z nich stoją nietknięte od 2022 roku, a tracker-axios dodatkowo blokuje aktualnego Axiosa zakresem 0.x.
Szósty: dopisanie własnych zmian do serwera i uruchomienie go jako usługi dla klientów bez przeczytania sekcji 13 AGPL v3. Licencja tego nie zabrania, ale nakłada obowiązek udostępnienia kodu użytkownikom instancji.
Siódmy: rozbudowanie wdrożenia o funkcje z katalogu ee/ w przekonaniu, że skoro kod jest publiczny w repozytorium na licencji otwartej, to wolno go używać. Katalog ee/ ma osobną licencję zastrzeżoną, a jej treść nie jest opublikowana.
Ósmy: uruchomienie trackera poza HTTPS. Kod odmawia startu poza protokołem https: i wypisuje w konsoli komunikat, chyba że ustawisz flagę __DISABLE_SECURE_MODE, przeznaczoną wyłącznie do testów lokalnych. Nazwa tej flagi nie jest przypadkowa i nie powinna trafić na produkcję.
FAQ
Czy OpenReplay jest w pełni otwarty?
Nie w całości. Tracker npm jest na MIT, kod serwera domyślnie na AGPL v3, a katalog ee/ na licencji zastrzeżonej wymagającej umowy z producentem. Główny plik LICENSE wspomina jeszcze o bliżej nieokreślonych katalogach na MIT, ale nie wymienia żadnego z nazwy.
Czy mogę hostować OpenReplay dla klientów jako usługę?
AGPL v3 tego nie zabrania, w odróżnieniu od SSPL czy BSL. Jeśli jednak wdrożysz zmodyfikowaną wersję, sekcja 13 licencji wymaga udostępnienia kodu tej wersji użytkownikom korzystającym z instancji przez sieć. Funkcje z katalogu ee/ pozostają poza tym scenariuszem.
Czy hasła i numery kart trafiają do nagrania?
Pola typu password są wycinane zawsze, bo wymusza to warunek w kodzie. Pozostałe pola są domyślnie maskowane gwiazdkami. Ryzyko pojawia się po zmianie defaultInputMode na Plain oraz w zwykłym tekście strony, gdzie liczby domyślnie nie są maskowane.
Ile miejsca zajmują nagrania sesji?
Producent nie publikuje przelicznika na sesję. Podaje wymagania minimalne wdrożenia, czyli 2 rdzenie, 8 GB RAM i 50 GB dysku, oraz pojemności planów hostowanych, od 120 GB w konfiguracji O1 do 4 TB w planie Dedicated. Realne zużycie trzeba zmierzyć na własnym ruchu.
Czy plan hostowany ma wersję darmową?
Na stronie cennika z 22 sierpnia 2026 roku darmowy jest wyłącznie wariant samodzielnie hostowany. Plan Dedicated zaczyna się od 199 dolarów miesięcznie i oferuje siedmiodniowy okres próbny bez karty. Warunki planu Serverless renderują się dopiero po uruchomieniu JavaScriptu i nie dało się ich odczytać z surowego HTML.
Czy OpenReplay zastąpi mi PostHog albo Sentry?
Raczej nie w całości. OpenReplay odpowiada na pytanie o pojedynczą sesję, PostHog o zachowanie populacji, a Sentry o konkretny wyjątek w konkretnym wydaniu. W Reakcie i w projektach na Next.js najczęściej spotyka się układ, w którym Sentry pilnuje błędów, a OpenReplay służy do odtwarzania zgłoszeń bez kroków odtworzenia.
Źródła sprawdzone 22 sierpnia 2026 roku: plik LICENSE w repozytorium, metadane paczki w rejestrze npm, dokumentacja wdrożeniowa oraz cennik producenta.