Skip to content
Ahmed Hamza

Automation

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

What changed when I stopped treating an MCP-to-WSL bridge as a connectivity problem and separated files, Bash, code intelligence, terminal lifetime, browser control, and constrained-client access into explicit authority boundaries.


Date

Updated

August 24, 2026

Read

4 min

Repository snapshot

WebSession MCP Bridge

Authenticated AI-to-WSL access with explicit trust boundaries.

Commits
192
Trust domains
3
Access
OAuth + MCP

Work done

Dev / Code / Terminal separationDurable PTYsPublic + private trust profiles

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

WebSession MCP Bridge authority map AI clients cross a Cloudflare HTTPS boundary into 1MCP, which routes to separate Dev, Code, Terminal, and Local Browser capabilities; an optional WebSession adapter authenticates back through the same gateway. WebSession MCP Bridge / authority map CONNECTIVITY IS NOT THE SAME THING AS AUTHORITY PERSONAL CAPABILITIES MCP OPTIONAL AI clients MCP · HTTPS PUBLIC EDGE Cloudflare HTTPS routing 1MCP OAuth · policy WebSession adapter durable HTTPS → 1MCP Dev files · Bash · waits · sleep Code repository-rooted intelligence Terminal durable PTYs · human handoff Local → Browser Windows · WSLg Chrome 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 authority splits after the request reaches the development machine.

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

The private harness now has three direct domains plus one stable Local broker domain:

Dev       files, Bash, durable waits, explicit host sleep
Code      repository-rooted code intelligence
Terminal  persistent interactive PTYs
Local     stable discovery/schema/call -> Browser facade

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 native Windows Chrome 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 stable broker actions for discovery, schema lookup, and calls; a private inner 1MCP and resource-local Browser facade own the Windows-versus-WSLg routing behind them.

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

The public side of the bridge does not need 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 private personal composition can expose broader WSL paths, code intelligence, durable terminals, and explicitly authorized browser control.

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.

Constrained clients needed another transport, not another authority model

Native MCP remains the normal path through 1MCP. The repository also has an optional WebSession adapter for clients that can make HTTPS requests but cannot speak MCP directly.

The adapter runs separately from the normal bridge lifecycle. Its readable-GET and bearer-authenticated JSON POST facades normalize into one durable SQLite operation core, then authenticate back through 1MCP. It mirrors the live tool descriptors and calls the exact upstream tools; it does not bypass the gateway or invent a parallel permission system.

That boundary matters more than the transport format. Adding compatibility should not duplicate authorization policy.

Public and private should not be the same artifact by accident

My personal harness includes implementation and evidence that only makes sense for my machine. The repository therefore keeps a publication boundary around private-only paths instead of assuming an open-source checkout should reproduce my authority model exactly.

That is a small lesson I keep rediscovering in agent tooling: a working internal composition is not automatically a safe default for somebody else.

The useful metric is clarity of authority

I could describe WebSession MCP Bridge 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.