Skip to content

Commit e3571cc

Browse files
Tighten agent-facing instructions + verify autonomous flow live
Minimize token cost of the strings an AI agent parses: - sentinel.ts: runtime `instructions` now leads with protocol: 'v2ray' (zero-admin Windows path) and drops the verbose setup() comment; was stale (no protocol) and contradicted the Windows-100 docs. - index.ts /manifest: collapse the V2Ray/zero-prereq explanation that was repeated 6x across tldr, platforms, connectWindows.{zeroPrereq, wireguardUpgrade,default} and connect.protocolNote into one canonical place (connectWindows). Terse pointers elsewhere. ~half the bytes, every load-bearing fact preserved. Autonomous-flow E2E (agent pays node directly in P2P, no operator): fresh-test/connect-autonomous.mjs + AUTONOMOUS-E2E-RESULTS.txt. Live PASS on Base/Sentinel: 1 GB per-GB session 45433200, v2ray, US node, exit IP verified through tunnel, 40.19 P2P spent (~live median). Mnemonic passed via env, never committed.
1 parent 6c33b9e commit e3571cc

4 files changed

Lines changed: 151 additions & 10 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
x402 AUTONOMOUS-FLOW E2E — LIVE RESULT
2+
========================================
3+
Date: 2026-06-14 (UTC)
4+
Flow: Autonomous — agent holds its own P2P and pays the node DIRECTLY.
5+
No x402 server, no operator, no fee-grant, no MsgShareSubscription.
6+
Runner: fresh-test/connect-autonomous.mjs
7+
SDK: blue-js-sdk@2.8.0 (blue-js-sdk/ai-path connect())
8+
Protocol: v2ray (zero-admin Windows path)
9+
Country: US (SDK auto-picked node)
10+
11+
RESULT: PASS
12+
------------
13+
Agent wallet: sent12r7g5zqj3w6glfqtxztc9qhd82yg4lddvezjt4
14+
Node selected: sentnode1qjy6hphezyqzf4l42peut3tgd45c8rpylczs6h
15+
"0_Premium_Service_100G_Nibiru129" — Santa Clara, United States
16+
v2ray, qualityScore=55
17+
Session: 45433200 (per-GB, 1 GB)
18+
Session TX: 9BD199C324800C2C05E6BA46539A9D377AAA0E14BA97D0987DC4DC6D42218AA1
19+
Handshake: V2Ray VLESS/TCP/TLS — 104.252.19.187:6699 — connected
20+
Exit IP: 104.252.19.187 (SDK-verified THROUGH the tunnel)
21+
Host IP: 67.169.16.153
22+
Routed: YES — exit IP differs from host IP
23+
24+
COST
25+
----
26+
Before: 1000.00 P2P
27+
After: 959.81 P2P
28+
Spent: 40.19 P2P for a 1 GB per-GB session
29+
(right at the live network median of ~40 P2P/GB)
30+
Gas: included in the 40.19 (MsgStartSession gas paid by the agent itself)
31+
32+
TIMING
33+
------
34+
End-to-end connect: ~67 s
35+
- node discovery (probed 200 US nodes, 197 online): ~46 s
36+
- session TX broadcast + index: ~7 s
37+
- V2Ray handshake + tunnel verify: ~13 s
38+
39+
NOTES
40+
-----
41+
1. The agent paid the node directly. No operator involvement at any step —
42+
this is the fully decentralized, self-custody path.
43+
44+
2. V2Ray is a SOCKS-proxy tunnel, NOT a full system TUN route (that's WireGuard).
45+
So a top-level fetch() on the host's default route still shows the host IP.
46+
The SDK's own through-tunnel IP check (vpn.ip = 104.252.19.187) is the source
47+
of truth and confirms traffic exits via the node. For full system routing on
48+
Windows, use protocol: 'wireguard' (admin-once) or route the app through the
49+
returned SOCKS port.
50+
51+
3. Disconnect was SOFT — session 45433200 is preserved on-chain (status=1) for
52+
reuse, so the remaining ~0.96 GB of the 1 GB quota stays available. No
53+
MsgCancelSession broadcast. To hard-end and settle the deposit, use
54+
disconnectAndEndSession().
55+
56+
4. Zero prerequisites held: admin=true, v2ray binary present. On a non-admin box
57+
the v2ray path still works (binary auto-downloads to ~/.sentinel-sdk/bin).

fresh-test/connect-autonomous.mjs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* x402 Autonomous-path E2E — agent pays the node directly in P2P.
3+
*
4+
* No x402 server, no operator, no fee-grant. The agent holds its own P2P and
5+
* calls connect() with protocol: 'v2ray' (zero-admin Windows path). Verifies the
6+
* public IP actually changes, then disconnects.
7+
*
8+
* Run: AGENT_MNEMONIC="..." node fresh-test/connect-autonomous.mjs
9+
*/
10+
11+
import { connect, disconnect, getBalance } from 'blue-js-sdk/ai-path';
12+
13+
const MNEMONIC = process.env.AGENT_MNEMONIC;
14+
if (!MNEMONIC) {
15+
console.error('ERROR: set AGENT_MNEMONIC in the environment');
16+
process.exit(1);
17+
}
18+
19+
const COUNTRY = process.env.AGENT_COUNTRY || 'US';
20+
const PROTOCOL = process.env.AGENT_PROTOCOL || 'v2ray'; // zero admin, ~70% of nodes
21+
22+
const t0 = Date.now();
23+
const secs = (t) => ((Date.now() - t) / 1000).toFixed(1);
24+
25+
async function publicIp() {
26+
try {
27+
const r = await fetch('https://api.ipify.org?format=json', { signal: AbortSignal.timeout(8000) });
28+
return (await r.json()).ip;
29+
} catch (e) {
30+
return `unknown (${e.message})`;
31+
}
32+
}
33+
34+
console.log('='.repeat(64));
35+
console.log(' x402 AUTONOMOUS E2E — agent pays node directly (P2P)');
36+
console.log(` protocol=${PROTOCOL} country=${COUNTRY}`);
37+
console.log('='.repeat(64));
38+
39+
// ai-path connect() registers cleanup handlers internally (idempotent) — no manual call needed.
40+
41+
const bal = await getBalance(MNEMONIC);
42+
console.log(` P2P balance: ${bal.p2p ?? bal.balance ?? JSON.stringify(bal)}`);
43+
44+
const ipBefore = await publicIp();
45+
console.log(` IP before: ${ipBefore}`);
46+
47+
console.log('\n Connecting (pays 1 GB session directly to the node)...');
48+
let vpn;
49+
try {
50+
vpn = await connect({
51+
mnemonic: MNEMONIC,
52+
country: COUNTRY,
53+
protocol: PROTOCOL,
54+
gigabytes: 1,
55+
timeout: 120000,
56+
onProgress: (step, detail) => console.log(` [${step}] ${detail}`),
57+
});
58+
} catch (err) {
59+
console.error(`\n CONNECT FAILED (${err.code || 'ERR'}): ${err.message}`);
60+
process.exit(1);
61+
}
62+
63+
// The SDK verifies the exit IP through the tunnel itself (vpn.ip). For V2Ray
64+
// (SOCKS proxy) this is the source of truth — a top-level fetch() on the host's
65+
// default route does NOT traverse the proxy, so compare vpn.ip to the host IP.
66+
const tunnelIp = vpn.ip ?? null;
67+
const routed = tunnelIp && tunnelIp !== ipBefore;
68+
console.log(`\n CONNECTED in ${secs(t0)}s`);
69+
console.log(` session: ${vpn.sessionId}`);
70+
console.log(` protocol: ${vpn.protocol ?? vpn.serviceType}`);
71+
console.log(` node: ${vpn.nodeAddress}`);
72+
console.log(` tunnel IP:${tunnelIp ?? 'n/a'} (SDK-verified through tunnel)`);
73+
console.log(` IP before: ${ipBefore}`);
74+
console.log(` Exit IP: ${tunnelIp}${routed ? 'DIFFERENT ✓ routed' : 'same as host — NOT routed'}`);
75+
76+
console.log('\n Disconnecting...');
77+
try {
78+
await disconnect();
79+
console.log(' Disconnected cleanly.');
80+
} catch (err) {
81+
console.error(` Disconnect warning: ${err.message}`);
82+
}
83+
84+
console.log(`\n DONE in ${secs(t0)}s — routed: ${routed ? 'PASS' : 'CHECK'}`);

server/src/index.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ app.get('/manifest', (_req, res) => {
302302
'Install (agent side only — you do NOT clone this repo): `npm install @x402/fetch @x402/evm viem blue-js-sdk`. That is the complete dependency set for paying and connecting. The express/@x402/express/dotenv packages are operator-only — ignore them.',
303303
`POST ${network === 'eip155:8453' ? 'https://x402.sentinel.co' : '/'}/vpn/connect/{1day|7days|30days} with { sentinelAddr } using @x402/fetch to auto-sign the 402. Tiers cost $0.033 / $0.233 / $1.00 USDC — pick the smallest that covers your need; $0.033 is enough to start. Add { country: "DE" } to get a node in a specific country (GET /nodes lists what is available). Payment is identical on every OS.`,
304304
'Connecting differs by OS. macOS & Linux: use the native CLI `sentinel-dvpncli` (go install github.com/sentinel-official/sentinel-dvpncli@latest — needs Go 1.24+). The CLI shells out to the system tunnel binary, so install `wireguard-tools` (provides `wg-quick`; most nodes are WireGuard) and, for V2Ray nodes, a `v2ray` binary on PATH. Then import the key (`printf \'%s\\n\\n\' "$MNEMONIC" | sentinel-dvpncli keys add agent --keyring.backend test`), `tx session-start <nodeAddress> --subscription-id <id> --tx.fee-granter-addr <feeGranter> --tx.from-name agent --keyring.backend test`, resolve the id with `query sessions --account-addr <sent1...> --subscription-id <id> --output-format json`, then `connect <sessionId>` (sudo to bring the interface up). See sentinel.connectMacLinux. Only Fedora is unsupported (SELinux blocks VPNs).',
305-
'Windows: NO manual install and NO admin needed if you pin V2Ray. `connect({ mnemonic, subscriptionId, feeGranter, nodeAddress, protocol: "v2ray" })` auto-downloads the v2ray binary (userspace SOCKS5, ~70% of nodes) on first use — zero prerequisites beyond the npm install. Optionally `await setup()` first to pre-fetch it. WireGuard nodes (the other ~30%, faster) need admin ONCE for the MSI; the SDK checks admin before payment and refuses a WireGuard node when not elevated, so no funds are wasted — if you cannot elevate, stay on protocol: "v2ray". See sentinel.connectWindows.',
305+
'Windows: zero prereqs. `connect({ mnemonic, subscriptionId, feeGranter, nodeAddress, protocol: "v2ray" })` auto-downloads the v2ray binary (userspace SOCKS5, ~70% of nodes, no admin). WireGuard (~30%, faster) needs admin once; the SDK gates on admin BEFORE payment, so no funds are wasted. See sentinel.connectWindows.',
306306
'Tunnel is up. You paid as little as $0.033 USDC (1day) up to $1.00 (30day), zero gas on Base, zero gas on Sentinel.',
307307
],
308308
network: {
@@ -524,7 +524,7 @@ app.get('/manifest', (_req, res) => {
524524
returns: { address: 'sent1... (use this as sentinelAddr)', mnemonic: 'string (persist this)' },
525525
note: 'Generate once per agent. The address goes in the request body; the mnemonic signs MsgStartSession.',
526526
},
527-
platforms: 'Payment is identical on every OS; only the connect step differs. Windows is the zero-prereq path: with protocol: "v2ray" the JS SDK auto-downloads the tunnel binary (no admin, no manual install — see connectWindows / connect); WireGuard nodes there are an optional admin-once upgrade. macOS & Linux use the native CLI (sentinel-dvpncli + a system tunnel binary you install yourself — see connectMacLinux). Only Fedora is unsupported: its SELinux blocks VPN interfaces and cannot be bypassed programmatically.',
527+
platforms: 'Payment is identical on every OS; only connect differs. Windows: zero-prereq with protocol: "v2ray" (see connectWindows). macOS & Linux: native CLI sentinel-dvpncli (see connectMacLinux). Fedora unsupported (SELinux blocks VPN interfaces).',
528528
connectMacLinux: {
529529
tool: 'sentinel-dvpncli',
530530
install: 'go install github.com/sentinel-official/sentinel-dvpncli@latest (Go 1.24+); binary lands in $(go env GOPATH)/bin',
@@ -542,18 +542,18 @@ app.get('/manifest', (_req, res) => {
542542
},
543543
connectWindows: {
544544
package: 'blue-js-sdk/ai-path',
545-
zeroPrereq: 'Windows needs NO manual install and NO admin if you pass protocol: "v2ray". V2Ray is a userspace SOCKS5 proxy (no system access); connect() auto-downloads the v2ray binary to ~/.sentinel-sdk/bin on first use (no admin). This covers ~70% of the network (~630 nodes). This is the fully self-provisioning Windows path.',
546545
recommended: 'connect({ mnemonic, subscriptionId, feeGranter, nodeAddress, protocol: "v2ray" })',
547-
requestHint: 'For a guaranteed-connectable node, also send { sentinelAddr, protocol: "v2ray" } in the POST body so the operator provisions a V2Ray node. GET /nodes?protocol=v2ray lists them.',
548-
wireguardUpgrade: 'WireGuard nodes (the other ~30%, faster) need admin ONCE: the WireGuard MSI install and tunnel-service creation are elevated operations. setup() auto-installs the MSI only when run elevated (cscript run-admin.vbs setup.js triggers a single UAC prompt; a Windows service running as SYSTEM/admin needs none). The SDK checks admin BEFORE payment and refuses a WireGuard node when not elevated — so no USDC and no P2P is ever wasted. If you cannot elevate, stay on protocol: "v2ray".',
549-
default: 'Calling connect() WITHOUT a protocol searches both node types — on a non-admin box it may pick a WireGuard node and fail the admin gate (no funds lost). Agents that cannot guarantee admin should always pin protocol: "v2ray".',
546+
v2ray: 'protocol: "v2ray" = zero prereq. Userspace SOCKS5; connect() auto-downloads the binary to ~/.sentinel-sdk/bin (no admin, no manual install). ~70% of nodes (~630).',
547+
wireguard: 'protocol: "wireguard" = ~30% of nodes, faster, but needs admin ONCE (MSI install). The SDK gates on admin BEFORE payment, so a non-admin box wastes no funds. Pin "v2ray" if you cannot elevate.',
548+
noProtocol: 'Omitting protocol searches both types and may pick a WireGuard node that fails the admin gate (no funds lost). Pin "v2ray" if admin is not guaranteed.',
549+
requestHint: 'To force a V2Ray node, POST { sentinelAddr, protocol: "v2ray" }. GET /nodes?protocol=v2ray lists them.',
550550
},
551551
connect: {
552552
package: 'blue-js-sdk/ai-path',
553-
platform: 'Windows path (JS tunnel). On macOS/Linux use connectMacLinux (native CLI) instead.',
553+
platform: 'Windows (JS tunnel). macOS/Linux use connectMacLinux.',
554554
fn: 'connect({ mnemonic, subscriptionId, feeGranter, nodeAddress, protocol: "v2ray" })',
555-
protocolNote: 'protocol is optional but RECOMMENDED on Windows: "v2ray" = zero admin, zero manual install, auto-downloaded binary, ~70% of nodes. Omit it only if you have admin (then WireGuard nodes are also usable). See connectWindows.',
556-
argsFrom: 'Pass response.success fields directly. mnemonic is the one from createWallet().',
555+
protocolNote: 'On Windows pin protocol: "v2ray" (zero admin) unless you have admin. See connectWindows.',
556+
argsFrom: 'Pass response.success fields directly. mnemonic is from createWallet().',
557557
returns: { connected: 'boolean', ip: 'string', protocol: 'wireguard | v2ray' },
558558
gasCost: '0 P2P — operator fee-grants MsgStartSession',
559559
},

server/src/sentinel.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ async function buildProvisionResult(opts: {
487487
sentinelTxHash: opts.txHash,
488488
expiresAt: opts.expiresAt,
489489
operatorAddress,
490-
instructions: `import { setup, connect } from 'blue-js-sdk/ai-path'; await setup(); /* installs V2Ray automatically if no tunnel binary is present */ await connect({ mnemonic, nodeAddress: '${recommended}', subscriptionId: '${opts.subscriptionId}', feeGranter: '${operatorAddress}' })`,
490+
instructions: `import { connect } from 'blue-js-sdk/ai-path'; await connect({ mnemonic, protocol: 'v2ray', nodeAddress: '${recommended}', subscriptionId: '${opts.subscriptionId}', feeGranter: '${operatorAddress}' }); // protocol:'v2ray' = no admin; binary auto-installs. gas paid by feeGranter.`,
491491
};
492492
}
493493

0 commit comments

Comments
 (0)