Skip to content

Runtime Network Permissions

Overview

Code-Mode now supports runtime network permissions via elicitations. When code tries to access a restricted network (private IPs, localhost), the user is prompted for permission. This makes local network MCPs practical while maintaining security.

The Problem

Phase 4 network isolation blocks private IPs (192.168.x.x) by default for security. But local network MCPs need access to devices:

  • LG TV at 192.168.1.100
  • Philips Hue at 192.168.1.50
  • Home Assistant at homeassistant.local:8123
  • Dev servers at localhost:3000

The Solution

When restricted network access is attempted, show a permission dialog asking the user:

┌──────────────────────────────────────┐
│  Network Access Permission           │
├──────────────────────────────────────┤
│  LG Remote wants to access           │
│  local network (private IP):         │
│                                      │
│  http://192.168.1.100:3000/power-on │
│                                      │
│  Allow this network access?          │
│                                      │
│  [Allow Once] [Allow Always] [Deny]  │
└──────────────────────────────────────┘

User decides:

  • Allow Once: Permission for 1 hour (temporary)
  • Allow Always: Permission forever (trusted device)
  • Deny: Block access

Real-World Example: LG Remote

Scenario

User asks: "Turn on my TV and set volume to 15"

AI generates code:

javascript
// Check TV status
const status = await fetch('http://192.168.1.100:3000/status');
const data = await status.json();

// Turn on if off
if (data.power === 'off') {
  await fetch('http://192.168.1.100:3000/power-on', { method: 'POST' });
}

// Set volume
await fetch('http://192.168.1.100:3000/volume', {
  method: 'POST',
  body: JSON.stringify({ level: 15 })
});

What Happens

First Request (/status):

  1. Code tries: fetch('http://192.168.1.100:3000/status')
  2. NetworkPolicyManager: ❌ Private IP (blocked by default)
  3. Elicitation shown:
    Worker Code wants to access local network (private IP):
    http://192.168.1.100:3000/status
    
    Allow this network access?
    [Allow Once] [Allow Always] [Deny]
  4. User clicks: [Allow Always] ← Trust this TV
  5. Permission cached permanently
  6. Request proceeds → TV returns status

Second Request (/power-on):

  1. Code tries: fetch('http://192.168.1.100:3000/power-on')
  2. NetworkPolicyManager checks cache: ✅ Approved (same URL)
  3. No prompt - uses cached permission
  4. Request proceeds → TV powers on

Third Request (/volume):

  1. Code tries: fetch('http://192.168.1.100:3000/volume')
  2. NetworkPolicyManager checks cache: ✅ Approved (same URL)
  3. No prompt - uses cached permission
  4. Request proceeds → Volume set

Result: User prompted once, subsequent requests work automatically!

Permission Types

1. Allow Once (Temporary)

  • Duration: 1 hour
  • Use case: Testing, one-time access, untrusted networks
  • Example: Accessing a friend's dev server
User clicks: [Allow Once]
└→ Permission expires after 1 hour
└→ Next request after expiry: shows prompt again

2. Allow Always (Permanent)

  • Duration: Forever (until manually revoked)
  • Use case: Trusted devices, home network, local development
  • Example: Your LG TV, Philips Hue, Home Assistant
User clicks: [Allow Always]
└→ Permission cached permanently
└→ All future requests: automatic (no prompts)
└→ Can revoke anytime via networkPolicy.revokePermission()

3. Deny (Block)

  • Duration: Blocked permanently
  • Result: Request fails with error
  • Example: Suspicious or unauthorized access
User clicks: [Deny]
└→ Request blocked
└→ Error: "Network request blocked: User denied permission"

Permission Management

List Permissions

javascript
const permissions = networkPolicyManager.getPermissions();

// Output:
[
  {
    url: 'http://192.168.1.100:3000/status',
    approved: true,
    permanent: true  // "Allow Always"
  },
  {
    url: 'http://localhost:3000/api',
    approved: true,
    permanent: false  // "Allow Once" (expires in 1h)
  }
]

Revoke Permission

javascript
// Revoke specific URL
networkPolicyManager.revokePermission('http://192.168.1.100:3000/status');

// Next request to that URL: shows prompt again

Clear All Permissions

javascript
// Clear all cached permissions
networkPolicyManager.clearPermissions();

// All future restricted requests: show prompts

Implementation

Setup (Main Thread)

javascript
import { NetworkPolicyManager, SECURE_NETWORK_POLICY } from './network-policy.js';
import { CodeExecutor } from './code-executor.js';

// Create elicitation function
const elicitationFunction = async (params) => {
  // If client supports elicitations (Claude Desktop)
  if (supportsElicitations) {
    return await showUIElicitation(params);
  }

  // Fallback to system dialog
  return await showSystemDialog(params);
};

// Create network policy with elicitations
const networkPolicy = new NetworkPolicyManager(
  SECURE_NETWORK_POLICY,  // Default: block private IPs
  elicitationFunction     // Show permission prompts
);

// Create code executor
const executor = new CodeExecutor(
  toolsProvider,
  toolExecutor,
  undefined,
  bindingsManager,
  networkPolicy  // With elicitation support
);

User Code (Sandbox)

javascript
// User code - no changes needed!
const response = await fetch('http://192.168.1.100:3000/status');

// First time: permission prompt shown
// Second time: uses cached permission

Security Guarantees

User sees exactly what's being accessed

  • Full URL shown in elicitation
  • Access type clearly labeled (localhost, private IP, etc.)

User controls all network access

  • Every restricted access requires approval
  • Can deny suspicious requests

Principle of least privilege

  • Only approved URLs work
  • Can revoke permissions anytime

Audit trail

  • All permissions logged
  • Can list all approved URLs

No blanket permissions

  • More secure than allowPrivateIPs: true
  • Each URL requires separate approval

Benefits Over Static Policies

ApproachSecurityFlexibilityUser Experience
Block all✅ Secure❌ Inflexible❌ Breaks local MCPs
Allow all❌ Insecure✅ Flexible✅ Works, but risky
Static policies✅ Secure⚠️ Pre-configured⚠️ Complex setup
Runtime permissions✅ Secure✅ Flexible✅ Simple & transparent

Elicitation Format

The elicitation message includes:

  1. Requester: Who is requesting access

    • "Worker Code" (direct fetch from sandbox)
    • "LG Remote" (from binding, if supported)
  2. Access Type: What kind of network

    • "localhost" (127.0.0.1, ::1)
    • "local network (private IP)" (192.168.x.x, 10.x.x.x)
    • "external domain" (public internet)
  3. Full URL: Exact URL being accessed

    • http://192.168.1.100:3000/power-on
    • Complete transparency
  4. Clear Options:

    • Allow Once - Temporary (1 hour)
    • Allow Always - Permanent
    • Deny - Block

Use Cases

1. LG TV Remote

javascript
// User: "Turn on my TV"
await fetch('http://192.168.1.100:3000/power-on');

// Prompt: "...wants to access local network: http://192.168.1.100..."
// User: [Allow Always] ← Trust this TV
// Future TV commands: automatic

2. Philips Hue Lights

javascript
// User: "Set living room lights to blue"
await fetch('http://192.168.1.50/api/lights/1/state', {
  method: 'PUT',
  body: JSON.stringify({ on: true, hue: 46920 })
});

// Prompt once → [Allow Always] → All light controls work

3. Home Assistant

javascript
// User: "Turn off all lights"
await fetch('http://homeassistant.local:8123/api/services/light/turn_off', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer ...' }
});

// Prompt once → [Allow Always] → All home automation works

4. Local Dev Server (Temporary)

javascript
// User: "Check my dev server status"
await fetch('http://localhost:3000/api/health');

// Prompt: "...wants to access localhost..."
// User: [Allow Once] ← Only for this debugging session
// Permission expires after 1 hour

Summary

Runtime network permissions via elicitations make Code-Mode practical for real-world use cases involving local devices while maintaining enterprise-grade security through informed user consent.

  • 🔒 Secure: User controls all restricted network access
  • 🎯 Flexible: Works with any local network device
  • 👤 Transparent: User sees exactly what's accessed
  • 🚀 Practical: LG Remote, Philips Hue, Home Assistant all work
  • Efficient: Permissions cached (user not spammed)
  • 🔧 Manageable: Can revoke/clear permissions anytime

Released under the Elastic License 2.0.