Access local networks with the SDK
Use a local proxy tunnel from Node.js or Python to reach company, VPN, and development sites from Cloud Browser.
Run the SDK on a computer that can reach your target site. A normal cloud browser can then send its web traffic through that SDK process. The SDK machine resolves destination names with its operating system DNS, including company/VPN DNS, and opens the TCP connections.
This is a Cloud Browser session feature. It does not add local-network access to the separate Web Fetch API.
Requirements
- Local proxy was introduced in Node.js and Python SDK 0.6.0; use 0.6.1 or later. Set
LEXMOUNT_API_KEYandLEXMOUNT_PROJECT_IDas described in the Node.js or Python quickstart. - The project must have
custom_proxycapability, and the selected region must have local proxy enabled. - Use
browserMode: 'normal'/browser_mode="normal". Light/Moli sessions do not support this mode. - Keep the tunnel and browser in the same project, API key, and region. Reuse the same client; optionally set
LEXMOUNT_REGIONto a region ID fromcatalogInfo()/catalog_info(). - The SDK machine needs outbound access to the service's HTTPS and WSS endpoints and to the target site. No inbound listening port, public IP, or cloud-side hosts entry is needed. Keep the SDK process and VPN running throughout the session.
Node.js Example
Install dependencies and save the TypeScript example as local-proxy.ts:
npm install lexmount@^0.6.1 playwright
npm install --save-dev tsx
export TARGET_URL='https://your-internal-site.example/'
npx tsx local-proxy.tsUse an ESM project ("type": "module" in package.json) for the top-level await example. Replace the target with an HTTP(S) URL reachable from the SDK machine and export your credentials first.
import { Lexmount } from 'lexmount';
import { chromium } from 'playwright';
const target = process.env.TARGET_URL;
if (!target) throw new Error('Set TARGET_URL to a reachable HTTP(S) URL');
const client = new Lexmount({ region: process.env.LEXMOUNT_REGION });
try {
const tunnel = await client.tunnels.open();
try {
const session = await client.sessions.create({
browserMode: 'normal',
proxy: { type: 'local', tunnelId: tunnel.id },
});
try {
const browser = await chromium.connectOverCDP(session.connectUrl);
try {
const context = browser.contexts()[0];
if (!context) throw new Error('No browser context');
const page = context.pages()[0] ?? await context.newPage();
await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60_000 });
console.log(await page.title());
} finally {
await browser.close();
}
} finally {
await session.close();
}
} finally {
await tunnel.close();
}
} finally {
client.close();
}Python Example
python3 -m pip install --upgrade 'lexmount>=0.6.1' playwright
export TARGET_URL='https://your-internal-site.example/'
python3 local_proxy.pySave this as local_proxy.py and export your credentials first:
import os
from lexmount import Lexmount
from playwright.sync_api import sync_playwright
target = os.environ["TARGET_URL"]
with Lexmount(region=os.getenv("LEXMOUNT_REGION") or None) as client:
with client.tunnels.open() as tunnel:
with client.sessions.create(
browser_mode="normal",
proxy={"type": "local", "tunnel_id": tunnel.id},
) as session:
with sync_playwright() as playwright:
browser = playwright.chromium.connect_over_cdp(session.connect_url)
try:
if not browser.contexts:
raise RuntimeError("No browser context")
context = browser.contexts[0]
page = context.pages[0] if context.pages else context.new_page()
page.goto(target, wait_until="domcontentloaded", timeout=60_000)
print(page.title())
finally:
browser.close()The browser runs in the cloud; these examples connect to it over CDP. A local Chromium installation is not needed for this connection.
Tunnel Options and Lifecycle
| Node.js | Python | Behavior |
|---|---|---|
client.tunnels.open() | client.tunnels.open() | Return after the connector is ready |
tunnel.id | tunnel.id | Pass into the session proxy configuration |
tunnel.regionId | tunnel.region_id | Region selected for the tunnel |
tunnel.ready | tunnel.ready | Whether the connector is currently ready |
reconnect | reconnect | Open option; defaults to enabled |
connectTimeoutMs | connect_timeout | Open option; local DNS/TCP timeout, default 15,000 ms / 15 seconds |
await tunnel.close() | tunnel.close() | Close connections and revoke the tunnel |
For example, client.tunnels.open({ connectTimeoutMs: 20_000 }) or client.tunnels.open(connect_timeout=20.0). These are connection deadlines, not page-navigation timeouts. Python options use seconds; Node.js uses milliseconds.
Close the browser connection, then the cloud session, then the tunnel, and finally the SDK client. The examples preserve this order when navigation fails. Closing a tunnel alone does not close the browser session or stop its billing.
Reconnect is enabled by default. A disconnect fails existing TCP streams; after reconnection only new connections use the restored tunnel. The connector does not replay requests or form submissions, and does not fall back to cloud DNS or another proxy. Verify the outcome of a write before retrying it. A tunnel expires after seven days and supports up to 128 concurrent TCP streams; that is a tunnel connection limit, not a browser-session quota.
Scope and Billing
- Supported browser traffic includes HTTP, HTTPS, WS, and WSS over TCP. This is not a general UDP/VPN interface, and browser restrictions still apply.
- Select one of local proxy, external proxy, or
officialProxy/official_proxy; they are mutually exclusive. - The tunnel provides network reachability, not a copy of local cookies, logged-in browser state, or certificate trust. Login and private CA handling remain part of the cloud browser workflow.
- Local egress is accounted for separately from official-proxy traffic. Normal browser time, concurrency, and project capability limits still apply.
Troubleshooting
| Symptom | Check |
|---|---|
tunnels is missing | Installed SDK version; upgrade to 0.6.1 or later |
403 when opening a tunnel | Project capability and the matching Key/Project ID |
503 / local proxy unavailable | Whether local proxy is enabled in the selected region |
| Tunnel and browser region mismatch | Use the same client/region and a newly opened tunnel |
| Tunnel never becomes ready | Outbound WSS access, VPN/firewall restrictions, and current credentials |
Navigation timeout / ERR_TUNNEL_CONNECTION_FAILED | Keep the connector alive; verify the target's DNS and port from the SDK machine, then check tunnel readiness |
| Certificate or login error | Cloud browser trust and authentication; local network access does not import these |
A ready tunnel or created session proves only that stage succeeded; validate access to the actual target. When a tunnel expires, create a new tunnel and new sessions using its ID.
Maintained Demos
After the Node.js examples setup, run:
npm run local-proxy-demo -- --url 'https://your-internal-site.example/'After the Python examples setup, run:
python3 local_proxy_demo.py --url 'https://your-internal-site.example/'Both demos accept --region or read LEXMOUNT_REGION from .env. Pass the target through --url; these demo scripts do not read TARGET_URL. They save local_proxy_demo.png and wait for a keypress before cleanup.
