Automation

The hard part of giving ChatGPT a shell is the trust boundary

How I separated file, terminal, and browser permissions when connecting AI clients to a local workstation with WebHarness.


Repository snapshot

WebHarness

Experimental public WSL MCP Personal Workstation reference.

WebHarness case study
Domains
4
Profiles
3
Browser
2 surfaces

Work done

  • Dev / Code / Terminal / Local
  • browser-fast + browser-devtools
  • OAuth + durable human handoff

Getting a model to run bash was not the milestone. The milestone was being able to answer one boring question: what can this session actually do?

A web AI session, an OAuth callback, a Linux user, a repository search tool, and a persistent terminal all have different authority. Treating them as one generic “connected machine” made the system easier to demo and harder to trust.

In 30 seconds

WebHarness authority map AI clients cross a Cloudflare HTTPS boundary into 1MCP, which routes to separate Dev, Code, Terminal, and Local capability domains; Local brokers routine browser interaction and full browser diagnostics. WebHarness / authority map CONNECTIVITY IS NOT THE SAME THING AS AUTHORITY CAPABILITY DOMAINS MCP AI clients MCP · HTTPS PUBLIC EDGE Cloudflare HTTPS routing 1MCP OAuth · policy Dev files · Bash · waits · sleep Code repository-rooted intelligence Terminal durable PTYs · human handoff Local → Browser browser-fast · browser-devtools TRUST PROFILES restricted → workspace-bounded · trusted-dev → native Bash · personal → Dev + Code + Terminal + Local
The important design choice is not the tunnel. It is where WebHarness splits authority after a request reaches the workstation.

Connectivity was the easy problem

The first useful prototype only needed a path:

ChatGPT -> HTTPS -> MCP -> WSL

That proves connectivity. It says almost nothing about authority.

Can the session read one workspace or the whole Linux account? Can it run arbitrary Bash? Can it control authenticated browser state? Can it start a process that outlives the current MCP request? Can it hand an interactive terminal to a human without opening a second shell? Can a constrained client reach the same tools without creating a second permission model?

Those questions became the architecture.

I stopped treating every tool as equivalent

WebHarness now has three direct domains plus one stable Local broker domain:

Dev       files, guarded edits, Bash, waits, host actions
Code      repository-rooted search, context, symbols
Terminal  persistent interactive PTYs + human handoff
Local     discovery / schema / call
            -> browser-fast
            -> browser-devtools

That split sounds simple, but it removes a lot of ambiguity.

A file edit and a terminal session do not have the same lifecycle. A semantic code search and unrestricted Bash do not have the same trust boundary. Browser control can reach authenticated browser state, so it should not be hidden inside a generic shell tool. A readiness wait should not consume terminal output just because both involve a running process.

Local keeps that browser surface small. The model sees three broker actions for discovery, schema lookup, and calls. Behind them, browser-fast owns compact routine interaction and browser-devtools owns deep Chrome diagnostics. Windows uses a dedicated MCP Chrome profile; Linux/WSLg can use managed Chrome or Clearcote. The caller can choose the resource target without changing the outer authority model.

Once those differences were explicit, routing became easier to reason about. The question for an agent changed from “which of these many tools could do this?” to “which boundary owns this kind of work?”

Trust profiles made the dangerous part visible

A public workstation reference should not silently grant the same authority as my personal WSL setup.

The restricted profile keeps ordinary file operations workspace-bounded. trusted-dev deliberately adds native Bash for a dedicated development host. The maintained personal composition can expose broader WSL paths, code intelligence, durable terminals, Local/Browser, and optional Windows host sleep.

The names matter less than the contract: more convenience should require a more explicit grant of authority.

That also changed setup. Preparing the personal runtime and enabling persistent startup are separate actions. A bootstrap can qualify and render the environment without silently deciding that services should come back forever.

Long-running work needed its own lifetime

MCP calls are request-shaped. Development work often is not.

A server, build, REPL, or interactive TUI may need to survive a provider restart or a later human handoff. Keeping those processes inside the lifetime of one connector call made them fragile.

The bridge instead gives Terminal its own durable PTY lifetime under tmux. The model can start work, observe the same process later, and yield the exact terminal when a human needs to type a password or make an interactive decision.

That removes a bad escape hatch: opening some second, hidden shell because the original one became inconvenient.

A large browser catalog did not belong in every session

Browser automation was the clearest example of why the Local broker exists. Routine clicking and form work need a compact interaction contract; debugging a browser failure may need network logs, console output, performance traces, Lighthouse, heap inspection, or screenshots.

Publishing both surfaces directly would make every client carry a large, changing catalog. WebHarness instead keeps three Local metatools stable and discovers the selected downstream schema only when it is needed.

That is also a trust boundary: everything admitted behind one Local broker legitimately shares that authorization domain. A capability that needs different authority should get a different broker or scope rather than hiding behind another tool name.

A public reference needs a qualified environment

WebHarness is intentionally specific about what is reproduced: the maintained reference targets WSL2 + Ubuntu + x86_64 + Node.js 24+ + systemd user services, with WSLg for headed Linux browser capability.

That specificity is useful. It separates “this architecture works and is qualified here” from “this project supports every Linux, macOS, and Windows environment.” The public repository can then document the actual constraints instead of turning my personal machine into an implicit universal default.

The useful metric is clarity of authority

I could describe WebHarness as Cloudflare + OAuth + MCP + WSL. That stack list is accurate and not very informative.

The project became more useful when I could describe the failure modes instead:

The tunnel connects the system. Those boundaries make it understandable.