Skip to content

Instantly share code, notes, and snippets.

@at-wr
Last active March 12, 2026 17:56
Show Gist options
  • Select an option

  • Save at-wr/d9ada66c397cc45d2d5e0f647f3361d8 to your computer and use it in GitHub Desktop.

Select an option

Save at-wr/d9ada66c397cc45d2d5e0f647f3361d8 to your computer and use it in GitHub Desktop.
connectivityd

connectivityd

Surge Module that automatically switches policy groups when your network location changes — so you don't have to.

Designed for people who travel or live abroad while maintaining a local proxy / home server (e.g. via Surge Ponte), and need domestic traffic to route through home when away.

How it works

When a network change is detected (Wi-Fi / Cellular switch, Airplane Mode toggle), the script checks your exit IP against your home country. It then sets your policy groups to either Home or Abroad mode:

Group Home Abroad
Domestic DIRECT Routes through your home device / Local Proxy
Final Your proxy group DIRECT
International Your proxy group DIRECT
Proxy (optional) Restores previous selection DIRECT

You can configure every parameter in Surge Module preferences.

Install

Install as a remote module in Surge iOS:

https://gist.githubusercontent.com/at-wr/d9ada66c397cc45d2d5e0f647f3361d8/raw/connectivityd.sgmodule

After installation, tap the module to configure parameters.

Parameters

Location

Parameter Description Default
home_country ISO 3166-1 country code JP

Policy Group Names

These must match your Surge config exactly.

Parameter Description Default
grp_domestic Group for domestic traffic Domestic
grp_final FINAL / catch-all group Final
grp_intl Group for international services International

Routing

What each group resolves to in each mode.

Parameter Description Default
home_domestic Domestic exit at home DIRECT
abroad_domestic Domestic exit abroad (e.g. DEVICE:PONTENAME, a proxy node, or a group) DIRECT
home_final FINAL at home Proxy
abroad_final FINAL abroad DIRECT
home_intl International at home Proxy
abroad_intl International abroad DIRECT

Proxy Auto-restore (optional)

Leave both empty to skip. Useful if your proxy group should be DIRECT abroad but restored to a specific policy when returning home.

Parameter Description Default
proxy_group A select-group to manage (empty)
home_default_proxy Policy to restore when returning home (empty)

Timing

Parameter Description Default
max_retries Max detection attempts 5
initial_delay Ms before first detection 3000
retry_interval Ms between retries 5000

Example: Surge Ponte user based in China

A user in China with a Mac mini at home running Surge Ponte, who frequently travels abroad or uses a foreign SIM (e.g. Google Fi):

Parameter Value
home_country CN
grp_domestic 🇨🇳 China
grp_final 🦊 Mask
grp_intl 🇺🇳 International
abroad_domestic DEVICE:HOMELAB
home_final 🧭 Primary
home_intl 🦊 Mask
proxy_group 🧭 Primary
home_default_proxy 💿 ProxySet

Home (Local Cellular / Wi-Fi): domestic traffic goes direct, international goes through proxy, FINAL catches everything else through proxy.

Abroad: domestic traffic routes through Ponte back home for a Chinese IP, international and FINAL go direct since you're already overseas.

Notes

  • Detection uses ipinfo.io — the module injects a DOMAIN,ipinfo.io,DIRECT rule automatically.
  • After toggling airplane mode, cellular may take up to 20 seconds to reconnect. The retry mechanism (5 attempts × 5s intervals) covers this window.
  • Notifications are delivered on each switch so you know which mode is active.

License

MIT

// connectivityd.js
// Automatically switch Surge policy groups based on network location.
// All parameters are configurable via connectivityd.sgmodule.
const GEO_API = "https://ipinfo.io/country";
// --- Parse query-string arguments from module ---
function parseArgs(str) {
var params = {};
if (!str) return params;
str.split("&").forEach(function (pair) {
var idx = pair.indexOf("=");
if (idx > 0) {
params[decodeURIComponent(pair.substring(0, idx))] = decodeURIComponent(
pair.substring(idx + 1),
);
}
});
return params;
}
var args = parseArgs($argument);
// Location
var HOME_COUNTRY = args.home_country || "HK";
// Policy group names
var GRP_DOMESTIC = args.grp_domestic || "Domestic";
var GRP_FINAL = args.grp_final || "Final";
var GRP_INTL = args.grp_intl || "International";
// Routing targets per mode
var HOME_DOMESTIC = args.home_domestic || "DIRECT";
var ABROAD_DOMESTIC = args.abroad_domestic || "";
var HOME_FINAL = args.home_final || "";
var ABROAD_FINAL = args.abroad_final || "DIRECT";
var HOME_INTL = args.home_intl || "";
var ABROAD_INTL = args.abroad_intl || "DIRECT";
// Optional: proxy group auto-restore
var PROXY_GROUP = args.proxy_group || "";
var HOME_DEFAULT = args.home_default_proxy || "";
// Timing
var MAX_RETRIES = parseInt(args.max_retries, 10) || 5;
var INITIAL_DELAY = parseInt(args.initial_delay, 10) || 3000;
var RETRY_INTERVAL = parseInt(args.retry_interval, 10) || 5000;
// --- Detection with retry ---
function detect(attempt) {
$httpClient.get(
{ url: GEO_API, timeout: 10 },
function (error, response, data) {
if (error) {
console.log(
"connectivityd: attempt " +
attempt +
"/" +
MAX_RETRIES +
" failed - " +
error,
);
if (attempt < MAX_RETRIES) {
setTimeout(function () {
detect(attempt + 1);
}, RETRY_INTERVAL);
} else {
$notification.post(
"Network",
"Detection Failed",
"Could not determine your location after " +
MAX_RETRIES +
" attempts.",
);
$done();
}
return;
}
try {
var country = data.trim();
var isHome = country === HOME_COUNTRY;
// Switch core groups
$surge.setSelectGroupPolicy(
GRP_DOMESTIC,
isHome ? HOME_DOMESTIC : ABROAD_DOMESTIC,
);
$surge.setSelectGroupPolicy(
GRP_FINAL,
isHome ? HOME_FINAL : ABROAD_FINAL,
);
$surge.setSelectGroupPolicy(GRP_INTL, isHome ? HOME_INTL : ABROAD_INTL);
// Optional: proxy group switching
if (PROXY_GROUP) {
if (isHome) {
if (HOME_DEFAULT) {
var groups = $surge.selectGroupDetails();
var current = groups.decisions[PROXY_GROUP];
if (current === "DIRECT") {
$surge.setSelectGroupPolicy(PROXY_GROUP, HOME_DEFAULT);
}
}
} else {
$surge.setSelectGroupPolicy(PROXY_GROUP, "DIRECT");
}
}
// Notification
if (isHome) {
$notification.post(
"Network",
"Connected to Home",
"Routing optimized for domestic access.",
);
} else {
$notification.post(
"Network",
"Connected Abroad",
"Routing through encrypted network for internal services.",
);
}
console.log(
"connectivityd: " +
(isHome ? "home" : "abroad") +
" (" +
country +
", attempt " +
attempt +
")",
);
} catch (e) {
console.log("connectivityd: parse error - " + e);
}
$done();
},
);
}
// --- Entry ---
setTimeout(function () {
detect(1);
}, INITIAL_DELAY);
#!name = connectivityd
#!desc = Auto-switch policy groups between Home and Abroad mode. (@at-wr)
#!system = ios
#!arguments = home_country:"JP",grp_domestic:"Domestic",grp_final:"Final",grp_intl:"International",home_domestic:"DIRECT",abroad_domestic:"DIRECT",home_final:"Proxy",abroad_final:"DIRECT",home_intl:"Proxy",abroad_intl:"DIRECT",proxy_group:"",home_default_proxy:"",max_retries:"5",initial_delay:"3000",retry_interval:"5000"
#!arguments-desc = home_country: Home country code (CN, US, JP, HK)\ngrp_domestic: Domestic traffic group\ngrp_final: FINAL group\ngrp_intl: International group\nhome_domestic: Domestic exit at home\nabroad_domestic: Domestic exit abroad (e.g. DEVICE:Name)\nhome_final: FINAL at home\nabroad_final: FINAL abroad\nhome_intl: Intl at home\nabroad_intl: Intl abroad\nproxy_group: (optional) Proxy group for auto-restore\nhome_default_proxy: (optional) Restore target\nmax_retries: Retry count\ninitial_delay: Ms before first check\nretry_interval: Ms between retries
[Rule]
DOMAIN,ipinfo.io,DIRECT
[Script]
connectivityd = type=event, event-name=network-changed, timeout=60, script-path=https://gist.githubusercontent.com/at-wr/d9ada66c397cc45d2d5e0f647f3361d8/raw/connectivityd.js, argument=home_country={{{home_country}}}&grp_domestic={{{grp_domestic}}}&grp_final={{{grp_final}}}&grp_intl={{{grp_intl}}}&home_domestic={{{home_domestic}}}&abroad_domestic={{{abroad_domestic}}}&home_final={{{home_final}}}&abroad_final={{{abroad_final}}}&home_intl={{{home_intl}}}&abroad_intl={{{abroad_intl}}}&proxy_group={{{proxy_group}}}&home_default_proxy={{{home_default_proxy}}}&max_retries={{{max_retries}}}&initial_delay={{{initial_delay}}}&retry_interval={{{retry_interval}}}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment