# How the Nautilus Wallet Extension Popup Communicates with the Background Script

> Discover how the Nautilus Wallet extension popup communicates with the background script using webext-bridge for typed message passing and request coordination via AsyncRequestQueue.

- Repository: [Nautilus Team/nautilus-wallet](https://github.com/nautls/nautilus-wallet)
- Tags: internals
- Published: 2026-03-07

---

**Nautilus Wallet uses the `webext-bridge` library to establish typed message passing between the Vue-based popup UI and the service worker background script, coordinating requests through a shared `AsyncRequestQueue`.**

The `nautls/nautilus-wallet` repository implements a sophisticated communication layer that allows the browser extension popup to securely exchange data with the persistent background script. Understanding how the extension popup communicates with the background script is essential for developers building on or contributing to this Ergo blockchain wallet.

## The Architecture: webext-bridge and Typed Message Passing

Nautilus Wallet abstracts Chrome's native `runtime.sendMessage` API using the **`webext-bridge`** library. This creates a type-safe messaging layer that distinguishes between the popup context (`"popup"`) and the background context (`"background"`).

The architecture relies on three core components:

- **[`src/extension/connector/rpc/uiRpcHandlers.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/uiRpcHandlers.ts)** – Registers popup-side message handlers and sends initialization signals
- **[`src/extension/background/background.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/background/background.ts)** – Listens for messages, manages the request queue, and opens the popup when user interaction is required
- **[`src/extension/connector/rpc/asyncRequestQueue.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/asyncRequestQueue.ts)** – Shared queue that correlates pending requests with their eventual UI-driven responses

## Step-by-Step Communication Flow

### 1. Popup Initialization and the Loaded Event

When the Vue popup application mounts in [`src/extension/popup/main.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/popup/main.ts), it triggers the RPC handler registration. The popup immediately notifies the background script that it is ready to receive requests:

```typescript
// src/extension/connector/rpc/uiRpcHandlers.ts
import { sendMessage, onMessage } from "webext-bridge/popup";

export function registerRpcHooks() {
  // Signal that the popup UI is loaded and ready
  sendMessage(InternalEvent.Loaded, _, BACKGROUND);
  
  // Register handlers for specific request types
  onMessage(InternalRequest.Connect, ({ data }) => 
    handle(InternalRequest.Connect, data)
  );
}

```

This `InternalEvent.Loaded` message is critical because it allows the background script to flush any pending requests that were queued while the popup was closed.

### 2. Background Script Message Handling

The background script in [`src/extension/background/background.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/background/background.ts) sets up persistent listeners using `onMessage` from `webext-bridge/background`. It distinguishes between internal endpoints and external dApp connections:

```typescript
// src/extension/background/background.ts
import { onMessage, sendMessage } from "webext-bridge/background";

onMessage(InternalRequest.Connect, async ({ data, sender }) => {
  if (!isInternalEndpoint(sender)) return false;
  
  // Check if already authorized
  const authorized = await checkConnection(data.payload.origin);
  if (authorized) return true;
  
  // Requires user interaction - open popup
  return await openWindow(InternalRequest.Connect, data, sender.tabId);
});

```

For requests requiring authentication, the background uses `onMessageAuth`, which automatically validates the session before executing the handler.

### 3. Queuing Requests and Opening the Popup

When user interaction is required, the background script creates a promise-based request and pushes it onto the `AsyncRequestQueue`. It then opens the popup window and returns the pending promise:

```typescript
// src/extension/background/background.ts
async function openWindow<T extends InternalRequest>(
  request: T,
  data: DataWithPayload,
  tabId?: number
): Promise<GetReturnType<T>> {
  // Create promise and enqueue
  const promise = requests.push<GetReturnType<T>>({
    type: request,
    origin: data.payload.origin,
    favicon: data.payload.favicon,
    data
  });
  
  // Open the popup UI
  await createWindow(tabId);
  
  // Return promise that resolves when user completes interaction
  return promise;
}

```

The `AsyncRequestQueue` stores the `resolve` and `reject` functions alongside the request metadata, allowing the background to fulfill the promise later when the popup sends back the result.

### 4. Resolving Requests from Background to Popup

Once the popup opens and sends the `InternalEvent.Loaded` signal, the background flushes the queue. For each pending request, it sends the request data to the popup using `sendMessage`, then resolves the original promise with the popup's response:

```typescript
// src/extension/background/background.ts
onMessage(InternalEvent.Loaded, async ({ sender }) => {
  if (!isInternalEndpoint(sender)) return;
  
  let request: AsyncRequest | undefined;
  
  // Process all queued requests
  do {
    request = requests.pop();
    if (!request) continue;
    
    const payload = { 
      origin: request.origin, 
      favicon: request.favicon 
    };
    const data = request.data ? { payload, ...request.data } : { payload };
    
    // Send to popup and wait for user decision
    const result = await sendMessage(request.type, data, "popup");
    request.resolve(result);
  } while (request);
});

```

On the popup side, the `handle` function in [`uiRpcHandlers.ts`](https://github.com/nautls/nautilus-wallet/blob/main/uiRpcHandlers.ts) receives this message, navigates to the appropriate view (e.g., the Connect or Sign Transaction screen), and returns a promise that resolves when the user clicks approve or reject.

## Key Implementation Files

Understanding the file structure helps navigate the communication logic:

- **[`src/extension/background/background.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/background/background.ts)** – Service worker entry point; registers message listeners, manages the `AsyncRequestQueue`, and opens the popup window when user interaction is required.
- **[`src/extension/connector/rpc/uiRpcHandlers.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/uiRpcHandlers.ts)** – Popup-side RPC handlers; sends the `Loaded` event and processes incoming requests from the background.
- **[`src/extension/connector/rpc/asyncRequestQueue.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/asyncRequestQueue.ts)** – Shared data structure that stores pending requests with their resolve/reject callbacks.
- **[`src/extension/popup/main.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/popup/main.ts)** – Vue application entry point that initializes the popup UI and triggers RPC registration.
- **[`src/common/browser.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/common/browser.ts)** – Utility functions for browser context detection and endpoint validation.

## Summary

- Nautilus Wallet uses **`webext-bridge`** to abstract Chrome's native messaging API between the popup and background script.
- The **`InternalEvent.Loaded`** signal synchronizes the popup UI with queued background requests.
- An **`AsyncRequestQueue`** correlates pending promises with user interactions, allowing the background to await popup responses.
- The background script opens the popup via **`createWindow`** when authentication or transaction signing requires user approval.
- All messages are typed using **`InternalRequest`** and **`InternalEvent`** enums to ensure compile-time safety.

## Frequently Asked Questions

### What library does Nautilus Wallet use for popup-to-background communication?

Nautilus Wallet uses the **`webext-bridge`** library, which wraps Chrome's `runtime.sendMessage` API to provide type-safe, promise-based messaging between extension contexts. This library allows the popup and background script to communicate using `sendMessage` and `onMessage` functions imported from `webext-bridge/popup` and `webext-bridge/background` respectively.

### How does the background script know when the popup is ready to receive requests?

The background script listens for the **`InternalEvent.Loaded`** message, which the popup sends immediately upon initialization in [`src/extension/connector/rpc/uiRpcHandlers.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/uiRpcHandlers.ts). When this event is received in [`src/extension/background/background.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/background/background.ts), the background flushes the `AsyncRequestQueue`, sending any pending requests to the now-ready popup UI.

### What is the purpose of the AsyncRequestQueue in Nautilus Wallet?

The **`AsyncRequestQueue`** is a shared data structure that stores pending requests along with their `resolve` and `reject` callbacks. It allows the background script to pause execution while waiting for user interaction in the popup, then resume by resolving the promise when the popup returns a result. This queue is defined in [`src/extension/connector/rpc/asyncRequestQueue.ts`](https://github.com/nautls/nautilus-wallet/blob/main/src/extension/connector/rpc/asyncRequestQueue.ts) and used by both the popup and background contexts.

### Can the popup send messages to the background without opening a new window?

Yes, the popup can send messages to the background at any time using **`sendMessage`** from `webext-bridge/popup`, regardless of whether it was opened by the background or the user clicked the extension icon. However, for security-critical operations like signing transactions or connecting dApps, the background typically initiates the flow by opening the popup to ensure the user sees the approval UI.