tunnel/

directory
v0.0.0-...-e54188d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: AGPL-3.0

README

Gram Secure Tunnel

Reach an MCP server that has no inbound connectivity. The customer's agent runs next to the MCP server, opens one outbound WebSocket to Gram's tunnel gateway, and the gateway forwards MCP traffic back over yamux substreams.

Status

The backend control plane exists:

  • tunneled_mcp_servers is the durable Postgres source record.
  • /rpc/tunneledMcp.* creates, lists, updates, and deletes tunneled MCP sources, with RBAC and audit logging.
  • mcp_servers.tunneled_mcp_server_id links a hosted MCP server to a tunneled MCP source.
  • The MCP serve path resolves live tunnel routes from Redis, injects the tunnel ID server-side, and reuses the remote MCP proxy stack for auth, usage, tool logs, and stream metadata.

The gateway resolves presented tunnel keys against the key hashes stored in Postgres. Redis is the live routing table and connection snapshot store.

Pieces

Piece Code Responsibility
Agent tunnel/agent, tunnel/cmd/tunnel-agent Customer-side process. Dials the gateway and proxies substream HTTP to one pinned local MCP URL.
Gateway tunnel/gateway, tunnel/cmd/tunnel-gateway Accepts agent WebSockets on the public listener, owns yamux sessions, and forwards requests by tunnel ID on the internal listener.
MCP serve server/internal/mcp/serveendpoint.go Resolves the tunnel route, injects X-Gram-Tunnel-Id, and runs the remote MCP proxy path.
Management server/internal/tunneledmcp Goa service backed by Postgres plus Redis connection metadata.
Shared wire tunnel/wire, tunnel/route Key format, control frames, WS net.Conn, Redis route and connection stores.

Request Path

MCP client
  -> gram-server /mcp/<slug>
  -> mcp_servers row resolves tunneled_mcp_server_id
  -> Redis candidates: tunnel_routes:<tunnelID> -> live gateway forward addresses
  -> gram-server selects one gateway by client affinity or random fallback
  -> gram-server proxies to gateway with X-Gram-Tunnel-Id
  -> gateway opens yamux substream to a live agent
  -> agent proxies to TUNNEL_LOCAL_MCP_URL
  -> customer MCP server

The caller never supplies the tunnel ID. Gram derives it from the project-scoped MCP server row and overwrites any inbound tunnel header before forwarding.

State

Postgres is durable control-plane state:

  • tunneled_mcp_servers: display name, key hash/prefix, lifecycle status, persisted agent version, last-seen timestamp, soft delete.
  • mcp_servers.tunneled_mcp_server_id: hosted MCP server binding.

Redis is live data-plane state:

  • tunnel_routes:<tunnelID>: sorted set of live gateway addresses, refreshed while an agent is connected.
  • tunnel_connections:<tunnelID>: owner-scoped live connection snapshots for UI/API overview data, merged on read.

Local Validation

Start the local dev stack with ./zero --agent. The local Postgres MCP server and agent are declared in compose.yml under the tunnel profile:

docker compose --profile tunnel up --build tunnel-postgres-mcp tunnel-agent

Two pitchfork daemon entries wrap the same local path:

  • tunnel-gateway: local gateway with agent /connect on :8090 and internal forwarding on :8091, using Redis for routes.
  • tunnel-postgres-mcp: starts Postgres MCP and the companion tunnel agent.

The tunnel seed task writes the local tunnel ID and key to mise.local.toml:

TUNNEL_LOCAL_ID=<tunneled_mcp_servers.id>
TUNNEL_LOCAL_KEY=<one-time tunnel key>
TUNNEL_LOCAL_MCP_ENDPOINT_SLUG=<mcp endpoint slug>
TUNNEL_LOCAL_MCP_SERVER_ID=<mcp server id>

The Compose service runs the agent in the Postgres MCP network namespace and pins the upstream to http://127.0.0.1:9000/mcp; Gram reaches it through the normal tunneled MCP endpoint seeded in the dashboard.

Tests

go test ./tunnel/... ./server/internal/tunneledmcp/...

server/internal/mcp covers the production MCP serve path that routes tunneled MCP servers through the remote MCP proxy stack.

Directories

Path Synopsis
Package agent reverse-proxies a pinned local MCP server over one outbound yamux/WebSocket tunnel.
Package agent reverse-proxies a pinned local MCP server over one outbound yamux/WebSocket tunnel.
cmd
tunnel-agent command
Command tunnel-agent runs the customer-side outbound tunnel agent.
Command tunnel-agent runs the customer-side outbound tunnel agent.
tunnel-gateway command
Command tunnel-gateway serves public agent connects and internal tunnel forwards.
Command tunnel-gateway serves public agent connects and internal tunnel forwards.
Package route stores live tunnel gateway owners and connection snapshots.
Package route stores live tunnel gateway owners and connection snapshots.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL