> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/mercuryworkshop/scramjet/llms.txt
> Use this file to discover all available pages before exploring further.

# Architecture overview

> Understand how Scramjet's components work together to proxy web content

Scramjet is a web proxy that intercepts and rewrites web content using Service Workers. This page explains how the core components work together to create a seamless proxying experience.

## Core components

Scramjet consists of four main components that work in harmony:

```mermaid theme={null}
graph TB
    A[ScramjetController] -->|Creates & manages| B[ScramjetFrame]
    B -->|Loads proxied content| C[ScramjetClient]
    C -->|Makes requests| D[ScramjetServiceWorker]
    D -->|Intercepts & proxies| E[External Sites]
    D -->|Returns rewritten content| C
```

### Service Worker layer

The **ScramjetServiceWorker** is the heart of the proxy. It intercepts all network requests and performs the following tasks:

* Intercepts fetch requests through the Service Worker API
* Decodes proxied URLs to real destination URLs
* Fetches content using a transport layer (via `BareClient`)
* Rewrites responses (HTML, JS, CSS, etc.) before returning them
* Manages cookies through an emulated cookie store

See [ScramjetServiceWorker](/concepts/service-worker) for details.

### Client layer

The **ScramjetClient** runs in each proxied page (window or worker). It:

* Intercepts DOM APIs and JavaScript APIs to rewrite URLs
* Maintains a reference to the real URL of the current page
* Handles navigation events and URL changes
* Provides utilities for wrapping functions and trapping properties
* Manages event callbacks to ensure proper interception

The client is injected into every HTML document via script tags in the `<head>` section.

### Controller layer

The **ScramjetController** is the high-level API for initializing and managing Scramjet. It:

* Stores configuration in IndexedDB for persistence
* Provides methods to encode/decode URLs
* Creates `ScramjetFrame` instances for easy iframe management
* Dispatches global events like downloads
* Allows runtime configuration updates

Typically, you initialize one controller per application:

```typescript theme={null}
const scramjet = new ScramjetController({
  prefix: "/scramjet/",
  flags: {
    strictRewrites: true,
    captureErrors: true
  }
});
await scramjet.init();
```

### Frame abstraction

The **ScramjetFrame** provides a convenient API for managing proxied iframes:

* Automatically handles frame name generation for internal tracking
* Provides navigation methods (`go()`, `back()`, `forward()`, `reload()`)
* Exposes the `ScramjetClient` instance for the iframe
* Dispatches navigation events you can listen to

Example usage:

```typescript theme={null}
const frame = scramjet.createFrame();
document.body.appendChild(frame.frame);

frame.addEventListener("urlchange", (e) => {
  console.log("URL changed to:", e.url);
});

frame.go("https://example.com");
```

## Request flow

Here's what happens when a proxied page makes a request:

<Steps>
  <Step title="URL encoding">
    The controller or client encodes the real URL into a proxied URL using the configured codec:

    ```
    https://example.com → http://localhost/scramjet/https%3A%2F%2Fexample.com
    ```
  </Step>

  <Step title="Service Worker interception">
    The Service Worker's `fetch` event handler intercepts the request. It checks if the URL matches the configured prefix.
  </Step>

  <Step title="URL decoding">
    The Service Worker decodes the proxied URL back to the real destination URL.
  </Step>

  <Step title="Fetch via transport">
    The Service Worker fetches the content using the BareClient transport layer, which handles the actual network request.
  </Step>

  <Step title="Response rewriting">
    Based on the content type, the Service Worker rewrites the response:

    * HTML: Injects Scramjet scripts and rewrites URLs in attributes
    * JavaScript: Rewrites using the oxc-based WASM rewriter
    * CSS: Rewrites `url()` references
    * Headers: Processes and rewrites cookies, CSP, etc.
  </Step>

  <Step title="Response delivery">
    The rewritten response is returned to the client, which renders it as if it came from the real site.
  </Step>
</Steps>

## Data flow

Scramjet maintains several stores to track state across the proxy:

### IndexedDB stores

Scramjet uses IndexedDB with the following object stores:

* **config**: Stores the active `ScramjetConfig`
* **cookies**: Stores the serialized cookie jar
* **redirectTrackers**: Tracks redirect chains for referrer policy enforcement
* **referrerPolicies**: Stores referrer policy data per URL
* **publicSuffixList**: Caches the public suffix list for cookie domain validation

### Message passing

The Service Worker and clients communicate via `postMessage`:

```typescript theme={null}
// Client → Service Worker messages (MessageC2W)
type MessageC2W =
  | { scramjet$type: "registerServiceWorker"; port: MessagePort; origin: string }
  | { scramjet$type: "cookie"; cookie: string; url: string }
  | { scramjet$type: "loadConfig"; config: ScramjetConfig };

// Service Worker → Client messages (MessageW2C)  
type MessageW2C =
  | { scramjet$type: "cookie"; cookie: string; url: string }
  | { scramjet$type: "download"; download: ScramjetDownload };
```

All messages include a `scramjet$type` field for routing and an optional `scramjet$token` for request/response pairing.

## URL metadata

Throughout the rewriting process, Scramjet uses a `URLMeta` object to track context:

```typescript theme={null}
type URLMeta = {
  origin: URL;           // The real origin URL of the page
  base: URL;             // The base URL for resolving relative URLs
  topFrameName?: string; // Name of the topmost Scramjet-controlled frame
  parentFrameName?: string; // Name of the parent frame
};
```

This metadata is passed to all rewriter functions to ensure URLs are rewritten correctly based on the current page context.

<Note>
  The `topFrameName` and `parentFrameName` are critical for handling nested iframes correctly. Always use `ScramjetFrame` to create iframes instead of raw `<iframe>` elements.
</Note>

## Component initialization order

1. **ScramjetController** is instantiated with configuration
2. Controller calls `init()`, which:
   * Opens IndexedDB and stores config
   * Sends config to the Service Worker
   * Sets up message listeners
3. **ScramjetFrame** instances are created as needed
4. When a frame loads, **ScramjetClient** is injected and initialized
5. Client hooks DOM and JavaScript APIs
6. **ScramjetServiceWorker** begins intercepting requests for that client

## Type definitions

Key interfaces defined in `src/types.ts`:

<CodeGroup>
  ```typescript ScramjetConfig theme={null}
  interface ScramjetConfig {
    prefix: string;
    globals: { /* runtime function names */ };
    files: { wasm: string; all: string; sync: string };
    flags: ScramjetFlags;
    siteFlags: Record<string, Partial<ScramjetFlags>>;
    codec: { encode: string; decode: string };
  }
  ```

  ```typescript ScramjetFlags theme={null}
  type ScramjetFlags = {
    serviceworkers: boolean;
    syncxhr: boolean;
    strictRewrites: boolean;
    rewriterLogs: boolean;
    captureErrors: boolean;
    cleanErrors: boolean;
    scramitize: boolean;
    sourcemaps: boolean;
    destructureRewrites: boolean;
    interceptDownloads: boolean;
    allowInvalidJs: boolean;
    allowFailedIntercepts: boolean;
  };
  ```
</CodeGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Service Worker" icon="gear" href="/concepts/service-worker">
    Learn how requests are intercepted and proxied
  </Card>

  <Card title="URL rewriting" icon="link" href="/concepts/url-rewriting">
    Understand URL encoding, decoding, and rewriting
  </Card>

  <Card title="Configuration" icon="sliders" href="/concepts/configuration">
    Explore configuration options and flags
  </Card>

  <Card title="API Reference" icon="code" href="/api/scramjet-controller">
    Browse the complete API documentation
  </Card>
</CardGroup>
