Skip to main content

Overview

Agent Browser supports isolated browser sessions that allow you to:
  • Run multiple browser instances concurrently
  • Keep each instance’s state (cookies, localStorage) separate
  • Persist state across browser restarts
  • Test different user flows in parallel
There are two types of session features:
  1. Session Isolation (--session): Run multiple concurrent browser instances
  2. Session Persistence (--session-name, --profile): Save and restore state

Session Isolation

Multiple Concurrent Sessions

Run multiple isolated browser instances with different session names:
# Terminal 1: Agent controlling site A
agent-browser --session agent1 open site-a.com
agent-browser --session agent1 snapshot
agent-browser --session agent1 click @e1

# Terminal 2: Agent controlling site B (concurrent)
agent-browser --session agent2 open site-b.com
agent-browser --session agent2 snapshot
agent-browser --session agent2 fill @e2 "test"
Each session gets its own:
  • Daemon process (separate PID)
  • Browser instance (separate Chromium process)
  • Cookies and storage (isolated state)
  • Navigation history
  • Socket/port (for IPC)

Session Architecture

Sessions are isolated at the OS process level:
~/.agent-browser/
├── default.pid       # PID for default session
├── default.sock      # Socket for default session
├── agent1.pid        # PID for agent1 session
├── agent1.sock       # Socket for agent1 session
├── agent2.pid        # PID for agent2 session
└── agent2.sock       # Socket for agent2 session
On Windows, port files replace .sock files:
~/.agent-browser/
├── agent1.port       # Port number for agent1
└── agent2.port       # Port number for agent2
Ports are deterministically derived from session names:
// From daemon.ts:188-196
function getPortForSession(session: string): number {
  let hash = 0;
  for (let i = 0; i < session.length; i++) {
    hash = (hash << 5) - hash + session.charCodeAt(i);
  }
  // Port range 49152-65535 (dynamic/private ports)
  return 49152 + (Math.abs(hash) % 16383);
}
This ensures the same session name always gets the same port.

Default Session

If you don’t specify --session, the session name defaults to "default":
# These are equivalent:
agent-browser open example.com
agent-browser --session default open example.com
You can also set the session via environment variable:
export AGENT_BROWSER_SESSION=agent1
agent-browser open example.com  # Uses agent1 session

Listing Active Sessions

See which sessions are currently running:
agent-browser session list
Active sessions:
→ default
  agent1
  agent2
The arrow () indicates the current session (from --session flag or AGENT_BROWSER_SESSION env var). In JSON mode:
agent-browser session list --json
{
  "success": true,
  "data": {
    "sessions": ["default", "agent1", "agent2"]
  }
}

Session Naming Rules

Session names must be:
  • Alphanumeric, hyphens, or underscores only: /^[a-zA-Z0-9_-]+$/
  • No path separators (/, \\)
  • No special characters
This prevents path traversal attacks:
// From state-utils.ts
export function isValidSessionName(name: string): boolean {
  return /^[a-zA-Z0-9_-]+$/.test(name);
}
Invalid examples:
# Bad - path traversal
agent-browser --session ../../../etc/passwd open example.com
# Error: Invalid session name

# Bad - special characters
agent-browser --session "user@domain" open example.com
# Error: Invalid session name

Closing Sessions

Close a specific session:
agent-browser --session agent1 close
This:
  1. Closes the browser for agent1
  2. Shuts down the daemon for agent1
  3. Removes agent1.sock and agent1.pid files
Other sessions continue running unaffected.

Session Persistence

Auto-Persist with --session-name

The --session-name flag automatically saves and restores cookies and localStorage:
# First run: login and save state
agent-browser --session-name twitter open twitter.com
agent-browser fill @e1 "username"
agent-browser fill @e2 "password"
agent-browser click @e3  # Login
agent-browser close     # Auto-saves state

# Later: state is restored automatically
agent-browser --session-name twitter open twitter.com
# Already logged in!
State files are stored at:
~/.agent-browser/sessions/{session_name}-{session_id}.json
Where:
  • {session_name} is the value of --session-name
  • {session_id} is the value of --session (or "default")
Example:
agent-browser --session agent1 --session-name twitter open twitter.com
# Saves to: ~/.agent-browser/sessions/twitter-agent1.json

agent-browser --session agent2 --session-name twitter open twitter.com
# Saves to: ~/.agent-browser/sessions/twitter-agent2.json
This allows different sessions to have separate Twitter logins.

State File Format

State files contain cookies and localStorage in Playwright’s storage state format:
{
  "cookies": [
    {
      "name": "session_id",
      "value": "abc123...",
      "domain": ".twitter.com",
      "path": "/",
      "expires": 1735689600,
      "httpOnly": true,
      "secure": true,
      "sameSite": "Lax"
    }
  ],
  "origins": [
    {
      "origin": "https://twitter.com",
      "localStorage": [
        { "name": "theme", "value": "dark" }
      ]
    }
  ]
}

State Encryption

Encrypt state files at rest with AES-256-GCM:
# Generate a random 256-bit key (64 hex characters)
openssl rand -hex 32

# Set the key
export AGENT_BROWSER_ENCRYPTION_KEY="your-64-char-hex-key"

# State is now encrypted automatically
agent-browser --session-name secure open example.com
Encrypted state files look like:
{
  "encrypted": true,
  "version": 1,
  "iv": "base64-encoded-iv",
  "authTag": "base64-encoded-auth-tag",
  "ciphertext": "base64-encoded-encrypted-data"
}
The daemon automatically detects encrypted files and decrypts them:
// From browser.ts:1424-1445
if (isEncryptedPayload(parsed)) {
  const key = getEncryptionKey();
  if (key) {
    const decrypted = decryptData(parsed, key);
    storageState = JSON.parse(decrypted);
  } else {
    // Warn: encrypted but no key set
  }
}
If the key is wrong or missing, the daemon starts with a fresh session and warns:
[WARN] State file is encrypted but AGENT_BROWSER_ENCRYPTION_KEY not set - starting fresh

State Expiration

Old state files are automatically deleted:
# Default: delete states older than 30 days
agent-browser close  # Cleanup runs on shutdown

# Custom expiration
export AGENT_BROWSER_STATE_EXPIRE_DAYS=7
agent-browser close  # Cleanup runs on shutdown
Cleanup runs on daemon startup:
// From daemon.ts:338-339
runCleanupExpiredStates();
You can also manually clean up old states:
agent-browser state clean --older-than 7

Persistent Profiles (--profile)

The --profile flag uses Playwright’s persistent context, which stores all browser data:
# Use a persistent profile directory
agent-browser --profile ~/.myapp-profile open myapp.com

# Login once
agent-browser --profile ~/.myapp-profile fill @e1 "user"
agent-browser --profile ~/.myapp-profile fill @e2 "pass"
agent-browser --profile ~/.myapp-profile click @e3

# Later: reuse the authenticated session
agent-browser --profile ~/.myapp-profile open myapp.com/dashboard
Profiles store:
  • Cookies and localStorage
  • IndexedDB databases
  • Service workers
  • Browser cache
  • Extension state (if using --extension)

--profile vs --session-name

Feature--profile--session-name
Cookies
localStorage
IndexedDB
Service workers
Browser cache
Extensions
Encryption✓ (optional)
Auto-saveAlwaysOn close
Use caseFull browser stateLightweight auth
When to use --profile:
  • You need full browser state (cache, IndexedDB, etc.)
  • You’re using extensions
  • You want persistence across commands without explicit save
When to use --session-name:
  • You only need cookies and localStorage
  • You want encrypted state files
  • You want explicit control over when state is saved

Manual State Save/Load

You can also manually save and load state:
# Save state to a file
agent-browser state save ~/my-state.json

# Load state from a file
agent-browser state load ~/my-state.json

# Or use --state flag on any command
agent-browser --state ~/my-state.json open example.com
The state command provides additional features:
# List saved states
agent-browser state list

# Show state summary (without exposing secrets)
agent-browser state show my-state.json

# Rename a state file
agent-browser state rename old.json new.json

# Clear states for current session
agent-browser state clear

# Clear all states
agent-browser state clear --all

Use Cases

Parallel Testing

Test different user flows concurrently:
# Test A: Free user flow
agent-browser --session test-a --session-name free-user open app.com &

# Test B: Premium user flow
agent-browser --session test-b --session-name premium-user open app.com &

# Tests run in parallel without interfering

Multi-Account Management

Manage multiple accounts on the same site:
# Work account
agent-browser --session work --session-name gmail-work open gmail.com

# Personal account
agent-browser --session personal --session-name gmail-personal open gmail.com

Agent Swarms

Run multiple AI agents simultaneously:
# Agent 1: Research competitor A
AGENT_BROWSER_SESSION=agent1 agent-browser open competitor-a.com

# Agent 2: Research competitor B
AGENT_BROWSER_SESSION=agent2 agent-browser open competitor-b.com

# Agent 3: Research competitor C
AGENT_BROWSER_SESSION=agent3 agent-browser open competitor-c.com
Each agent has its own browser and doesn’t interfere with others.

State Isolation in CI/CD

Use session names to isolate test runs:
# In CI pipeline
export AGENT_BROWSER_SESSION_NAME="ci-run-${CI_BUILD_ID}"
agent-browser open myapp.com
# State saved to: ~/.agent-browser/sessions/ci-run-12345-default.json
This prevents state pollution between CI runs.

Security Considerations

Socket Permissions

Socket files are created with 0o700 permissions (owner-only):
// From daemon.ts:330-333
if (!fs.existsSync(socketDir)) {
  fs.mkdirSync(socketDir, { recursive: true, mode: 0o700 });
}
This prevents other users from:
  • Connecting to your session
  • Reading your traffic
  • Injecting commands

State File Permissions

State files are created with 0o600 permissions (owner read/write only):
// From daemon.ts:518
fs.chmodSync(savePath, 0o600);
This prevents other users from reading cookies and localStorage.

Encryption Key Security

If using encryption, protect your key:
# Bad - key in shell history
export AGENT_BROWSER_ENCRYPTION_KEY="my-secret-key"

# Good - read from secure vault
export AGENT_BROWSER_ENCRYPTION_KEY=$(vault read -field=key secret/agent-browser)

# Good - read from file
export AGENT_BROWSER_ENCRYPTION_KEY=$(cat ~/.agent-browser/.encryption-key)
The authentication vault feature auto-generates a key at ~/.agent-browser/.encryption-key if AGENT_BROWSER_ENCRYPTION_KEY is not set.

Performance

Session Overhead

Each session has minimal overhead:
  • Daemon process: ~50-100 MB RAM
  • Browser instance: ~200-500 MB RAM (depends on page)
  • Disk: ~10-50 MB per profile (if using --profile)
You can run 10-20 concurrent sessions on a typical development machine without issues.

State File Size

State files are typically small:
  • Minimal state (few cookies): ~1-5 KB
  • Typical app (10-20 cookies + localStorage): ~10-50 KB
  • Complex app (many cookies + large localStorage): ~100-500 KB
Encryption adds minimal overhead (~5% size increase for the IV and auth tag).

Next Steps

Build docs developers (and LLMs) love