Skip to main content
Flow Browser is built with privacy and security in mind, offering content blocking, isolated profiles, and modern web compatibility including DRM support for streaming services.

Content Blocking

Flow Browser includes a powerful content blocker powered by the Ghostery adblocker engine.

Blocking Modes

The content blocker offers three filtering levels:

All

Block all ads, trackers, annoyances, and other unwanted content

Ads & Trackers

Block advertisements and tracking scripts only

Ads Only

Block only advertisements, allow trackers

How It Works

The content blocker uses Ghostery’s @ghostery/adblocker-electron package:
import { ElectronBlocker } from '@ghostery/adblocker-electron';

class ContentBlocker {
  private async createBlockerInstance(type: BlockerInstanceType) {
    switch (type) {
      case 'all':
        return ElectronBlocker.fromPrebuiltFull();
      case 'adsAndTrackers':
        return ElectronBlocker.fromPrebuiltAdsAndTracking();
      case 'adsOnly':
        return ElectronBlocker.fromPrebuiltAdsOnly();
    }
  }
}
From src/main/modules/content-blocker.ts:23-53.

Session-Based Blocking

Content blocking is applied per-session (per-profile):
private async enableBlockerForSession(
  blockerType: BlockerInstanceType,
  session: Session
): Promise<void> {
  const blocker = await this.createBlockerInstance(blockerType);
  
  // Enable blocking in session
  blocker.enableBlockingInSession(
    unifiedWebRequests.createSession(session, SESSION_KEY)
  );
}
From src/main/modules/content-blocker.ts:74-86.
Each profile can have different content blocking settings, giving you granular control over privacy levels.

Blocked Request Logging

The content blocker logs blocked requests for debugging:
blocker.on('request-blocked', (request) => {
  debugPrint('CONTENT_BLOCKER', 'Request blocked:', request.url);
});

Dynamic Configuration

The content blocker automatically updates when settings change:
public async updateConfig(): Promise<void> {
  const contentBlocker = getSettingValueById('contentBlocker');
  const profiles = loadedProfilesController.getAll();
  
  switch (contentBlocker) {
    case 'all':
    case 'adsAndTrackers':
    case 'adsOnly':
      for (const profile of profiles) {
        this.enableBlockerForSession(contentBlocker, profile.session);
      }
      break;
    default:
      this.disableBlocker();
  }
}
From src/main/modules/content-blocker.ts:91-108.

Profile Isolation

Each profile in Flow Browser is completely isolated with separate:
  • Cookies
  • Cache
  • Local Storage
  • Session Storage
  • IndexedDB
  • Service Workers
  • HTTPS certificates
  • Authentication tokens
  • Site permissions
  • Password storage
  • Separate extension installations
  • Independent extension data
  • Profile-specific extension settings

Implementation

Profiles use Chromium’s built-in profile system:
const profilePath = path.join(FLOW_DATA_DIR, 'Profiles', profileId);
await fs.mkdir(profilePath, { recursive: true });
From src/main/controllers/profiles-controller/raw.ts:82-83. Each profile directory contains:
  • Cache/ - HTTP cache
  • Cookies - Cookie database
  • Local Storage/ - localStorage data
  • Session Storage/ - sessionStorage data
  • Extensions/ - Installed extensions
  • Preferences - Chromium preferences

DRM Support (Widevine)

Flow Browser supports Widevine DRM for streaming services like Netflix, Spotify, and Disney+.

Castlabs Electron

The browser uses a special build of Electron with Widevine support:
{
  "electron": "https://github.com/castlabs/electron-releases#v40.1.0+wvcus"
}
From package.json in the source repository.
The Castlabs Electron build is larger than standard Electron due to the included Widevine CDM (Content Decryption Module).

Supported Services

With Widevine DRM, Flow Browser supports:
  • Netflix
  • Spotify Web Player
  • Disney+
  • Hulu
  • HBO Max
  • Amazon Prime Video
  • And other DRM-protected content

Web Security Features

Sandbox Mode

All web content runs in a sandbox for security:
const webPreferences: WebPreferences = {
  sandbox: true,
  webSecurity: true,
  nodeIntegration: false,
  contextIsolation: true,
  // ...
};
From src/main/controllers/tabs-controller/tab.ts:90-107.

Sandbox

Renderer processes run in a restricted sandbox

Context Isolation

Preload scripts isolated from web content

No Node Integration

Web pages can’t access Node.js APIs

Web Security

Same-origin policy and CORS enforced

Safe Dialogs

Dialog bombing protection is enabled:
const webPreferences: WebPreferences = {
  safeDialogs: true,
  // Prevents malicious pages from showing infinite alerts
};

Scroll Bounce

macOS-style scroll bounce is enabled:
const webPreferences: WebPreferences = {
  scrollBounce: true,
};

Drag & Drop Navigation

Navigating by dragging files is enabled:
const webPreferences: WebPreferences = {
  navigateOnDragDrop: true,
};

External Protocol Handling

External protocols (like mailto:, tel:) are handled safely through the operating system.

Settings-Based Privacy

The content blocker integrates with the settings system:
// Listen for setting changes
settingsEmitter.on('settings-changed', () => {
  contentBlocker.updateConfig();
});

// Listen for profile changes
loadedProfilesController.on('profile-loaded', () => {
  contentBlocker.updateConfig();
});
From src/main/modules/content-blocker.ts:118-125.

Initialization

The content blocker initializes after settings are loaded:
onSettingsCached().then(() => {
  debugPrint('CONTENT_BLOCKER', 'Initializing content blocker');
  contentBlocker.initialize();
});
From src/main/modules/content-blocker.ts:133-136.

Privacy Best Practices

1

Use Separate Profiles

Create different profiles for work, personal, and sensitive browsing
2

Enable Content Blocking

Turn on “All” mode in settings for maximum protection
3

Review Extensions

Only install extensions you trust and review their permissions
4

Delete Unused Profiles

Remove profiles you no longer need to eliminate stored data

Disable Blocking for Sites

Currently, the content blocker applies globally per-profile. Per-site exceptions are not yet implemented but could be added by extending the ContentBlocker class.
To disable blocking for specific sites, you could:
  1. Create a separate profile without content blocking
  2. Switch to that profile when visiting the site
  3. Or temporarily disable content blocking in settings

Technical Architecture

Unified Web Requests

The content blocker uses a unified web request system to avoid conflicts:
blocker.enableBlockingInSession(
  unifiedWebRequests.createSession(session, SESSION_KEY)
);
This ensures multiple systems (content blocker, extensions, etc.) can intercept web requests without conflicts.

Session Keys

Each blocker instance uses a unique session key:
const SESSION_KEY = 'content-blocker';
This allows the unified web request system to manage the blocker’s hooks.

Blocker Instance Lifecycle

  1. Creation: Blocker loads prebuilt filter lists
  2. Attachment: Blocker attaches to session web requests
  3. Filtering: Requests are checked against filter lists
  4. Blocking: Matching requests are blocked
  5. Events: request-blocked events are emitted
  6. Detachment: On disable, blocker detaches from session
  7. Cleanup: Instance is disposed and memory freed

Future Privacy Features

Potential future enhancements:
  • Per-site content blocking exceptions
  • Fingerprinting protection
  • User-agent spoofing
  • Cookie auto-deletion
  • Private browsing mode
  • VPN integration
  • Encrypted profile storage
  • Profiles - Profile isolation provides privacy boundaries
  • Extensions - Review extension permissions for privacy
  • Tabs - Each tab respects profile privacy settings

Build docs developers (and LLMs) love