# gstack Administrator & Developer Guide

gstack (Garry's Stack) is an all-in-one AI engineering toolkit built on Bun, providing headless browser automation, AI-powered design generation, PDF rendering, and 30+ Claude Code slash-command skills. This guide covers architecture, configuration, deployment, security, and developer internals for administrators and contributors working with gstack v1.10.1.0.

---

## 1. Architecture Overview

### 1.1 System Design

gstack is organized as a monorepo with four primary subsystems that share a common Bun runtime:

```
┌─────────────────────────────────────────────────────────────┐
│                        gstack CLI                           │
│  browse/src/cli.ts → HTTP → browse/src/server.ts (Bun)     │
│                              ├── BrowserManager (Playwright)│
│                              ├── Security Classifier        │
│                              ├── Activity Stream (SSE)      │
│                              ├── Token Registry (multi-agent)│
│                              └── Sidebar Agent (Claude)      │
├─────────────────────────────────────────────────────────────┤
│  design/src/cli.ts → OpenAI GPT-4o Image API               │
│  make-pdf/src/cli.ts → browse CLI → Chromium PDF           │
├─────────────────────────────────────────────────────────────┤
│  Skills System                                               │
│  ├── SKILL.md.tmpl (templates with resolver placeholders)    │
│  ├── scripts/resolvers/ (dynamic content generators)         │
│  ├── scripts/gen-skill-docs.ts (multi-host doc generation)   │
│  └── hosts/ (per-host config: path rewrites, frontmatter)    │
├─────────────────────────────────────────────────────────────┤
│  Supabase Backend                                            │
│  ├── telemetry-ingest (edge function)                        │
│  ├── community-pulse (edge function, cached)                 │
│  ├── update-check (edge function)                            │
│  └── Migrations (001-004): telemetry, RLS, upsert, attack   │
├─────────────────────────────────────────────────────────────┤
│  Chrome Extension                                            │
│  ├── background.js (health polling, command proxy, refs)     │
│  ├── sidepanel.js (activity stream, chat)                    │
│  ├── content.js (indicator injection, inspector)             │
│  └── inspector.js (element picking)                          │
└─────────────────────────────────────────────────────────────┘
```

**browse** is the centerpiece: a persistent HTTP server wrapping Playwright-driven Chromium. The CLI (`browse/src/cli.ts`) is a thin client that sends commands over HTTP to the long-running server (`browse/src/server.ts`). This architecture means the browser persists across individual command invocations, preserving page state, cookies, and sessions.

**design** is a standalone tool that calls the OpenAI GPT-4o Responses API for image generation, with iterative refinement, vision-based quality gates, and gallery management.

**make-pdf** orchestrates Markdown-to-PDF conversion by rendering HTML through the browse server's Chromium instance, using print-optimized CSS.

**Skills** are template-driven slash commands. Each skill is a `.md.tmpl` file with resolver placeholders (e.g., `{{BROWSE_COMMAND_REFERENCE}}`) that get expanded at generation time by TypeScript resolvers. The `gen-skill-docs.ts` script produces host-specific versions for each supported AI coding environment.

### 1.2 Process Model

The browse server uses Bun's HTTP server with a single-threaded event loop. Key architectural decisions:

- **Persistent server**: The browse server starts once and stays alive, with an idle timeout (`BROWSE_IDLE_TIMEOUT`). CLI commands talk to it over localhost HTTP.
- **Token-based auth**: The server generates a random token on startup, writes it to a state file, and all `/command` requests require `Bearer <token>`.
- **Tab sessions**: Each browser tab is managed by a `TabSession` instance (`browse/src/tab-session.ts`) that tracks refs, frames, and loaded HTML independently.
- **Watchdog**: The CLI can set `BROWSE_PARENT_PID` so the server auto-exits when the parent process dies (set to `0` to disable).

### 1.3 Technology Stack

| Technology | Version | Purpose |
|-----------|---------|---------|
| Bun | >=1.0.0 | Runtime, package manager, test runner, compiler |
| TypeScript | — | Primary language across all components |
| Playwright | ^1.58.2 | Browser automation (Chromium) |
| puppeteer-core | ^24.40.0 | Additional browser CDP APIs |
| @huggingface/transformers | ^4.1.0 | ML model loading for security classifier |
| @ngrok/ngrok | ^1.7.0 | Tunneling for remote browser access |
| marked | ^18.0.2 | Markdown parsing (make-pdf) |
| diff | ^7.0.0 | Text diffing |
| OpenAI GPT-4o | — | Design image generation + vision quality gate |
| @anthropic-ai/claude-agent-sdk | 0.2.117 | Agent SDK for sidebar agent (dev dependency) |
| @anthropic-ai/sdk | ^0.78.0 | Anthropic API (dev dependency) |
| Supabase | Deno edge functions | Backend telemetry + community stats |
| Chrome Extension Manifest V3 | — | Browser integration side panel |

### 1.4 Module Reference

#### browse/src/ — Core Browser Engine

| Module | Responsibility |
|--------|---------------|
| `server.ts` | Persistent HTTP server — routes, lifecycle, port discovery |
| `cli.ts` | Thin CLI client — argument parsing, server auto-start |
| `browser-manager.ts` | `BrowserManager` class — Chromium lifecycle, tab management, viewport, context |
| `tab-session.ts` | `TabSession` class — per-tab refs, frames, loaded HTML state |
| `read-commands.ts` | Read-only commands: `js`, `eval`, `css`, `attrs`, `console`, `network`, `source`, `text`, `refs` |
| `write-commands.ts` | Mutating commands: `click`, `fill`, `select`, `goto`, `load-html`, `upload`, `scroll`, `hover`, `wait` |
| `meta-commands.ts` | Utility commands: `pdf`, `screenshot`, `tabs`, `state`, `frame`, `mhtml` |
| `snapshot.ts` | Accessibility tree extraction + annotated screenshots |
| `security-classifier.ts` | ML ensemble for prompt injection detection |
| `content-security.ts` | Content filtering — marks output between `BEGIN/END UNTRUSTED` markers |
| `url-validation.ts` | URL allowlist/blocklist, cloud metadata blocking, `file://` handling |
| `path-security.ts` | File path validation — enforces safe directory allowlist |
| `activity.ts` | SSE real-time activity feed for Chrome extension |
| `audit.ts` | Append-only JSONL audit trail at `.gstack/browse-audit.jsonl` |
| `network-capture.ts` | `SizeCappedBuffer` for network request/response logging |
| `buffers.ts` | `CircularBuffer` for console, network, and dialog capture |
| `token-registry.ts` | Multi-agent token management with read/write/admin/meta/control scopes |
| `tunnel-denial-log.ts` | Logs denied tunnel access attempts |
| `sse-session-cookie.ts` | Session tracking for SSE connections |
| `sidebar-agent.ts` | Claude agent powering the Chrome sidebar chat |
| `cookie-import-browser.ts` | Import cookies from installed browsers (Chrome, Firefox, etc.) |
| `cookie-picker-routes.ts` / `cookie-picker-ui.ts` | Cookie selection UI and HTTP routes |
| `config.ts` | `resolveConfig()` — state dir, project detection |
| `platform.ts` | OS detection, temp directories |
| `error-handling.ts` | `safeUnlink`, `safeKill`, `isProcessAlive` utilities |
| `find-browse.ts` | Binary discovery for external callers |
| `media-extract.ts` | Media URL extraction from pages |
| `welcome.html` | Default page served in new tabs |

#### design/src/ — AI Design Tool

| Module | Responsibility |
|--------|---------------|
| `cli.ts` | Entry point, subcommand routing |
| `auth.ts` | OpenAI API key resolution (env, config file) |
| `generate.ts` | Image generation via OpenAI Responses API |
| `iterate.ts` | Iterative design refinement with feedback loop |
| `evolve.ts` | Design evolution using vision feedback |
| `check.ts` | Vision-based quality gate (GPT-4o evaluates output) |
| `compare.ts` | Side-by-side design comparison |
| `gallery.ts` | Design gallery management |
| `serve.ts` | Local gallery HTTP server |
| `brief.ts` | Structured design brief interface |
| `session.ts` | Design session persistence |
| `memory.ts` | Design memory/preferences |
| `variants.ts` | A/B variant generation |
| `diff.ts` | Visual diff between designs |
| `design-to-code.ts` | Convert mockups to implementation code |

#### make-pdf/src/ — PDF Rendering Pipeline

| Module | Responsibility |
|--------|---------------|
| `cli.ts` | Entry point, argument parsing |
| `orchestrator.ts` | `ProgressReporter` class — multi-file PDF orchestration |
| `render.ts` | Markdown to HTML to PDF rendering chain |
| `browseClient.ts` | Typed shell-out wrapper for browse CLI |
| `pdftotext.ts` | PDF text extraction for verification |
| `smartypants.ts` | Typography improvement (curly quotes, dashes) |
| `print-css.ts` | Print-optimized CSS injection |
| `setup.ts` | One-time setup routine |
| `types.ts` | `BrowseClientError` and shared types |

#### lib/ — Shared Libraries

| Module | Responsibility |
|--------|---------------|
| `worktree.ts` | `WorktreeManager` class — Git worktree management for isolated testing |

#### scripts/ — Build & Generation

| Module | Responsibility |
|--------|---------------|
| `gen-skill-docs.ts` | Multi-host skill documentation generation |
| `resolvers/index.ts` | Template resolver registry |
| `resolvers/browse.ts` | Command reference, snapshot flags, setup instructions |
| `resolvers/composition.ts` | `INVOKE_SKILL` cross-skill composition |
| `resolvers/confidence.ts` | Finding confidence calibration |
| `resolvers/constants.ts` | AI slop blacklist, design constants |
| `resolvers/design.ts` | Design review integration |
| `resolvers/codex-helpers.ts` | Codex frontmatter transformation |
| `resolvers/model-overlay.ts` | Model-specific behavior overlays |
| `resolvers/review.ts` | Code review resolver |
| `resolvers/review-army.ts` | Multi-agent review orchestration |
| `resolvers/testing.ts` | Test strategy resolver |
| `resolvers/gbrain.ts` | GBrain integration resolver |
| `resolvers/learnings.ts` | Cross-session learning resolver |
| `resolvers/make-pdf.ts` | PDF generation resolver |
| `resolvers/preamble.ts` | Skill preamble generation |
| `resolvers/question-tuning.ts` | Question quality tuning |
| `resolvers/dx.ts` | Developer experience resolver |
| `resolvers/utility.ts` | Utility helpers |
| `host-adapters/openclaw-adapter.ts` | OpenClaw-specific adapter |

---

## 2. System Requirements

### 2.1 Runtime Requirements

| Requirement | Minimum | Recommended |
|------------|---------|-------------|
| Bun | >=1.0.0 | Latest stable |
| OS | macOS, Linux, Windows (WSL or native) | macOS or Linux |
| RAM | 2 GB free | 4+ GB (Chromium is memory-hungry) |
| Disk | 500 MB (Chromium + dependencies) | 1+ GB with cache |
| Network | Required for design tool, telemetry, tunneling | Broadband for headed browser use |

### 2.2 External Service Dependencies

| Service | Required For | Authentication |
|---------|-------------|----------------|
| OpenAI API | `design` subcommands | `OPENAI_API_KEY` env var |
| Anthropic API | Sidebar agent (dev) | `ANTHROPIC_API_KEY` env var |
| ngrok | Remote tunnel access | `NGROK_AUTHTOKEN` env var |
| Supabase | Telemetry, community pulse, update checks | Public anon key (in `supabase/config.sh`) |

> [!NOTE]
> The browse server itself requires no external API keys. It runs entirely locally with Playwright-managed Chromium. API keys are only needed for the design tool (OpenAI) and optional features like tunneling (ngrok) or sidebar chat (Anthropic).

### 2.3 Supported AI Coding Hosts

gstack generates host-specific skill files for these environments:

| Host | Config File | Notes |
|------|------------|-------|
| Claude Code | `hosts/claude.ts` | Primary target |
| OpenAI Codex CLI | `hosts/codex.ts` | Codex frontmatter transformation |
| Cursor | `hosts/cursor.ts` | — |
| Factory Droid | `hosts/factory.ts` | — |
| Kiro | `hosts/kiro.ts` | — |
| OpenCode | `hosts/opencode.ts` | — |
| Slate | `hosts/slate.ts` | — |
| OpenClaw | `hosts/openclaw.ts` | Custom adapter in `scripts/host-adapters/` |
| Hermes | `hosts/hermes.ts` | — |
| GBrain | `hosts/gbrain.ts` | Dedicated resolver |

---

## 3. Configuration Reference

gstack is configured entirely through environment variables. There are no YAML or TOML configuration files.

### 3.1 Browse Server Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `BROWSE_PORT` | Auto-discovered | Fix the server to a specific port |
| `BROWSE_IDLE_TIMEOUT` | Server default | Idle timeout before auto-shutdown (seconds) |
| `BROWSE_HEADED` | `0` | Set to `1` for headed Chromium (visible browser window) |
| `BROWSE_HEADLESS_SKIP` | `0` | Skip headless browser launch entirely |
| `BROWSE_PARENT_PID` | Parent PID | PID to watch — server exits when this process dies. Set `0` to disable watchdog |
| `BROWSE_NO_AUTOSTART` | `0` | Prevent CLI from auto-starting the server |
| `BROWSE_TAB` | — | Pin CLI operations to a specific tab ID |
| `BROWSE_SERVER_PORT` | — | Server port for sidebar agent communication |
| `BROWSE_STATE_FILE` | `.gstack/browse-state.json` | Override path for server state file |
| `BROWSE_EXTENSIONS_DIR` | — | Directory containing Chrome extensions to load |
| `GSTACK_CHROMIUM_PATH` | Playwright default | Path to a custom Chromium binary |
| `GSTACK_BROWSE_MAX_HTML_BYTES` | — | Maximum HTML payload size for `load-html` command |
| `GSTACK_SLUG` | Auto-detected | Project slug identifier |
| `GSTACK_SKILL_ROOT` | — | Custom root directory for skill discovery |

> [!TIP]
> For CI/CD pipelines, `BROWSE_PARENT_PID=0` prevents the watchdog from killing the server if the parent shell exits. Combine with `BROWSE_PORT` for deterministic port assignment in parallel test environments.

### 3.2 Security Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `GSTACK_SECURITY_OFF` | `0` | Set to `1` to disable the ML security classifier entirely |
| `GSTACK_SECURITY_ENSEMBLE` | All layers | Configure which ML ensemble layers to activate |
| `GSTACK_HAIKU_TIMEOUT_MS` | — | Timeout in ms for the Haiku classifier layer |
| `BROWSE_CONTENT_FILTER` | — | Content security filter mode for untrusted web content |

> [!WARNING]
> Setting `GSTACK_SECURITY_OFF=1` disables prompt injection detection entirely. This removes the ML ensemble that protects against malicious web content being injected into AI agent conversations. Only disable for debugging or in fully trusted environments.

### 3.3 Tunnel Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `BROWSE_TUNNEL` | `0` | Enable ngrok tunnel for remote access |
| `NGROK_AUTHTOKEN` | — | ngrok authentication token (required for tunneling) |
| `NGROK_DOMAIN` | — | Custom ngrok domain |

### 3.4 Design Tool Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `OPENAI_API_KEY` | — | OpenAI API key for GPT-4o image generation and vision |
| `HOME` | System default | Home directory (used for API key file resolution) |

### 3.5 PDF Tool Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `BROWSE_BIN` | Auto-discovered | Override path to the browse binary |
| `PDFTOTEXT_BIN` | Auto-discovered | Override path to the `pdftotext` binary |

### 3.6 Platform / Environment Detection

| Variable | Default | Description |
|----------|---------|-------------|
| `CI` | — | When set, adds `--no-sandbox` to Chromium launch args |
| `CONTAINER` | — | When set, adds `--no-sandbox` to Chromium launch args |
| `PROGRAMFILES` | System default | Windows Program Files path (cookie import) |
| `USERPROFILE` | System default | Windows user profile path |

### 3.7 Sidebar Agent Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `SIDEBAR_QUEUE_PATH` | — | File path for sidebar agent message queue |
| `SIDEBAR_AGENT_TIMEOUT` | — | Timeout for sidebar agent operations |
| `BROWSE_SERVER_PORT` | — | Port the sidebar agent uses to connect to the browse server |

### 3.8 Testing & Eval Configuration

| Variable | Default | Description |
|----------|---------|-------------|
| `EVALS` | — | Enable eval test mode |
| `EVALS_ALL` | — | Run all eval tiers |
| `EVALS_BASE` | — | Base branch for eval comparison |
| `EVALS_TIER` | — | Eval tier selection (`gate` or `periodic`) |
| `EVALS_MODEL` | — | Model override for eval runs |
| `ANTHROPIC_API_KEY` | — | Anthropic API key for eval judge |
| `GSTACK_SDK_MAX_CONCURRENCY` | — | Maximum concurrent SDK sessions in test harness |

> [!NOTE]
> The eval system supports tiered execution: `gate` evals run on every PR, while `periodic` evals run on a schedule. Set `EVALS_ALL=1` to force all tiers in a single run.

---

## 4. Deployment & Scaling

### 4.1 Installation

```bash
# Clone the repository
git clone https://github.com/garrytan/gstack.git
cd gstack

# Install dependencies (Bun handles everything)
bun install

# Build the browse server bundle (required for Windows)
bun run build
```

### 4.2 Browse Server Lifecycle

The browse server follows a specific lifecycle:

1. **Auto-start**: When the CLI sends a command and no server is running, it auto-starts one (unless `BROWSE_NO_AUTOSTART` is set).
2. **Port discovery**: The server tries a range of ports, or uses `BROWSE_PORT` if set. If the port is taken, it fails with a clear error.
3. **State file**: On startup, the server writes its PID, port, and auth token to the state file (default: `.gstack/browse-state.json`).
4. **Idle timeout**: After `BROWSE_IDLE_TIMEOUT` seconds of no requests, the server shuts down.
5. **Watchdog**: If `BROWSE_PARENT_PID` is set and that process dies, the server exits.

```bash
# Start server explicitly with headed Chromium
BROWSE_HEADED=1 BROWSE_PORT=9222 bun run browse/src/server.ts

# Start with tunneling enabled
BROWSE_TUNNEL=1 NGROK_AUTHTOKEN=your_token bun run browse/src/server.ts
```

### 4.3 CI/CD Integration

For CI environments:

```bash
# Recommended CI environment variables
export CI=1                    # Adds --no-sandbox for containerized Chromium
export BROWSE_PARENT_PID=0     # Disable watchdog
export BROWSE_PORT=9222        # Fixed port for predictability
export GSTACK_SECURITY_OFF=1   # Optional: skip ML classifier in trusted CI
```

> [!IMPORTANT]
> Chromium requires `--no-sandbox` inside Docker containers and most CI runners. gstack automatically adds this flag when `CI=1` or `CONTAINER=1` is set. Without it, the browser will fail to launch with a sandbox-related error.

### 4.4 Container Deployment

The browse server can run in Docker. Key considerations:

- Mount a volume for `.gstack/` to persist state across restarts
- Expose the browse server port
- Set `CONTAINER=1` for automatic `--no-sandbox`
- Chromium needs shared memory — use `--shm-size=2g` or mount `/dev/shm`

```dockerfile
FROM oven/bun:latest

WORKDIR /app
COPY . .
RUN bun install

ENV CONTAINER=1
ENV BROWSE_PORT=9222

EXPOSE 9222

CMD ["bun", "run", "browse/src/server.ts"]
```

```bash
docker run -d \
  --shm-size=2g \
  -p 9222:9222 \
  -e CONTAINER=1 \
  -e BROWSE_PORT=9222 \
  -v gstack-state:/app/.gstack \
  gstack-browse
```

### 4.5 Multi-Agent Deployment

gstack supports multiple AI agents sharing a single browse server through the token registry:

1. The primary agent starts the server and gets the admin token from the state file.
2. Additional agents connect via the `/pair` endpoint to get scoped tokens.
3. Token scopes (`read`, `write`, `admin`, `meta`, `control`) restrict what each agent can do.

This enables architectures where a supervisor agent delegates browsing tasks to worker agents, each with least-privilege access.

### 4.6 Remote Access via ngrok

For accessing the browse server from remote machines:

```bash
export BROWSE_TUNNEL=1
export NGROK_AUTHTOKEN=your_ngrok_token
export NGROK_DOMAIN=custom.ngrok.io  # Optional: custom domain

# Server will log the tunnel URL on startup
bun run browse/src/server.ts
```

> [!CAUTION]
> The ngrok tunnel exposes your browse server to the internet. While token-based authentication protects the `/command` endpoint, ensure your ngrok account has appropriate access controls. Denied tunnel access attempts are logged by `tunnel-denial-log.ts`.

---

## 5. Operations

### 5.1 HTTP API Endpoints

The browse server exposes these HTTP endpoints:

| Method | Path | Auth | Purpose |
|--------|------|------|---------|
| POST | `/command` | Bearer token | Execute a browse command |
| GET | `/health` | None | Server health check |
| POST | `/connect` | None | Initial connection handshake |
| POST | `/pair` | Bearer token | Register a new agent with scoped token |
| GET | `/refs` | Bearer token | Get current page element references |
| GET | `/activity/stream` | Bearer token | SSE real-time activity feed |
| GET | `/activity/history` | Bearer token | Activity history |
| GET | `/sidebar-tabs` | Bearer token | Sidebar tab listing |
| POST | `/tunnel/start` | Bearer token | Start ngrok tunnel |
| GET | `/file` | Bearer token | Serve local files |
| POST | `/batch` | Bearer token | Execute multiple commands in sequence |

### 5.2 Server State File

The browse server persists its state to `.gstack/browse-state.json` (or the path in `BROWSE_STATE_FILE`). This file contains:

- Server PID
- Server port
- Authentication token
- Server start timestamp

The CLI reads this file to discover and authenticate with a running server. If the state file is stale (PID no longer alive), the CLI starts a new server.

### 5.3 Audit Log

Every command execution is logged to `.gstack/browse-audit.jsonl` as append-only JSONL. Each entry includes:

- Timestamp
- Command name and arguments
- Agent token (anonymized)
- Result status
- Execution duration

This provides a forensic trail for debugging multi-agent interactions and security incidents.

### 5.4 Telemetry

gstack sends anonymous telemetry to Supabase edge functions:

- **telemetry-ingest**: Batch insertion of usage events (max 100 events, max 50KB per request)
- **community-pulse**: Aggregated community statistics (cached for 1 hour)
- **update-check**: Version ping that logs installation info

Supabase configuration (public, non-secret) lives in `supabase/config.sh`.

> [!NOTE]
> Telemetry is anonymous. Session tracking is gated by telemetry configuration (see issue #1150 for the gating fix).

### 5.5 Upgrade System

gstack includes a versioned migration system in `gstack-upgrade/migrations/`:

| Version | Migration | What It Does |
|---------|-----------|-------------|
| v0.15.2.0 | Fix skill directory structure | Runs `gstack-relink` for unprefixed skill discovery |
| v0.16.2.0 | Merge resource logs | Migrates per-project `resources-shown.jsonl` to unified `builder-profile.jsonl` |
| v1.0.0.0 | V1 writing style | Prompts for ELI10 vs terse writing style preference |
| v1.1.3.0 | Remove /checkpoint | Uninstalls stale `/checkpoint` skill, replaces with `/context-save` + `/context-restore` |

Migrations run automatically during `gstack` updates and are idempotent.

### 5.6 Monitoring

**Health check:**

```bash
curl http://localhost:9222/health
```

**Process monitoring:**

```bash
# Check if server is alive using state file
cat .gstack/browse-state.json | jq '.pid' | xargs kill -0
```

**Audit log tail:**

```bash
tail -f .gstack/browse-audit.jsonl | jq .
```

---

## 6. Performance

### 6.1 Browser Resource Management

The `BrowserManager` manages Chromium's resource consumption:

- **Tab limit**: Each tab is a `TabSession` with its own page context. More tabs means more memory.
- **Circular buffers**: Console and network captures use `CircularBuffer` (fixed-size ring buffers) to prevent unbounded memory growth.
- **Size-capped network capture**: `SizeCappedBuffer` in `network-capture.ts` caps total captured network data.
- **Idle timeout**: The server auto-shuts down after inactivity, freeing all Chromium resources.

### 6.2 Chromium Launch Optimization

The server applies platform-specific Chromium flags:

- `--no-sandbox` in CI/container environments
- Anti-detection patches in headed mode (navigator.webdriver, plugins, languages, CDP artifacts, Permissions API)
- Custom Chromium binary via `GSTACK_CHROMIUM_PATH` for pre-warmed or optimized builds

### 6.3 PDF Rendering Performance

The make-pdf pipeline chains Markdown parsing (marked) through HTML rendering to Chromium's PDF output. For large documents:

- The `ProgressReporter` class in `orchestrator.ts` provides real-time feedback during multi-file orchestration
- Print-optimized CSS (`print-css.ts`) ensures clean page breaks and typography
- `smartypants.ts` applies typography improvements (curly quotes, em-dashes) in a single pass

### 6.4 Security Classifier Overhead

The ML security ensemble adds latency to command processing. If this is a bottleneck in trusted environments:

- Set `GSTACK_SECURITY_OFF=1` to disable entirely
- Configure `GSTACK_SECURITY_ENSEMBLE` to activate only specific layers
- Adjust `GSTACK_HAIKU_TIMEOUT_MS` to cap the Haiku classifier timeout

> [!TIP]
> In development, the security classifier can add noticeable latency to the first request while ML models load. Subsequent requests use cached models. Pre-warm the server by sending a no-op command after startup.

---

## 7. Security

### 7.1 Authentication Model

The browse server uses a token-based authentication system:

1. **Server token**: Generated randomly on each server startup and written to the state file. Required as `Bearer <token>` header on all `/command` requests.
2. **Multi-agent tokens**: The `TokenRegistry` (`browse/src/token-registry.ts`) manages scoped tokens for paired agents.

Token scopes:

| Scope | Permissions |
|-------|------------|
| `read` | Read-only commands (js, eval, css, attrs, text, refs, source, console, network) |
| `write` | Mutating commands (click, fill, select, goto, load-html, upload, scroll, hover, wait) |
| `admin` | Server management commands |
| `meta` | Utility commands (pdf, screenshot, tabs, state, frame) |
| `control` | Tunnel and extension management |

### 7.2 ML Security Classifier

The security classifier (`browse/src/security-classifier.ts`) uses an ensemble of five detection layers to identify prompt injection attacks in web content:

| Layer | Method |
|-------|--------|
| `testsavant_content` | ML content classification |
| `transcript_classifier` | Transcript-based classification |
| `aria_regex` | Regular expression matching on ARIA/accessibility text |
| `canary` | Canary token detection |
| `deberta_content` | DeBERTa-based content classification |

The classifier produces verdicts: SAFE, WARN, or BLOCK. Attack telemetry (domain, payload hash, confidence, layer, verdict) is stored in Supabase via migration 004.

> [!IMPORTANT]
> Issue #1156 notes that WARN verdicts currently take no defensive action beyond logging. Until this is addressed, WARN-level detections are informational only. Monitor audit logs for WARN verdicts in production deployments.

### 7.3 Content Security

The content security module (`browse/src/content-security.ts`) filters untrusted web content before it reaches AI agents:

- All web-sourced content is wrapped between `BEGIN UNTRUSTED CONTENT` and `END UNTRUSTED CONTENT` markers
- The `BROWSE_CONTENT_FILTER` environment variable controls filtering aggressiveness
- This prevents web content from being interpreted as instructions by downstream AI agents

### 7.4 URL Validation

`browse/src/url-validation.ts` implements:

- **Cloud metadata blocking**: Blocks access to `169.254.169.254` and similar cloud metadata endpoints (SSRF prevention)
- **`file://` validation**: Restricts file URL access to safe directories
- **Allowlist/blocklist**: Configurable URL filtering

### 7.5 Path Security

`browse/src/path-security.ts` enforces a safe directory allowlist for all file operations. Any path outside the allowed directories triggers the error:

```
Path must be within: <SAFE_DIRECTORIES>
```

### 7.6 Privacy Filtering

The activity stream (`browse/src/activity.ts`) automatically redacts:

- Passwords
- Authentication tokens
- Sensitive query parameters

This ensures the Chrome extension's side panel and activity stream do not leak credentials.

### 7.7 Cookie Security

`BrowserManager` tracks imported cookie domains to maintain an elevated security context for those origins. Cookie import (`browse/src/cookie-import-browser.ts`) supports extracting cookies from installed browsers.

### 7.8 Anti-Bot Stealth

In headed mode (`BROWSE_HEADED=1`), gstack patches Chromium to evade bot detection:

- `navigator.webdriver` returns `false`
- Plugin and language arrays are populated with realistic values
- CDP (Chrome DevTools Protocol) artifacts are hidden
- Permissions API behavior is normalized

### 7.9 Supabase Row-Level Security

The database migrations progressively tighten RLS policies:

- **Reads blocked** on all tables (telemetry data is write-only from clients)
- **Updates restricted** to tracking columns only (cannot modify event payload)
- **Inserts kept** for backward compatibility with older client versions

Verify RLS policies are correctly applied:

```bash
./supabase/verify-rls.sh
```

### 7.10 Audit Trail

The audit log (`browse/src/audit.ts`) writes append-only JSONL to `.gstack/browse-audit.jsonl`. This log is the forensic record for:

- All command executions
- Authentication events
- Security classifier verdicts
- Tunnel access attempts (denied attempts logged separately by `tunnel-denial-log.ts`)

> [!WARNING]
> The audit log grows without bound. Implement log rotation for long-running deployments. The log file path is determined by the state directory (`.gstack/` by default).

---

## 8. Developer Internals

### 8.1 Repository Structure

```
gstack/
├── browse/
│   ├── src/              # Browser automation engine
│   │   ├── server.ts     # HTTP server entry point
│   │   ├── cli.ts        # CLI client entry point
│   │   └── ...           # 25+ modules (see Module Reference)
│   └── test/             # Browse tests
├── design/
│   └── src/              # AI design tool (15 modules)
├── make-pdf/
│   └── src/              # PDF pipeline (8 modules)
├── skills/               # SKILL.md.tmpl templates
├── scripts/
│   ├── resolvers/        # Template resolvers (18 resolvers)
│   ├── host-adapters/    # Host-specific adapters
│   └── gen-skill-docs.ts # Multi-host doc generator
├── hosts/                # Per-host config (10 hosts)
├── lib/                  # Shared libraries (worktree.ts)
├── model-overlays/       # Model-specific behavior files
│   ├── claude.md
│   ├── gemini.md
│   ├── gpt.md
│   ├── gpt-5.4.md
│   ├── o-series.md
│   └── opus-4-7.md
├── extension/            # Chrome extension (Manifest V3)
├── supabase/
│   ├── functions/        # Edge functions (3)
│   ├── migrations/       # Schema migrations (001-004)
│   ├── config.sh         # Public Supabase config
│   └── verify-rls.sh     # RLS smoke test
├── gstack-upgrade/
│   └── migrations/       # Version migrations (4)
└── test/
    └── helpers/          # Test utilities
```

### 8.2 Key Classes

| Class | Location | Purpose |
|-------|----------|---------|
| `BrowserManager` | `browse/src/browser-manager.ts` | Singleton managing Chromium lifecycle, tab creation/destruction, viewport, browser context |
| `TabSession` | `browse/src/tab-session.ts` | Per-tab state container — element refs, frame navigation, loaded HTML tracking |
| `CircularBuffer` | `browse/src/buffers.ts` | Fixed-size ring buffer for console and dialog capture |
| `SizeCappedBuffer` | `browse/src/network-capture.ts` | Size-limited buffer for network request/response logging |
| `WorktreeManager` | `lib/worktree.ts` | Git worktree creation and cleanup for isolated test environments |
| `ProgressReporter` | `make-pdf/src/orchestrator.ts` | Progress tracking for multi-file PDF orchestration |
| `CookieImportError` | `browse/src/cookie-import-browser.ts` | Error type for cookie import failures |
| `BrowseClientError` | `make-pdf/src/types.ts` | Error type for browse CLI communication failures |
| `PdftotextUnavailableError` | `make-pdf/src/pdftotext.ts` | Error type when pdftotext binary is not found |
| `RateLimitExhaustedError` | `test/helpers/agent-sdk-runner.ts` | Error type for exhausted API rate limits in tests |
| `Semaphore` | `test/helpers/agent-sdk-runner.ts` | Concurrency limiter for parallel SDK test sessions |
| `EvalCollector` | `test/helpers/eval-store.ts` | Test evaluation result collector |

### 8.3 Skills System Architecture

Skills are the primary interface between gstack and AI coding hosts. The generation pipeline works as follows:

1. **Templates**: Each skill has a `SKILL.md.tmpl` file with resolver placeholders like `{{BROWSE_COMMAND_REFERENCE}}`, `{{INVOKE_SKILL}}`, `{{CONFIDENCE_CALIBRATION}}`.

2. **Resolvers**: TypeScript functions in `scripts/resolvers/` that generate dynamic content. The resolver registry (`scripts/resolvers/index.ts`) maps placeholder names to resolver functions.

3. **Host configs**: Each host in `hosts/` defines path rewrites and frontmatter requirements. For example, Codex requires specific frontmatter transformation (`scripts/resolvers/codex-helpers.ts`).

4. **Generation**: `scripts/gen-skill-docs.ts` iterates over all templates, runs resolvers, applies host-specific transformations, and writes final `.md` files.

5. **Model overlays**: Files in `model-overlays/` provide model-specific behavior adjustments (Claude, Gemini, GPT, GPT-5.4, O-series, Opus 4-7).

### 8.4 Command System

Browse commands are split into three categories by side-effect profile:

**Read commands** (`browse/src/read-commands.ts`) — no page mutation:

| Command | Description |
|---------|-------------|
| `js` / `eval` | Execute JavaScript on the page |
| `css` | Query computed CSS properties |
| `attrs` | Get element attributes |
| `console` | Read console log buffer |
| `network` | Read network capture buffer |
| `source` | Get page source HTML |
| `text` | Extract text content |
| `refs` | Get element reference map |

**Write commands** (`browse/src/write-commands.ts`) — mutate page state:

| Command | Description |
|---------|-------------|
| `goto` | Navigate to a URL |
| `load-html` | Load raw HTML into the page |
| `click` | Click an element |
| `fill` | Fill a form field |
| `select` | Select a dropdown option |
| `upload` | Upload a file |
| `scroll` | Scroll the page or element |
| `hover` | Hover over an element |
| `wait` | Wait for a selector or condition |

**Meta commands** (`browse/src/meta-commands.ts`) — server/session operations:

| Command | Description |
|---------|-------------|
| `pdf` | Generate PDF of current page |
| `screenshot` | Capture screenshot |
| `tabs` | List or switch tabs |
| `state` | Save or load browser state |
| `frame` | Switch between frames |
| `mhtml` | Save page as MHTML archive (requires Chromium CDP) |

### 8.5 Snapshot System

The snapshot module (`browse/src/snapshot.ts`) produces accessibility tree representations and annotated screenshots. These are the primary mechanism by which AI agents "see" web pages:

- **Accessibility tree**: Extracts the page's a11y tree into a structured text format that AI agents can reason about
- **Annotated screenshots**: Captures the page with element reference overlays for visual grounding

### 8.6 Testing Strategy

gstack uses Bun's built-in test runner with a tiered evaluation system:

| Tier | Scope | When |
|------|-------|------|
| Unit tests | Individual modules | Every change |
| Gate evals | Core functionality (`EVALS_TIER=gate`) | Every PR |
| Periodic evals | Extended coverage (`EVALS_TIER=periodic`) | Scheduled |
| Full evals | Everything (`EVALS_ALL=1`) | Manual |

Test infrastructure:

- **`test/helpers/agent-sdk-runner.ts`**: Parallel SDK session runner with `Semaphore` for concurrency control and `RateLimitExhaustedError` handling
- **`test/helpers/benchmark-judge.ts`**: LLM-based evaluation judge (uses Anthropic API)
- **`test/helpers/e2e-helpers.ts`**: End-to-end test helpers with eval mode gating
- **`test/helpers/eval-store.ts`**: `EvalCollector` for aggregating evaluation results
- **`test/helpers/session-runner.ts`**: Session management for eval runs (supports `EVALS_MODEL` override)

```bash
# Run unit tests
bun test

# Run gate evals
EVALS=1 EVALS_TIER=gate bun test

# Run all evals with a specific model
EVALS=1 EVALS_ALL=1 EVALS_MODEL=claude-3-5-sonnet bun test
```

### 8.7 Git Worktree Manager

`lib/worktree.ts` provides the `WorktreeManager` class for creating isolated Git worktrees. This is used by skills that need to run tests or builds in isolation without affecting the main working directory.

### 8.8 Code Patterns

**Error handling**: gstack uses `safeUnlink`, `safeKill`, and `isProcessAlive` utilities (`browse/src/error-handling.ts`) for defensive cleanup. Custom error classes (`CookieImportError`, `BrowseClientError`, `PdftotextUnavailableError`) provide typed error handling.

**State management**: The browse server treats the filesystem as persistent state. The state file, audit log, and session cookies are all file-based. `resolveConfig()` in `browse/src/config.ts` determines the state directory based on project detection.

**Buffer management**: Two buffer implementations prevent unbounded memory growth:
- `CircularBuffer` (fixed item count, ring buffer) for console/dialog logs
- `SizeCappedBuffer` (fixed byte size) for network captures

**Platform abstraction**: `browse/src/platform.ts` handles OS detection and temp directory resolution, enabling cross-platform behavior in cookie import, path validation, and process management.

### 8.9 Contributing

#### Adding a New Browse Command

1. Determine the command category (read, write, or meta).
2. Add the handler function to the appropriate module (`read-commands.ts`, `write-commands.ts`, or `meta-commands.ts`).
3. Register the command in `server.ts`'s command dispatch.
4. Update the browse resolver (`scripts/resolvers/browse.ts`) to include it in generated documentation.
5. Add tests.

#### Adding a New Skill

1. Create a `SKILL.md.tmpl` file in the `skills/` directory.
2. Use resolver placeholders (`{{RESOLVER_NAME}}`) for dynamic content.
3. If a new resolver is needed, add it to `scripts/resolvers/` and register it in `scripts/resolvers/index.ts`.
4. Run `bun run scripts/gen-skill-docs.ts` to generate host-specific versions.
5. Test with each target host.

#### Adding a New Host

1. Create a host config file in `hosts/` (e.g., `hosts/newhost.ts`).
2. Define path rewrites and frontmatter requirements.
3. Register the host in `hosts/index.ts`.
4. If the host needs special handling, create an adapter in `scripts/host-adapters/`.
5. Run skill generation and verify output.

#### Adding a New Resolver

1. Create a resolver module in `scripts/resolvers/`.
2. Export a function that returns the resolved content string.
3. Register the resolver name in `scripts/resolvers/index.ts`.
4. Use the placeholder in skill templates: `{{YOUR_RESOLVER_NAME}}`.

#### Adding a Supabase Migration

1. Create a new migration file in `supabase/migrations/` following the naming pattern `00N-description.sql`.
2. Apply the migration to your Supabase project.
3. Update `supabase/verify-rls.sh` if the migration adds new tables or policies.
4. Test with the telemetry-ingest and community-pulse edge functions.

---

## 9. Integration Points

### 9.1 Chrome Extension

The Chrome extension (`extension/`) connects to the browse server for real-time interaction:

| File | Role |
|------|------|
| `manifest.json` | Chrome Manifest V3 configuration |
| `background.js` | Service worker — health polling, command proxy, ref relay |
| `sidepanel.js` / `sidepanel.html` / `sidepanel.css` | Activity stream display, chat interface |
| `content.js` / `content.css` | Page-level indicator injection, inspector fallback |
| `inspector.js` / `inspector.css` | Full element inspection overlay |
| `popup.html` / `popup.js` | Toolbar popup interface |

The extension communicates with the browse server via:

- `/health` for connection status polling
- `/activity/stream` (SSE) for real-time event streaming
- `/command` for executing browse commands from the side panel
- `/refs` for element reference data

### 9.2 Supabase Backend

**Tables:**

| Table | Purpose |
|-------|---------|
| `telemetry_events` | Raw usage events (write-only from clients) |
| `installations` | Installation tracking |
| `update_checks` | Version check history |
| `community_pulse_cache` | Cached aggregation for community stats |

**Views:**

| View | Purpose |
|------|---------|
| `crash_clusters` | Aggregated crash data for pattern analysis |
| `skill_sequences` | Skill usage sequences for workflow detection |

**Edge functions:**

| Function | Method | Limits |
|----------|--------|--------|
| `telemetry-ingest` | POST | Max 100 events, max 50KB per request |
| `community-pulse` | GET | Cached for 1 hour |
| `update-check` | GET | Logs installation info |

> [!TIP]
> The `community-pulse` endpoint caches results for one hour. During development, you may need to wait for cache expiration or clear the `community_pulse_cache` table directly to see updated aggregations.

### 9.3 Model Overlays

Model overlay files in `model-overlays/` provide behavioral adjustments for different LLMs:

| Overlay | Target Model |
|---------|-------------|
| `claude.md` | Claude (default) |
| `gemini.md` | Google Gemini |
| `gpt.md` | GPT-4 / GPT-4o |
| `gpt-5.4.md` | GPT-5.4 |
| `o-series.md` | OpenAI O-series (reasoning models) |
| `opus-4-7.md` | Claude Opus 4-7 |

These overlays are injected into skill templates via the `model-overlay` resolver (`scripts/resolvers/model-overlay.ts`) to tune prompt behavior for each model's strengths and characteristics.

### 9.4 make-pdf Integration

The make-pdf tool depends on the browse server for rendering:

```
Markdown → marked parser → HTML + print-css → browse load-html → Chromium PDF
```

`make-pdf/src/browseClient.ts` provides a typed wrapper around shell-outs to the browse CLI. Set `BROWSE_BIN` to override binary discovery. The pipeline includes:

1. **Setup** (`setup.ts`): One-time dependency check
2. **Rendering** (`render.ts`): Markdown to HTML conversion with typography improvements
3. **Orchestration** (`orchestrator.ts`): Multi-file coordination with progress reporting
4. **Verification** (`pdftotext.ts`): Optional text extraction to verify PDF content

---

## 10. Troubleshooting

### Error: `Server failed to start within Ns`

**Cause:** The browse server did not become ready within the startup timeout, typically because Chromium failed to launch.

**Solution:**
1. Check that Chromium is installed: `bunx playwright install chromium`
2. In containers, ensure `CI=1` or `CONTAINER=1` is set for `--no-sandbox`
3. Verify sufficient shared memory: `df -h /dev/shm` (need 256MB+)
4. Check for port conflicts: `lsof -i :9222`

**Prevention:** Set `BROWSE_PORT` to avoid port scanning delays. Use `GSTACK_CHROMIUM_PATH` for a known-good binary.

---

### Error: `Cannot find server.ts. Set BROWSE_SERVER_SCRIPT env or run from the browse source tree.`

**Cause:** The CLI cannot locate the server entry point. This happens when running the browse CLI from outside the gstack directory.

**Solution:**
1. Run from within the gstack repository
2. Or set the `BROWSE_SERVER_SCRIPT` environment variable to the absolute path of `browse/src/server.ts`

**Prevention:** Install gstack globally or use a shell alias that includes the correct working directory.

---

### Error: `server-node.mjs not found. Run 'bun run build' to generate the Windows server bundle.`

**Cause:** On Windows, the browse server requires a pre-built bundle that has not been generated.

**Solution:**

```bash
bun run build
```

**Prevention:** Always run `bun run build` after cloning or updating the repository on Windows.

---

### Error: `Authentication failed` (HTTP 401)

**Cause:** The CLI is using a stale token that does not match the running server's token.

**Solution:**
1. Check the state file: `cat .gstack/browse-state.json`
2. Verify the server PID is still alive
3. If the server was restarted, the CLI will auto-discover the new token on the next command
4. Delete the state file and let the CLI start a fresh server

**Prevention:** Let the CLI manage server lifecycle automatically rather than starting the server manually.

---

### Error: `[browse] Server crashed twice in a row — aborting`

**Cause:** The server process crashed on two consecutive startup attempts.

**Solution:**
1. Check system logs for Chromium crash details
2. Verify system resources (RAM, disk space)
3. Try with `BROWSE_HEADED=1` to see if the browser window shows an error
4. Check for conflicting Chromium processes: `ps aux | grep chromium`

**Prevention:** Monitor system resources. In CI, allocate sufficient `--shm-size` for Docker.

---

### Error: `Browser not launched`

**Cause:** A command was sent before the browser context was initialized.

**Solution:**
1. Wait for the server to fully start (check `/health` endpoint)
2. If using `BROWSE_HEADLESS_SKIP=1`, the browser will not launch — unset this variable

**Prevention:** Use the CLI's auto-start mechanism rather than manual server management.

---

### Error: `Tab N not found`

**Cause:** The specified tab ID does not exist or has been closed.

**Solution:**
1. List available tabs: `browse tabs`
2. Use a valid tab ID or omit `BROWSE_TAB` to use the active tab

**Prevention:** Do not hardcode tab IDs across sessions. Query available tabs before switching.

---

### Error: `No active page. Use browse goto <url> first.`

**Cause:** The current tab has no page loaded (e.g., fresh server with no navigation).

**Solution:**

```bash
browse goto https://example.com
```

**Prevention:** Always navigate to a URL before running page commands.

---

### Error: `[browse] Port N (from BROWSE_PORT env) is in use`

**Cause:** The port specified in `BROWSE_PORT` is already occupied by another process.

**Solution:**
1. Find what is using the port: `lsof -i :N` (or `netstat -tlnp | grep N`)
2. Kill the process or choose a different port
3. Unset `BROWSE_PORT` to use automatic port discovery

**Prevention:** Use port ranges rather than fixed ports in multi-instance deployments.

---

### Error: `[browse] No available port after N attempts in range MIN-MAX`

**Cause:** All ports in the auto-discovery range are occupied.

**Solution:**
1. Check for zombie browse server processes: `ps aux | grep server.ts`
2. Clean up stale state files: `rm .gstack/browse-state.json`
3. Kill orphaned Chromium processes

**Prevention:** Ensure the watchdog (`BROWSE_PARENT_PID`) is active so servers auto-exit with their parent.

---

### Error: `MHTML archive requires Chromium CDP`

**Cause:** The `mhtml` command was invoked but CDP (Chrome DevTools Protocol) is not available.

**Solution:** MHTML archiving requires a Chromium-based browser. Ensure you are not using a non-Chromium browser override via `GSTACK_CHROMIUM_PATH`.

**Prevention:** Only use `mhtml` with the default Playwright Chromium or a compatible Chromium binary.

---

### Error: `Path must be within: <SAFE_DIRECTORIES>`

**Cause:** A file operation (upload, file serving) attempted to access a path outside the allowed directories.

**Solution:**
1. Move the file to a directory within the allowlist
2. Check `browse/src/path-security.ts` for the current safe directory list

**Prevention:** Keep working files within the project directory or configured safe directories.

---

### Error: URL validation failures (cloud metadata, blocked domains)

**Cause:** The URL validation module blocked a request to a restricted URL.

**Solution:**
1. For cloud metadata blocking (`169.254.169.254`): this is intentional SSRF prevention — do not bypass
2. For `file://` URLs: ensure the file path is within safe directories
3. Check URL against the allowlist/blocklist in `browse/src/url-validation.ts`

**Prevention:** Use `goto` with HTTPS URLs. For local files, use `load-html` with file content instead of `file://` URLs.

---

### Error: Exit code 128 in conductor template creation

**Cause:** Git operations failed due to unborn HEAD or missing refs (GitHub issues #1174, #1176).

**Solution:**
1. Ensure the repository has at least one commit
2. Check that the current branch has a valid HEAD reference
3. If using worktrees, verify the main repository is in a clean state

**Prevention:** Initialize repositories with an initial commit before running conductor skills.

---

### Error: `DESIGN_NOT_AVAILABLE` in Codex workflows

**Cause:** Design tool is not available in the Codex environment (GitHub issue #1159).

**Solution:** The design tool requires OpenAI API access. Verify `OPENAI_API_KEY` is set and accessible in the Codex environment.

**Prevention:** Check host compatibility before invoking design workflows. Not all hosts support all gstack features.

---

### Error: Custom `OPENAI_BASE_URL` not respected

**Cause:** The design tool does not honor custom OpenAI base URL configuration (GitHub issue #957).

**Solution:** Currently a known limitation. Use the standard OpenAI API endpoint or monitor issue #957 for a fix.

**Prevention:** Do not rely on custom base URLs for gstack's design features.

---

### Error: `./setup --help` runs install instead of showing help

**Cause:** The `--help` flag is not properly intercepted before the install flow (GitHub issue #1133).

**Solution:** Run `gstack --help` for general help instead.

**Prevention:** Refer to this guide for setup options.

---

### Error: `./setup --prefix` bug

**Cause:** The `--prefix` option in setup has a known issue (GitHub issue #620).

**Solution:** Use the default installation path or manually configure after setup.

**Prevention:** Monitor issue #620 for a fix.

---

### Error: Review skill not looking at correct diff

**Cause:** The review skill examines the wrong diff base (GitHub issue #1152).

**Solution:** Explicitly specify the base branch when invoking the review skill, or ensure your Git branch tracking is configured correctly.

**Prevention:** Verify `git diff` output matches expectations before running the review skill.

---

### Error: Codex generated skills carried Claude defaults

**Cause:** Host-specific skill generation was not properly transforming Claude defaults to Codex equivalents (GitHub issue #1163).

**Solution:** Regenerate skills with `bun run scripts/gen-skill-docs.ts` after updating to a version with this fix.

**Prevention:** After updating gstack, always regenerate skills for all hosts.

---

### Error: WARN verdict takes no defensive action

**Cause:** The security classifier's WARN verdict logs the detection but does not block the content (GitHub issue #1156).

**Solution:** Monitor audit logs for WARN verdicts manually. Consider treating high-confidence WARNs as BLOCKs in your own middleware.

**Prevention:** Follow issue #1156 for the planned enforcement of WARN verdicts.

> [!CAUTION]
> When debugging browse server issues, check the audit log (`.gstack/browse-audit.jsonl`) first. It contains timestamped records of every command execution, authentication event, and security verdict, making it the fastest path to root cause.

---

## 11. Supabase Schema Reference

### 11.1 Tables

**telemetry_events**: Stores raw telemetry events. Client-writable (INSERT only), reads blocked by RLS.

**installations**: Tracks unique gstack installations. Populated by the update-check edge function.

**update_checks**: Version check history — logs which versions are checking for updates.

**community_pulse_cache**: Cached aggregation table used by the community-pulse edge function (1-hour TTL).

### 11.2 Views

| View | Purpose |
|------|---------|
| `crash_clusters` | Aggregates crash data from telemetry events for pattern analysis |
| `skill_sequences` | Extracts skill usage sequences from telemetry to identify common workflows |

### 11.3 Migration History

| Migration | Description |
|-----------|-------------|
| 001 | Initial schema: `telemetry_events`, `installations` |
| 002 | Row-level security: block reads, restrict updates, keep inserts |
| 003 | Upsert support for telemetry deduplication |
| 004 | Attack telemetry columns: `domain`, `payload_hash`, `confidence`, `layer`, `verdict` |

> [!NOTE]
> RLS policies are strict: clients can INSERT telemetry but cannot READ any data back. Telemetry is effectively write-only from the client perspective. All aggregation happens server-side via edge functions.

---

## 12. Quick Reference

### Environment Variable Cheat Sheet

```bash
# Essential browse server config
export BROWSE_PORT=9222
export BROWSE_HEADED=1
export BROWSE_IDLE_TIMEOUT=3600
export BROWSE_PARENT_PID=0

# Security (development only)
export GSTACK_SECURITY_OFF=1

# Tunneling
export BROWSE_TUNNEL=1
export NGROK_AUTHTOKEN=your_token

# Design tool
export OPENAI_API_KEY=sk-your-key

# CI/containers
export CI=1
export CONTAINER=1

# Custom Chromium
export GSTACK_CHROMIUM_PATH=/usr/bin/chromium-browser
```

### Common Operations

```bash
# Start headed browse server
BROWSE_HEADED=1 bun run browse/src/server.ts

# Health check
curl http://localhost:9222/health

# Run all tests
bun test

# Run gate evals
EVALS=1 EVALS_TIER=gate bun test

# Generate skill docs for all hosts
bun run scripts/gen-skill-docs.ts

# Build Windows bundle
bun run build

# Verify Supabase RLS
./supabase/verify-rls.sh

# Tail audit log
tail -f .gstack/browse-audit.jsonl | jq .
```

### HTTP API Quick Reference

```bash
# Execute a command
curl -X POST http://localhost:9222/command \
  -H "Authorization: Bearer $(jq -r .token .gstack/browse-state.json)" \
  -H "Content-Type: application/json" \
  -d '{"command": "goto", "args": ["https://example.com"]}'

# Pair a new agent with read-only access
curl -X POST http://localhost:9222/pair \
  -H "Authorization: Bearer $ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"scopes": ["read"]}'

# Get element references
curl http://localhost:9222/refs \
  -H "Authorization: Bearer $TOKEN"

# Stream activity (SSE)
curl -N http://localhost:9222/activity/stream \
  -H "Authorization: Bearer $TOKEN"
```

See the [User Guide](garrytan__gstack__UserGuide.md) for hands-on usage tutorials, CLI command reference, and workflow examples.

---

_Generated May 2026 from garrytan/gstack at e3d7f49. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
