# How the Summarize Chrome Extension Communicates with the Local Daemon

> Discover how the Summarize Chrome extension communicates with its local daemon using HTTP requests REST endpoints and Server-Sent Events for seamless interaction.

- Repository: [Peter Steinberger/summarize](https://github.com/steipete/summarize)
- Tags: internals
- Published: 2026-02-19

---

**The Summarize Chrome extension communicates with its local daemon via HTTP requests to `http://127.0.0.1:8787`, using REST endpoints for health checks and content extraction, and Server-Sent Events (SSE) for streaming summaries.**

The steipete/summarize repository implements a Chrome extension that relies on a locally running daemon for content processing. Understanding how the Chrome extension communicates with the local daemon reveals a clean HTTP-based architecture that avoids native messaging in favor of standard web protocols. This implementation uses standard `fetch` requests against localhost to handle authentication, content extraction, and real-time summarization.

## HTTP-Based Communication Architecture

The extension’s background script ([`apps/chrome-extension/src/entrypoints/background.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/entrypoints/background.ts)) initiates all communication over standard HTTP to `http://127.0.0.1:8787`. Unlike extensions that use Chrome's native messaging API, this architecture relies entirely on REST endpoints and Server-Sent Events, keeping the communication within the browser process while targeting the local daemon.

All requests originate from the background service worker and are directed at three primary endpoints:

- `GET /health` – Liveness checks
- `GET /v1/ping` – Authentication validation
- `POST /v1/summarize` – Content extraction and summarization

## Health Checks and Authentication

Before processing any content, the extension verifies daemon availability and validates authentication tokens.

### Verifying Daemon Availability

The extension calls `daemonHealth()` to send a GET request to `/health`. This checks whether the daemon is running and responsive.

```typescript
// apps/chrome-extension/src/entrypoints/background.ts
const health = await daemonHealth();  // GET http://127.0.0.1:8787/health

if (!health.ok) {
  // UI shows "Daemon unreachable" state
}

```

### Token Authentication

When a bearer token is configured, the extension performs an additional validation via `daemonPing()`, which sends a GET request to `/v1/ping` with the `Authorization: Bearer` header.

```typescript
// apps/chrome-extension/src/entrypoints/background.ts
const auth = token ? await daemonPing(token) : Promise.resolve({ ok: false });
// GET /v1/ping with Authorization: Bearer <token>

if (!auth.ok) {
  // Handle authentication failure
}

```

## Content Extraction Requests

For pages requiring transcript fetching or full-page scraping, the extension sends a POST request to `/v1/summarize` with `extractOnly: true`. The `buildSummarizeRequestBody()` function in [`apps/chrome-extension/src/lib/daemon-payload.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/lib/daemon-payload.ts) constructs these payloads.

```typescript
// apps/chrome-extension/src/lib/daemon-payload.ts
import { buildSummarizeRequestBody } from "../lib/daemon-payload";

const body = buildSummarizeRequestBody({
  extracted,          // Page or video data collected earlier
  settings,           // User-chosen model, length, token, etc.
  noCache: false,
  inputMode: "video",
  timestamps: true,
  slides: { 
    enabled: true, 
    ocr: true, 
    maxSlides: 20, 
    minDurationSeconds: 5 
  },
});

```

This payload structure allows the daemon to handle various content types—whether extracting YouTube transcripts, scraping article text, or processing local files—before returning structured data to the extension.

## Streaming Summarization with Server-Sent Events

When generating summaries, the extension posts to the same `/v1/summarize` endpoint with a richer payload including model selection, length preferences, and slide settings. The daemon returns a Server-Sent Events (SSE) stream rather than a single JSON response.

The `parseSseStream()` generator in [`apps/chrome-extension/src/lib/sse.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/lib/sse.ts) consumes this stream, parsing each `event:data` chunk and forwarding incremental updates to the side-panel UI.

```typescript
// apps/chrome-extension/src/entrypoints/background.ts
const res = await fetch("http://127.0.0.1:8787/v1/summarize", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${settings.token.trim()}`,
    "content-type": "application/json",
  },
  body: JSON.stringify(body),
  signal: controller.signal,
});

// Parse SSE stream from daemon
for await (const { event, data } of parseSseStream(res.body!)) {
  if (event === "message") {
    // Forward partial summary to side-panel UI
    send(session, { 
      type: "agent:chunk", 
      requestId, 
      text: data 
    });
  }
}

```

The SSE implementation allows the UI to display summary text as it is generated, rather than waiting for the entire completion, creating a responsive user experience for long-form content.

## Recovery and Retry Logic

Network failures or daemon unavailability are handled by [`apps/chrome-extension/src/lib/daemon-recovery.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/lib/daemon-recovery.ts). The `createDaemonRecovery()` helper records failed URLs and evaluates whether conditions are right for automatic retry when the daemon becomes healthy again.

```typescript
// apps/chrome-extension/src/lib/daemon-recovery.ts
import { createDaemonRecovery } from "../lib/daemon-recovery";

const recovery = createDaemonRecovery();
recovery.recordFailure(url);  // Called when request errors

if (recovery.maybeRecover({ 
  isReady, 
  currentUrlMatches, 
  isIdle 
})) {
  // Automatically retry summarization
}

```

This recovery mechanism ensures that transient daemon restarts or network hiccups do not require manual user intervention to resume pending summarization tasks.

## Summary

- **HTTP Protocol**: The extension uses standard `fetch` requests to `http://127.0.0.1:8787` rather than Chrome's native messaging API
- **Health Verification**: `daemonHealth()` checks `/health` and `daemonPing()` validates tokens via `/v1/ping` before processing
- **Payload Construction**: `buildSummarizeRequestBody()` in [`daemon-payload.ts`](https://github.com/steipete/summarize/blob/main/daemon-payload.ts) assembles request bodies for both extraction and summarization modes
- **Streaming Responses**: The daemon returns SSE streams consumed by `parseSseStream()` in [`lib/sse.ts`](https://github.com/steipete/summarize/blob/main/lib/sse.ts) for real-time UI updates
- **Fault Tolerance**: `createDaemonRecovery()` tracks failures and enables automatic retry when the daemon returns to a healthy state

## Frequently Asked Questions

### What port does the Summarize daemon use?

The daemon listens on port **8787** by default. The extension hardcodes `http://127.0.0.1:8787` as the base URL for all daemon communication in the background script.

### Does the extension use Chrome's native messaging API?

No. According to the source code in [`apps/chrome-extension/src/entrypoints/background.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/entrypoints/background.ts), all communication uses standard HTTP `fetch` requests to localhost. This design choice simplifies the architecture by treating the local daemon as a standard web service rather than using Chrome's native messaging protocol.

### How does the extension handle streaming responses?

The extension consumes Server-Sent Events (SSE) using the `parseSseStream()` generator function defined in [`apps/chrome-extension/src/lib/sse.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/lib/sse.ts). This async generator yields parsed events from the daemon's response stream, allowing the background script to forward incremental summary chunks to the side-panel UI as they are generated.

### What happens if the daemon is offline when I request a summary?

If the daemon is unreachable, the `recordFailure()` method in [`apps/chrome-extension/src/lib/daemon-recovery.ts`](https://github.com/steipete/summarize/blob/main/apps/chrome-extension/src/lib/daemon-recovery.ts) logs the failed request. When the daemon becomes available again (verified by subsequent health checks), the `maybeRecover()` function evaluates whether to automatically retry the summarization based on the current UI state and URL matching.