Plugin / Docs / LAN / Localhost

LAN / Localhost — enable intranet & dev hosts safely

Remote Tab Opener is deny-by-default for local networks. Here is how to allow intranet and localhost in v7.10.0.

Rule of thumb: you need two things — popup flags and allow-list.

v7.10.0

2) Why it’s off by default

  • Predictability & safety — local services are diverse; an opt-in avoids accidental control of sensitive tools.
  • Consent — you choose which hosts are controllable (allow-list) and explicitly toggle LAN/localhost usage.
  • HTTPS discipline — keep the same security posture as for public domains.
Summary: LAN/localhost require a double barrier:
  1. Turn on the flags in the popup: Allow intranet (LAN) and/or Allow localhost (dev).
  2. Add the host(s) to the allow-list (wildcards allowed, HTTPS only).

3) Enable LAN / Localhost (2 steps)

Step 1 — Toggle the flags in the popup
  1. Open the extension popup.
  2. Tick Allow intranet (LAN) for hosts like *.corp.local or 10.x.x.x.
  3. Tick Allow localhost (dev) for localhost / 127.0.0.1.

Flags enable the category of hosts; they don’t whitelist specific domains by themselves.

Step 2 — Add the exact host(s) to the allow-list
  1. In the popup, add your host(s) to the Allow-list.
  2. Use https:// origins (recommended). Wildcards like *.corp.local are accepted.

Both steps are required. If one is missing, requests will be blocked.

4) Allow-list examples

GoalExample entryNotes
Single dev app on localhost https://localhost:5173 Port matters. Match your dev server port.
Any subdomain in a corp zone https://*.corp.local Wildcard host allowed; scheme is still https://.
Numeric intranet IP https://10.0.1.23 Consider using a cert (self-signed or internal CA) in dev.
QA farm https://qa-*.intra.example Prefix wildcards are useful for ephemeral stacks.
Remember: schemes like about:, chrome:, file:, moz-extension: are restricted by the browser — content scripts don’t inject there.

5) Quick Start (copy & paste)

5.1 Helper + detect (works on LAN/localhost too)

Canonical envelope with requestId. Keep a single instance on your page.

<script>
const ADMIN_ORIGIN='admin', EXT_ORIGIN='extension';
let _seq=0, _waiters=new Map();
addEventListener('message', (e) => {
  const d=e.data; if(!d||typeof d!=='object') return;
  if(d.origin!==EXT_ORIGIN) return;
  if(d.requestId && _waiters.has(d.requestId)) { _waiters.get(d.requestId)(d); _waiters.delete(d.requestId); }
});
function ask(action, args={}, timeoutMs=12000){
  const requestId='r'+(++_seq);
  postMessage({ origin:ADMIN_ORIGIN, type:'RTO_REQUEST', requestId, action, args }, '*');
  return new Promise((resolve,reject)=>{
    const t=setTimeout(()=>{ _waiters.delete(requestId); reject(Object.assign(new Error('timeout'),{code:'TIMEOUT'})); }, timeoutMs);
    _waiters.set(requestId, (resp)=>{ clearTimeout(t); resolve(resp); });
  });
}
(async () => {
  const det = await ask('detect');
  console.log('RTO present?', det.ok);
})();
</script>
5.2 Open an intranet (LAN) tab and act

Prereqs: Allow intranet (LAN) checked + host added to the Allow-list.

<script type="module">
const det = await ask('detect'); if (!det.ok) throw new Error('RTO not detected');

await ask('openTab', { url:'https://intranet.corp.local/dashboard', tabKey:'intra', focus:true });
await ask('domType', { selector:'#q', value:'status', clear:true, tabKey:'intra' });
await ask('domClick', { selector:'form button[type=submit]', tabKey:'intra' });

const page = await ask('getUrl', { tabKey:'intra' });
console.log('Intranet at:', page.ok ? page.data.url : '(unknown)');
</script>
5.3 Localhost (dev server)

Prereqs: Allow localhost (dev) checked + https://localhost:<port> in the Allow-list.

<script type="module">
await ask('openTab', { url:'https://localhost:5173', tabKey:'dev', focus:true });

// Simple highlighter using runJs (to show we're in control):
await ask('runJs', { tabKey:'dev', code:`(function(){
  const el=document.querySelector('body'); if(!el) return false;
  el.scrollIntoView({behavior:'smooth',block:'center'});
  el.style.transition='box-shadow .2s';
  el.style.boxShadow='0 0 0 4px rgba(13,110,253,.35)';
  setTimeout(()=>el.style.boxShadow='', 800);
  return true;
})()` });

await ask('navigate',  { url:'https://localhost:5173/docs', tabKey:'dev' });
</script>

6) Troubleshooting

Symptom / ErrorLikely causeFix (beginner checklist)
DOMAIN_NOT_ALLOWED Host not on Allow-list or LAN/localhost flag off
  1. Tick Allow intranet or Allow localhost.
  2. Add the exact https://host[:port] (or wildcard) to Allow-list.
NO_CONTROLLED_TAB No tab opened/adopted yet Run openTab first, or adoptTab an existing tab (see examples).
Security banner not visible Content script not injected Check scheme (https:// required), host permission, and CSP/iframe constraints.
Dev server is HTTP only No HTTPS on localhost Use HTTPS/dev certs or a tunnel; many dev stacks offer an HTTPS option.
Selectors fail on SPA Elements render late Use waits or retry; prefer stable id / data-* selectors.
Tip: If you change the Allow-list while testing, just retry your last action — RTO resumes once the host is authorized.

7) FAQ

Is LAN/localhost safe? Yes. They’re disabled by default and require both flags and allow-list entries.

Do I need a server or proxy? No. Everything runs locally in the browser.

Can I target IP ranges? You can allow specific IPs (e.g., https://10.0.1.23). Hostname wildcards work on names, not raw IP ranges.

Does this bypass SOP? No. Actions execute inside the page via content scripts on authorized hosts.

Next Pages

Page Description
Start Overview, how it works, setup, quick start, and links to all sections.
Detect the Extension Detection contract (ping/pong), install prompts, and graceful fallbacks when the extension isn’t present.
Allow-list & Permissions Deny-by-default model, domain allow-list via popup, and suggested UX to guide users to enable hosts.
LAN / LocalhostHow to safely enable intranet/localhost (flags + allow-list).
Remote Tab Control Open/navigate/focus the controlled tab, read URL/title, select an existing tab, lifecycle & restore patterns.
DOM & Automation Action catalog (type, setValue, click, select, submit, waitFor) and safe interaction tips.
Recipes & Flows Copy-paste flows: resilient login, dashboard checks, table waits, filters, playlists, and allow-list banners.
Events Event stream reference, subscription helpers, live logger widget, and basic metrics/durations collection.
Diagnostics Inline console, error cookbook, QA self-check, and performance tips for troubleshooting flows quickly.
Full API Reference Canonical message envelopes, request/response schemas, error codes, and return shapes for every action.
Compatibility Supported browsers/versions, permissions nuances, CSP/iframe notes, and known limitations or workarounds.
Advanced Patterns Idempotent flows, retries/backoff, state restore after reload, playlist strategies, and robust selectors.
Favorites Star and group URLs, quick actions (open/focus/navigate), export/import sets, and playlist runs from favorites.
Real-World Flows (E2E) Run end-to-end flows from your admin page: helper setup, modern message contract, copy-paste journeys (login, multi-tab, extract), lightweight assertions, best practices, and troubleshooting.