# Protocol for Message Passing Between Webview and VS Code Extension Host in Secure Design

> Discover the secure JSON protocol for webview to VS Code extension host message passing. Learn how vscode.postMessage and onDidReceiveMessage enable seamless communication.

- Repository: [Harold Martin/secure-design](https://github.com/hbmartin/secure-design)
- Tags: architecture
- Published: 2026-03-03

---

**The Secure Design extension uses a simple JSON-based command protocol where the webview calls `vscode.postMessage()` to send commands to the extension host, which processes requests via `onDidReceiveMessage` and responds through `webview.postMessage()`.**

The `hbmartin/secure-design` repository implements a bidirectional communication layer between its React-based webview UI (rendering the Chat sidebar and Canvas) and the TypeScript extension host backend. This protocol enables the UI to request file operations, trigger LLM prompts, and synchronize state while keeping the webview sandboxed according to VS Code security architecture.

## How the Message Protocol Works

### Webview to Extension Host (Outbound)

The webview obtains a reference to the VS Code API by calling `const vscode = acquireVsCodeApi();` at initialization. When the UI requires backend action—such as loading design files or submitting a prompt—it constructs a plain JavaScript object containing a `command` property and posts it via `vscode.postMessage(message)`.

In [`src/webview/components/CanvasView.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx), eight distinct `postMessage` calls dispatch commands including:

- **`loadDesignFiles`** – Requests the host to read workspace design files and stream them back
- **`selectDesign`** – Notifies the host that the user selected a specific design element via `{ command: 'selectDesign', designId: string }`
- **`clearContext`** – Signals the host to reset stored LLM context
- **`prompt`** – Transmits user-typed text to the LLM service via `{ command: 'prompt', text: string }`
- **`setTheme`** – Persists theme configuration with `{ command: 'setTheme', theme: ThemeData }`

### Extension Host to Webview (Inbound)

The extension host creates a `WebviewPanel` in [`src/SuperdesignCanvasPanel.ts`](https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts) and registers an inbound message handler:

```typescript
this._panel.webview.onDidReceiveMessage(async message => {
  switch (message.command) {
    case 'loadDesignFiles':
      const designs = await this._designService.getAllDesigns();
      this._panel.webview.postMessage({
        type: 'designData',
        designs,
      });
      break;
    // … additional command handlers …
  }
});

```

After processing, the host responds using `this._panel.webview.postMessage(response)`, where the response object contains a `type` field identifying the payload category.

### Complete Message Flow

1. **User interaction** triggers a React component to build a command object and call `vscode.postMessage`.
2. **Extension host** receives it via `onDidReceiveMessage`, switches on `message.command`, and executes the requested work (file I/O, LLM calls).
3. **Host responds** with `webview.postMessage` containing a typed payload.
4. **Webview** receives the response via `window.addEventListener('message', …)` in [`src/webview/index.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx) and updates React state accordingly.

## Key Commands and Message Types

| Direction | Identifier | Payload Structure | Purpose |
|-----------|------------|-------------------|---------|
| Webview → Host | `loadDesignFiles` | `{ command: 'loadDesignFiles' }` | Request workspace design file enumeration |
| Webview → Host | `selectDesign` | `{ command: 'selectDesign', designId: string }` | Notify selection of specific design element |
| Webview → Host | `clearContext` | `{ command: 'clearContext' }` | Reset LLM conversation context |
| Webview → Host | `prompt` | `{ command: 'prompt', text: string }` | Submit user prompt to LLM service |
| Webview → Host | `setTheme` | `{ command: 'setTheme', theme: ThemeData }` | Persist theme configuration |
| Host → Webview | `designData` | `{ type: 'designData', designs: DesignFile[] }` | Deliver design file list to render |
| Host → Webview | `status` | `{ type: 'status', message: string }` | Display temporary info banner |
| Host → Webview | `error` | `{ type: 'error', error: string }` | Propagate exception for user feedback |
| Host → Webview | `contextUpdate` | `{ type: 'contextUpdate', context: string }` | Synchronize LLM context after tool execution |

## Implementation Examples

### Posting a Message from the Webview

In [`src/webview/components/CanvasView.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx), the UI requests design files:

```typescript
const vscode = acquireVsCodeApi();
const loadMessage = { command: 'loadDesignFiles' };
vscode.postMessage(loadMessage);

```

### Handling Messages in the Extension Host

From [`src/SuperdesignCanvasPanel.ts`](https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts), the host processes the request and replies:

```typescript
this._panel.webview.onDidReceiveMessage(async message => {
  switch (message.command) {
    case 'loadDesignFiles':
      const designs = await this._designService.getAllDesigns();
      this._panel.webview.postMessage({
        type: 'designData',
        designs,
      });
      break;
    // … other commands …
  }
});

```

### Receiving Responses in the Webview

In [`src/webview/index.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx), the React application listens for host responses:

```typescript
useEffect(() => {
  const listener = (event: MessageEvent) => {
    const msg = event.data;
    if (msg.type === 'designData') {
      setDesigns(msg.designs);
    } else if (msg.type === 'error') {
      showError(msg.error);
    }
    // … handle other types …
  };
  window.addEventListener('message', listener);
  return () => window.removeEventListener('message', listener);
}, []);

```

## Source Files and Architecture

| File | Role | GitHub Link |
|------|------|-------------|
| [`src/webview/components/CanvasView.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx) | React component dispatching commands via `vscode.postMessage` and rendering design data. | https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx |
| [`src/SuperdesignCanvasPanel.ts`](https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts) | Extension host class creating the `WebviewPanel`, registering `onDidReceiveMessage`, and posting responses. | https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts |
| [`src/webview/index.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx) | Webview entry point setting up the global `message` event listener to bridge VS Code API events into React state. | https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx |
| [`src/webview/components/DesignPanel.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/components/DesignPanel.tsx) | Secondary UI component also utilizing `vscode.postMessage` for design‑specific interactions. | https://github.com/hbmartin/secure-design/blob/main/src/webview/components/DesignPanel.tsx |

## Summary

- The Secure Design extension implements a **JSON command protocol** for webview-extension host communication.
- **Outbound messages** use `acquireVsCodeApi().postMessage()` with a `command` property to trigger host actions like `loadDesignFiles` or `prompt`.
- **Inbound messages** are captured by `onDidReceiveMessage` in [`SuperdesignCanvasPanel.ts`](https://github.com/hbmartin/secure-design/blob/main/SuperdesignCanvasPanel.ts), processed, and returned via `webview.postMessage()` with a `type` field such as `designData` or `error`.
- The webview listens for responses via `window.addEventListener('message', …)` in [`src/webview/index.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx) to update React application state.

## Frequently Asked Questions

### How does the webview acquire the VS Code API?

The webview calls `const vscode = acquireVsCodeApi();` at initialization. This function is injected into the webview global scope by VS Code and provides the `postMessage` method used to transmit commands to the extension host.

### What happens if a message command is not recognized by the extension host?

The extension host’s `onDidReceiveMessage` handler in [`SuperdesignCanvasPanel.ts`](https://github.com/hbmartin/secure-design/blob/main/SuperdesignCanvasPanel.ts) uses a `switch` statement on `message.command`. If the command does not match any defined case, the message is silently ignored; no error response is automatically generated unless explicitly implemented in a `default` case.

### Can the protocol handle binary data or file transfers?

The current protocol transmits plain JSON objects. Binary data or large file contents are not sent as raw binary; instead, the extension host reads files using Node.js APIs and transmits the serialized content (e.g., base64 strings or JSON‑serializable objects) within the `payload` or `designs` fields of the message structure.

### How are errors propagated back to the webview UI?

When the extension host encounters an exception during command processing, it catches the error and responds with a message object containing `type: 'error'` and an `error` string property. The webview’s global message listener in [`src/webview/index.tsx`](https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx) detects this type and routes it to the error display logic, presenting feedback to the user without reloading the webview.