A unified approach to browser session persistence
Browser automation tools handle session persistence in fundamentally different ways, creating significant interoperability problems for developers. The PersistentSessionsProtocol establishes a unified method for capturing, storing, transmitting, and replaying browser sessions across Playwright, Selenium, Skyvern, and other major frameworks.
PSP employs a layered architecture with four distinct components:
The capture layer extracts state through:
Framework adapters translate between PSP's structures and framework implementations.
Data serialization uses:
Multiple backend options supported:
Session restoration through:
interface BrowserSessionState {
version: string;
timestamp: number;
origin: string;
storage: StorageState;
dom?: DOMState;
history?: HistoryState;
network?: NetworkState;
recording?: RecordingState;
}
interface StorageState {
cookies: Cookie[];
localStorage: Map>; // Origin to key-value map
sessionStorage: Map>;
indexedDB?: IndexedDBState;
cacheStorage?: CacheStorageState;
}
interface Cookie {
name: string;
value: string;
domain: string;
path: string;
expires: number | null;
httpOnly: boolean;
secure: boolean;
sameSite: "Strict" | "Lax" | "None";
partitioned: boolean; // For CHIPS (Cookie Having Independent Partitioned State)
}
interface AuthenticationState {
tokens: AuthToken[];
credentials?: EncryptedCredentials; // Only with explicit permission
}
interface AuthToken {
type: string; // "Bearer", "JWT", "OAuth", etc.
value: string;
scope?: string[];
expiresAt?: number;
refreshToken?: string;
isHttpOnly: boolean;
domain: string;
}
interface NavigationState {
currentUrl: string;
entries: HistoryEntry[];
currentIndex: number;
}
interface HistoryEntry {
url: string;
title: string;
state?: object; // History state object
scrollPosition?: { x: number, y: number };
timestamp: number;
}
interface RecordingState {
events: Event[];
startTime: number;
duration: number;
}
interface Event {
type: string; // "click", "keydown", "timer", "network", etc.
timestamp: number;
target?: string; // CSS selector or XPath
data: object; // Event-specific data
}
interface PSPSession {
// Session management
create(options?: SessionOptions): Promise;
load(sessionId: string): Promise;
list(filter?: SessionFilter): Promise;
delete(sessionId: string): Promise;
// State operations
capture(page: BrowserPage): Promise;
restore(page: BrowserPage): Promise;
// Recording operations
startRecording(options?: RecordingOptions): Promise;
stopRecording(): Promise;
playRecording(options?: PlaybackOptions): Promise;
}
GET /sessions # List sessions
POST /sessions # Create a new session
GET /sessions/:id # Get session by ID
PUT /sessions/:id # Update session
DELETE /sessions/:id # Delete session
PATCH /sessions/:id # Partial update
GET /sessions/:id/events # Get recorded events
POST /sessions/:id/events # Add events to session
// Event types
type PSPWebSocketEvent =
| { type: 'session.update', data: BrowserSessionState }
| { type: 'session.event', data: Event }
| { type: 'session.deleted', data: { id: string } };
// Connection message for subscribing to session updates
interface PSPWebSocketConnect {
action: 'subscribe';
sessionId: string;
token: string; // Authentication token
}
Framework adapters must implement:
Storage backends must provide: