gstack Command Dispatch Categorization: READ, WRITE, and META Explained

The gstack browser daemon categorizes every incoming command into one of three canonical sets—READ, WRITE, or META—to determine the execution path, apply appropriate content filtering, and enforce permission boundaries.

The gstack project (garrytan/gstack) implements a sophisticated command dispatch system that classifies browser automation instructions based on their side effects and scope. This categorization, defined in the source of truth at browse/src/commands.ts, enables the daemon to route requests through specialized handlers for data extraction, state modification, or meta-level control operations.

The Three Command Categories

The classification system partitions all available commands into READ, WRITE, and META sets. Each category determines how the server processes the request and whether the operation mutates browser state or daemon configuration.

READ Commands

READ commands extract information from the current page without causing side effects or modifying state. These operations are considered safe for repeated execution and are routed through the handleReadCommand path.

The READ_COMMANDS set includes: text, html, links, forms, accessibility, js, eval, css, attrs, console, network, cookies, storage, perf, dialog, media, data, inspect.

According to the source in browse/src/server.ts, when a command matches this set, the server applies content-filtering and wrapping before returning untrusted external content to the caller.

WRITE Commands

WRITE commands modify the page, browser state, or environment. These operations trigger the handleWriteCommand path and may alter DOM elements, navigation history, cookies, or perform file downloads.

The WRITE_COMMANDS set includes: goto, back, forward, reload, load-html, click, fill, select, hover, type, press, scroll, wait, viewport, cookie, header, useragent, upload, dialog-accept, dialog-dismiss, style, cleanup, prettyscreenshot, download, scrape, archive.

META Commands

META commands control the daemon itself, manage tabs, handle snapshots, or provide auxiliary services. These operations invoke handleMetaCommand and can affect the lifecycle of browser instances or orchestrate multi-step workflows.

The META_COMMANDS set includes: tabs, tab, tab-each, newtab, closetab, status, stop, restart, screenshot, pdf, responsive, chain, url, snapshot, handoff, resume, connect, disconnect, focus, inbox, watch, state, frame, ux-audit, domain-skill, skill, cdp.

Runtime Dispatch Implementation

The categorization logic executes at runtime in browse/src/server.ts using simple set membership checks:

if (READ_COMMANDS.has(command)) {
  // Route through read-path: handleReadCommand
} else if (WRITE_COMMANDS.has(command)) {
  // Route through write-path: handleWriteCommand
} else if (META_COMMANDS.has(command)) {
  // Route through meta-path: handleMetaCommand
}

This dispatch mechanism ensures that READ operations receive appropriate content wrapping for security, WRITE operations execute with proper state management, and META operations can access privileged daemon APIs. The browse/src/token-registry.ts file leverages these same categories to map token scopes onto permission checks, ensuring that authorization boundaries align with command classifications.

Practical Usage Examples

Execute commands through the gstack CLI using the B shorthand:


# READ example – extract clean page text without side effects

$ B text
--- BEGIN UNTRUSTED EXTERNAL CONTENT (source: https://example.com) ---
Lorem ipsum dolor sit amet…
--- END UNTRUSTED EXTERNAL CONTENT ---

# WRITE example – navigate to a new URL (modifies browser state)

$ B goto https://example.com

# META example – create a snapshot of the accessibility tree

$ B snapshot -i
{
  "elements": [  ],
  "refs": { "@e1": "button.submit", }
}

Programmatic Category Detection

Import the canonical command sets from browse/src/commands.ts to categorize commands programmatically:

import { READ_COMMANDS, WRITE_COMMANDS, META_COMMANDS } from './browse/src/commands';

function categorize(cmd: string): 'READ' | 'WRITE' | 'META' | 'UNKNOWN' {
  if (READ_COMMANDS.has(cmd)) return 'READ';
  if (WRITE_COMMANDS.has(cmd)) return 'WRITE';
  if (META_COMMANDS.has(cmd)) return 'META';
  return 'UNKNOWN';
}

The test suite in test/skill-validation.test.ts validates that the union of these three sets covers every documented command, ensuring the categorization remains exhaustive as the API evolves.

Summary

  • READ commands (text, html, links, etc.) extract data without side effects and route through handleReadCommand with content filtering.
  • WRITE commands (goto, click, fill, etc.) mutate page or browser state and execute via handleWriteCommand.
  • META commands (snapshot, tabs, skill, etc.) control the daemon lifecycle and tab management through handleMetaCommand.
  • The canonical definitions live in browse/src/commands.ts, while the runtime dispatch logic resides in browse/src/server.ts.

Frequently Asked Questions

How does gstack determine which handler to use for a command?

The server performs set membership checks against READ_COMMANDS, WRITE_COMMANDS, and META_COMMANDS exported from browse/src/commands.ts. Depending on which Set contains the command string, the server invokes handleReadCommand, handleWriteCommand, or handleMetaCommand respectively.

Can I extend the gstack command categories with custom commands?

Yes, you can modify browse/src/commands.ts to add entries to the existing Sets, though you must implement corresponding logic in browse/src/server.ts to handle the new commands. The validation tests in test/skill-validation.test.ts will help ensure your additions maintain categorization coverage.

Why does gstack distinguish between READ and WRITE operations?

The separation enables security controls like content filtering for external data (applied to READ results) and permission scoping via the token registry in browse/src/token-registry.ts. It also allows the daemon to optimize caching and logging, since READ operations are idempotent while WRITE operations require state synchronization.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →