Skip to content

Windows: gstack browse creates .gstack/ with broken DACL (only machine SID), client can't read its own state files; sidebar PTY/terminal-agent sidecar fails to start (chatEnabled:false, terminalPort:null) #1605

@jaiswal-naman

Description

@jaiswal-naman

Summary

On Windows, gstack browse connect has two distinct bugs that together make the sidebar GUI unusable:

  1. Broken DACL on .gstack/ state directory — the directory and its child files are created with an ACL containing only the machine SID (S-1-5-21-<machine>, renders as MACHINE\ with empty username and FullControl). Even the directory's owner gets no enumerable access. The next browse invocation can't read its own browse.json, lock files, or browse-startup-error.log. $ browse status reports "server failed to start" while a fully working server is actually listening.
  2. PTY/terminal-agent sidecar fails to spawn — once the server is reachable, /health returns {"status":"unhealthy", "chatEnabled":false, "terminalPort":null}. The Side Panel extension then shows "Browse server not ready. Reload sidebar to retry." The chat tab never works.

Environment

  • OS: Windows 11 Home Single Language 10.0.26200, x64
  • Shell: PowerShell 5.1 + Git Bash
  • gstack version: 1.40.0.0 (from ~/.claude/skills/gstack/package.json)
  • Binary: ~/.claude/skills/gstack/browse/dist/browse.exe (bun-compiled)
  • Server: ~/.claude/skills/gstack/browse/dist/server-node.mjs running under Node 22 from C:\Program Files\nodejs\node.exe
  • Chromium: ms-playwright chromium-1208 (Chrome for Testing 145.0.7632.6)
  • Invoked from Claude Code v? (Anthropic CLI)

Bug 1: Broken DACL on .gstack/

Repro

  1. Delete any existing .gstack/ and ~/.gstack/chromium-profile/
  2. Run browse.exe connect from a project directory whose parent has normal ACLs (e.g. user + Administrators + SYSTEM inherited)
  3. After connect (whether it succeeds or times out at 15s), inspect the newly-created .gstack/ directory:
> icacls .gstack
.gstack MACHINE\:(OI)(CI)(F)
Successfully processed 1 files; Failed processing 0 files

Note the malformed entry — username is empty, SID is the machine SID with no RID. There is no ACE for the user that ran the command, even though that user is the directory's Owner. The same broken ACL is applied to every file written into .gstack/ (e.g. browse.json, browse-startup-error.log).

> Get-ChildItem .gstack
Get-ChildItem : Access to the path '...\.gstack' is denied.

Same happens for ~/.gstack/chromium-profile/ — the EPERM caught in browse-startup-error.log is:

EPERM: operation not permitted, mkdir 'C:\Users\<user>\.gstack\chromium-profile'
    at Object.mkdirSync (node:fs:1377:26)
    at BrowserManager.launchHeaded (.../server-node.mjs:8645:9)
    at start (.../server-node.mjs:14898:28)

Workaround (confirms the diagnosis)

As Owner, rewriting the DACL using .NET DirectoryInfo.SetAccessControl with AccessControlSections.Access only (avoids needing SeSecurityPrivilege which Set-Acl demands) fixes enumeration immediately:

$di = New-Object System.IO.DirectoryInfo('.gstack')
$sec = New-Object System.Security.AccessControl.DirectorySecurity
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
  [System.Security.Principal.WindowsIdentity]::GetCurrent().User,
  'FullControl',
  'ContainerInherit,ObjectInherit', 'None', 'Allow')
$sec.SetAccessRuleProtection(\$false, \$true)  # inherit from parent
$sec.AddAccessRule($rule)
$di.SetAccessControl($sec)

This needs to be done on .gstack/ AND ~/.gstack/ AND every file written inside them (the broken ACL is re-applied per-file, not just on the directory).

Likely cause

Whatever code creates these directories/files is passing an explicit security descriptor that contains only the machine SID — looks like mkdir/fs.writeFile is being called with a custom mode/security parameter that maps to a Windows ACL with no user grants. Could be:

  • A bun stdlib bug in fs.mkdirSync ACL translation on Windows
  • An explicit mode: 0o700-style call inside server-node.mjs that bun's runtime translates to a broken DACL
  • A chmod call after creation that strips inherited ACLs without re-adding the user

grep -n "mkdir\|chmod\|0o7" browse/src/**/*.ts in the gstack source would likely identify the call site.

Bug 2: PTY/terminal-agent sidecar never starts

Repro

  1. After fixing Bug 1's ACL (manually or otherwise), get the server reachable on its declared port
  2. curl http://127.0.0.1:34567/health returns:
{
  "status": "unhealthy",
  "mode": "headed",
  "uptime": 863,
  "tabs": 2,
  "token": "...",
  "chatEnabled": false,
  "security": {...},
  "terminalPort": null
}
  1. Browser control via browse goto, browse click, browse snapshot all work
  2. Side Panel extension shows: "Browse server not ready. Reload sidebar to retry. Real PTY. Real terminal. Real claude."
  3. Reloading the sidebar doesn't help — terminalPort stays null

Likely cause

The "terminal agent" mentioned in the connect banner (Launching headed Chromium with extension + terminal agent...) is presumably a node-pty / ConPTY-based child process. On Windows, node-pty requires either:

  • A native prebuild matching the Node ABI of the embedded Node (the server runs under C:\Program Files\nodejs\node.exe, which is the user's system Node, not bundled)
  • Or ConPTY direct invocation via the Windows API

If server-node.mjs is bundled with a Linux/macOS-compatible PTY implementation but no Windows fallback, terminalPort will never bind and chatEnabled stays false. No log entry surfaces because the failure is swallowed.

Expected behavior

  1. gstack browse connect on Windows creates .gstack/ directories that are at minimum readable/writable by the invoking user
  2. The terminal-agent sidecar starts on Windows (or gracefully degrades with a visible log message), enabling the Side Panel chat

Workaround for users hitting this

For Bug 1 only (browser control without sidebar chat): after every browse connect, run a PowerShell snippet that rewrites the DACL on .gstack/browse.json so the client can read it. Browser commands (browse goto, click, snapshot) then work for AI-driven control even though the sidebar chat doesn't.

For Bug 2: no workaround found. The PTY sidecar appears to be a hard requirement for the sidebar chat tab.

Related

This effectively makes the gstack sidebar GUI Windows-incompatible in v1.40.0.0. Mentioning because /open-gstack-browser SKILL.md walks users through pinning the extension and opening the Side Panel, which then fails with the confusing "Browse server not ready" message.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions