Live on Base Mainnet

Private internet
for AI agents

AI agents pay USDC on Base over HTTP 402 and get private VPN access through Sentinel's decentralized node network. One request, one signature, connected. No KYC. No accounts. Everything on-chain.

Windows live
macOS live
Linux live

Only exception: Fedora — its SELinux blocks VPNs and can’t be worked around. macOS, Ubuntu and other mainstream Linux are fine.

agent dVPN — use cases 5 uses
Agent privacy Mask the agent's IP — no request traces back to its operator. 01
Geo-restricted exchanges Reach region-locked trading venues from anywhere. 02
Geo-locked APIs & data Fetch sources only served in certain countries. 03
Anonymity at scale Rotate exits across hundreds of servers to dodge IP bans and rate limits. 04
Censorship resistance Reach the open internet from blocked networks. 05

Pay per day. No subscriptions.

Pricing for the x402 Managed Plan. Agents pay upfront for exactly the days they need. USDC on Base, settled automatically by the x402 protocol. On the separate Autonomous path, agents hold P2P and pay nodes at their posted per-GB / per-hour rates.

Unlimited VPN

$1/month
$0.033 per day · USDC on Base
  • Decentralized Sentinel nodes
  • Unlimited bandwidth
  • WireGuard + V2Ray tunnels
  • Zero gas on Sentinel (fee granted)
  • HTTP 402 — standard protocol
EndpointCost
/vpn/connect/1day$0.033
/vpn/connect/7days$0.233
/vpn/connect/30days$1.00

One paid request, fully provisioned

This site is the x402 managed path: the agent pays USDC over HTTP 402 and the operator provisions everything (subscription share and a Sentinel gas grant) so the agent connects with one command, holding zero P2P tokens and paying zero gas. Just an EVM key with USDC on Base.

Live Now

x402 Managed Plan — one paid request

Agent pays USDC via HTTP 402 — live at x402.sentinel.co, from $0.033 for a day to $1.00 for 30 days. We add the agent to our subscription plan, grant gas allowance, and the agent connects with one command. Zero gas on Sentinel.

  • Agent sends POST /vpn/connect/30days
  • Server returns HTTP 402 + payment details
  • @x402/fetch auto-signs USDC (EIP-3009)
  • Facilitator settles payment on Base
  • Server runs MsgShareSubscription + MsgGrantAllowance
  • Agent connects — zero gas, one command
Gas$0 — fee granted by operator
Node choiceHundreds of servers in our plan (GET /nodes for the live list)
TrustMinimal — tunnel still direct
Best forQuick integration — only need USDC
Separate site ↗

Want full autonomy instead?

In the autonomous flow the agent holds its own P2P, picks any Sentinel node, and pays it directly. The agent controls its keys, gas, node selection, and payment.

GasAgent pays its own (bundled into the session TX)
PrerequisiteAgent holds P2P
PricingNode's posted rate — ~40 P2P/GB median, from ~2 P2P
TrustZero — no intermediary at all
Read the dedicated guide at autonomous.sentinel.co →
Current status: The x402 Managed Plan is live at https://x402.sentinel.co — hundreds of active servers in the plan, pricing from $0.033 (1 day) to $1.00 (30 days), and the only prerequisite is an EVM key holding USDC on Base. Want the fully self-funded flow with no operator in the loop? That lives on its own dedicated site: autonomous.sentinel.co. Same Sentinel network, same direct WireGuard/V2Ray tunnels.

How x402 subscription works

Live at https://x402.sentinel.co. Agent sends one HTTP request. Payment, settlement, subscription provisioning, and fee granting all happen automatically.

1

Agent sends request

POST /vpn/connect/30days with a Sentinel address in the body. Server returns HTTP 402 Payment Required with a PAYMENT-REQUIRED header containing USDC amount, payTo address, and network (Base).

Agent → Server
2

Payment signed automatically

@x402/fetch reads the 402 response, signs an EIP-3009 transferWithAuthorization for the exact USDC amount, and resends the request with a PAYMENT-SIGNATURE header. Agent's EVM key never leaves the agent.

Base · USDC
3

Facilitator settles on-chain

Our self-hosted facilitator verifies the EIP-3009 signature and settles the USDC transfer on Base. Fully decentralized: no Coinbase, no third party. Payment confirmed in ~2 seconds.

Automatic
4

Agent added to subscription plan

Server sends one atomic Sentinel transaction: MsgShareSubscription (adds agent to our plan) + MsgGrantAllowance (fee grant so agent pays zero gas). Agent can now start VPN sessions on any node in the plan, handshaking directly. We never see the tunnel or the traffic.

Agent ↔ Node

Prefer no operator at all?

There's a second, fully self-funded way onto Sentinel: the autonomous flow. The agent holds its own P2P tokens, picks any node on the network, settles directly with that node, and manages its own gas. No operator sits in the middle at all. It has a different trust model, so it lives on its own site.

✓ Verified live on mainnet

A representative run: a test agent ran the autonomous flow end-to-end with no operator in the loop. It picked a US node (Seattle), opened on-chain session 45943402, paid 40.19 P2P for a 1 GB V2Ray session, and the SDK confirmed the exit IP 192.3.53.166 through the tunnel in about 35 s. Session ID, node, exit IP, and timing vary run to run; that 40 P2P is right at the live network median, with the cheapest nodes around ~2 P2P/GB.

Read the autonomous guide at autonomous.sentinel.co →

The verified connect() reference, the V2Ray SOCKS-proxy footgun, and per-OS notes — all on the dedicated site. Agent-readable summary: autonomous.sentinel.co/llms.txt.

Integrate the x402 managed flow

Let the x402 server handle everything with @x402/fetch — the agent pays USDC on Base and connects with one call, no P2P and no gas. Prefer the fully self-funded path where the agent pays nodes directly? That lives at autonomous.sentinel.co.

● x402 Managed Plan Flow — Live Now

Install — Managed plan (x402 handles everything)
npm install @x402/fetch @x402/evm blue-js-sdk viem
Step 0: Prerequisites — fund an EVM key with USDC on Base
// You need ONE thing: an EVM private key with >= $1.00 USDC on Base (chainId 8453).
// No ETH needed — EIP-3009 is gasless from the agent side (facilitator pays Base gas).
// No P2P needed — the operator fee-grants Sentinel gas after provisioning.
//
// How to fund a fresh EVM key with USDC on Base:
//   * Coinbase / any CEX with Base withdrawals: withdraw USDC, pick "Base" as network.
//   * Canonical bridge: bridge.base.org (ETH L1 → Base, ~10 min).
//   * Third-party bridges: Across, Stargate (faster, small fee).
//   * Already on Base? Swap ETH → USDC on Uniswap or Aerodrome.
//
// USDC contract on Base: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 (6 decimals, native).
// Verify balance:
const bal = await fetch(`https://api.basescan.org/api?module=account&action=tokenbalance&contractaddress=0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913&address=${addr}`);
Step 1: Create Sentinel wallet + set up x402 payment client
import { x402Client, wrapFetchWithPayment } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm/exact/client';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import { setup, createWallet, connect, disconnect, rpcQueryNodesForPlan, createRpcQueryClientWithFallback } from 'blue-js-sdk/ai-path';

// Optional: pre-fetch V2Ray (no admin). connect({ protocol: 'v2ray' }) auto-fetches it anyway.
await setup();

// Create a Sentinel wallet (one-time — save the mnemonic!)
const wallet = await createWallet();
// wallet.address = 'sent1...'  wallet.mnemonic = '12 words'

// Set up x402 payment client with your EVM key
const account = privateKeyToAccount(process.env.EVM_KEY);
const viemClient = createWalletClient({
  account, chain: base, transport: http('https://mainnet.base.org'),
});
const scheme = new ExactEvmScheme({
  address: account.address,
  signTypedData: (msg) => viemClient.signTypedData(msg),
});
const client = new x402Client();
client.register('eip155:8453', scheme);
const paidFetch = wrapFetchWithPayment(fetch, client);
Step 2: Buy 30 days of VPN — Base mainnet
// Request VPN access — 402 → auto-sign → facilitator settles → provisioned
const res = await paidFetch('https://x402.sentinel.co/vpn/connect/30days', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ sentinelAddr: wallet.address, country: 'DE' }), // country optional — validated BEFORE payment
});

const provision = await res.json();
// {
//   provisioned: true,
//   subscriptionId: 1192288,
//   planId: 42,
//   feeGranter: 'sent12e03w...',     ← operator pays all gas
//   nodeAddress: 'sentnode1...',      ← node matching country — or choose from nodes[]
//   nodeCountry: 'Germany',           ← verified location of nodeAddress
//   nodes: ['sentnode1a...', ...],   ← full list of plan nodes
//   sentinelTxHash: 'F1FE3C...',
//   expiresAt: '2026-05-14T...',
// }
Step 3 — Windows: connect via blue-js-sdk (JS tunnel)
// connect(), createWallet(), rpcQueryNodesForPlan(), createRpcQueryClientWithFallback()
// already imported above from 'blue-js-sdk/ai-path'

// Resolve node — server returns one, but fall back to a live plan query if empty
let nodeAddress = provision.nodeAddress;
if (!nodeAddress) {
  const rpc = await createRpcQueryClientWithFallback();
  const { items } = await rpcQueryNodesForPlan(rpc, provision.planId, { status: 1, limit: 50 });
  nodeAddress = items[0]?.address;
}

// Connect using the provisioned subscription + fee grant
// Agent has 0 P2P tokens — operator covers all gas
const vpn = await connect({
  mnemonic: wallet.mnemonic,                    // from Step 1
  nodeAddress,                                  // from provision or fallback query
  subscriptionId: String(provision.subscriptionId),  // from Step 2
  feeGranter: provision.feeGranter,          // operator pays gas
  protocol: 'v2ray',                          // zero admin, zero manual install (Windows)
});

// vpn.ip is the exit IP verified THROUGH the tunnel by the SDK
console.log(`VPN active: ${vpn.ip} via ${vpn.protocol}`);
// { connected: true, ip: '104.234.x.x', protocol: 'v2ray',
//   sessionId: '39269345', nodeAddress: 'sentnode1...',
//   socksPort: 11375 }  ← v2ray: route your traffic through THIS port (dynamic — never hardcode)

// Disconnect also uses fee grant — 0 tokens needed
await disconnect();
V2Ray routing — choose your scope

protocol: 'v2ray' runs a SOCKS5 proxy on vpn.socksPort. It can encrypt the entire device or only the requests you choose. Pick one:

  • Whole device. Pass systemProxy: true to connect(). The SDK sets the system proxy (Windows registry / macOS networksetup / Linux gsettings) and all traffic routes through the node. The original proxy is restored on disconnect().
  • Per-request (split tunnel). Omit systemProxy and send only the requests you choose through vpn.socksPort. Other traffic (chain, RPC, npm) stays direct and fast. Use socks5h:// (not socks5://) so DNS resolves at the exit node, preventing a DNS leak.
  • WireGuard. Pass protocol: 'wireguard' for a kernel-level full tunnel (admin once on Windows).

Two facts that catch agents: (1) For per-request routing, Node's native fetch() ignores SOCKS5 agents and exits on the real IP — use axios with SocksProxyAgent and adapter: 'http'. (2) socksPort is dynamic — read it from the connect() result, never hardcode it. vpn.ip is the exit IP, already verified through the tunnel by the SDK.

// Whole device — all traffic routes through the node, restored on disconnect():
const vpn = await connect({ mnemonic, nodeAddress, subscriptionId, feeGranter, protocol: 'v2ray', systemProxy: true });

// Per-request — route one call through the proxy. Use axios; native fetch() ignores SOCKS5.
import { SocksProxyAgent } from 'socks-proxy-agent';
import axios from 'axios';
const proxy = new SocksProxyAgent(`socks5h://127.0.0.1:${vpn.socksPort}`);
const res = await axios.get('https://api.ipify.org', {
  httpAgent: proxy, httpsAgent: proxy, adapter: 'http',
}); // res.data === vpn.ip

Verified live (Windows). Split tunnel was tested end-to-end on a US V2Ray node (session 45984926): the axios+SocksProxyAgent request returned the exit IP 45.38.249.7 while a plain fetch() on the same machine still returned the host IP 67.169.16.153 — only the routed request changed IP, and the native fetch() footgun is real.

Step 3 — macOS & Linux: WireGuard via native CLI (optional)
// V2Ray works the SAME on macOS & Linux: the JS connect({ protocol: 'v2ray' }) above runs
// as-is (the binary auto-downloads for darwin/linux too). Use this CLI only for WireGuard
// nodes or Node-less environments. Payment (Steps 0-2) is identical. The CLI does NOT bundle
// a tunnel: it shells out to wg-quick, so install wireguard-tools first (apt/brew/pacman).
// CLI: https://github.com/sentinel-official/sentinel-dvpncli

// Install once (Go 1.24+):
//   go install github.com/sentinel-official/sentinel-dvpncli@latest

// 1. Import the SAME wallet whose sentinelAddr (sent1...) you sent to x402.
//    keys add is interactive: prompt 1 = mnemonic, prompt 2 = BIP-39 passphrase.
//    printf feeds both (mnemonic line + blank line = default empty passphrase).
printf '%s\n\n' "$AGENT_MNEMONIC" | sentinel-dvpncli keys add agent --keyring.backend test

// 2. Start the session against the subscription x402 shared with you.
//    --tx.fee-granter-addr = provision.feeGranter → operator pays the P2P gas (you pay 0).
sentinel-dvpncli tx session-start "$NODE_ADDRESS" \
  --subscription-id "$SUBSCRIPTION_ID" \
  --tx.fee-granter-addr "$FEE_GRANTER" \
  --tx.from-name agent --keyring.backend test \
  --rpc.chain-id sentinelhub-2 --output-format json

// 3. Get the session id you just created (session-start does not print it cleanly).
//    AGENT_ADDR = the same sent1... you sent to x402. Newest entry is your session.
SESSION_ID=$(sentinel-dvpncli query sessions \
  --account-addr "$AGENT_ADDR" --subscription-id "$SUBSCRIPTION_ID" \
  --output-format json | jq '.result[-1].id // .response[-1].id')

// 4. Bring the tunnel up (takes the SESSION id, not the subscription id).
//    WireGuard may need sudo to create the interface, same as any wg client.
sentinel-dvpncli connect "$SESSION_ID"
// → tunnel up. Tear down with: sentinel-dvpncli tx session-cancel "$SESSION_ID"
//
// Defaults: --rpc.addrs https://rpc.sentinel.co:443, --rpc.chain-id sentinelhub-2.
// Only exception across all platforms: Fedora (SELinux blocks VPN interfaces).
Under the hood — what x402 does for you
// 1. Agent sends POST /vpn/connect/30days
//    → Server returns HTTP 402 + PAYMENT-REQUIRED header
//
// 2. @x402/fetch reads the 402 response:
//    { scheme: 'exact', network: 'eip155:8453',
//      amount: '1000000', asset: '0x8335...USDC',
//      payTo: '0x605C...85B' }
//
// 3. Signs EIP-3009 transferWithAuthorization
//    (USDC native, no approve needed)
//
// 4. Resends request with PAYMENT-SIGNATURE header
//    → Facilitator verifies + settles USDC on Base
//    → Server provisions agent on Sentinel chain
//    → Returns { subscriptionId, feeGranter, expiresAt }
//
// 5. Agent calls connect({ mnemonic, nodeAddress: provision.nodeAddress, subscriptionId, feeGranter })
//    → SDK validates fee grant on-chain (RPC, ~250ms)
//    → Broadcasts MsgStartSession via broadcastWithFeeGrant
//    → Handshakes with VPN node (WireGuard/V2Ray)
//    → Tunnel established, IP changed
Error handling — fee grant pre-check errors
try {
  const vpn = await connect({
    mnemonic, nodeAddress, subscriptionId, feeGranter,
  });
} catch (err) {
  if (err.code === 'FEE_GRANT_NOT_FOUND') {
    // No grant on-chain — request provisioning from x402 server
  } else if (err.code === 'FEE_GRANT_EXPIRED') {
    // Grant expired — re-purchase via /vpn/connect/*
  } else if (err.code === 'FEE_GRANT_EXHAUSTED') {
    // spend_limit too low (<20k udvpn) — re-provision
  }
  // err.nextAction tells the agent what to do programmatically
  // err.details has { granter, grantee, ... } for debugging
}

Pricing & Discovery

Check pricing (free, no payment)
const res = await fetch('https://x402.sentinel.co/pricing');
const pricing = await res.json();
// {
//   protocol: 'x402', network: 'eip155:8453', asset: 'USDC',
//   payTo: '0xCC689D...',
//   tiers: {
//     '1day':  { price: '$0.033',  endpoint: '/vpn/connect/1day' },
//     '7days': { price: '$0.233',  endpoint: '/vpn/connect/7days' },
//     '30days':{ price: '$1.00',   endpoint: '/vpn/connect/30days' }
//   },
//   sentinelNetwork: 'sentinel', countries: '70+',
//   protocols: ['wireguard', 'v2ray']
// }
// Minimum USDC needed: $0.033 for 1 day tier

What you trust, and what you don't

In the x402 managed flow you trust the operator for one thing: provisioning the subscription and the gas grant. Your keys stay local, the tunnel is direct, and the operator never sees your traffic.

Agent keys never leave the agent

The EIP-3009 authorization is signed locally on the agent. The facilitator only ever receives the signature, never the EVM private key. The agent's Sentinel key stays local too. No private key ever reaches the operator.

Tunnel is always agent-to-node

The WireGuard/V2Ray handshake is direct between the agent and the VPN node. The operator never sees the tunnel credentials, encryption keys, or traffic.

Self-hosted facilitator

The Managed Plan uses our own facilitator: no Coinbase, no third party. It verifies EIP-3009 signatures and settles USDC on Base. Fully auditable on-chain. Open source.

Minimal trust, by design

You trust us only to provision the subscription and the fee grant — the tunnel is still direct and we can't see your traffic. Want zero intermediaries at all? The autonomous flow lets the agent pay nodes directly with no operator.

Live on Base mainnet

USDC payments settled via EIP-3009 transferWithAuthorization: no custom contract, no approve step. Native USDC on Base.

USDC on Base · eip155:8453
0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Base
EVM L2 · EIP-3009 (no approve)
PaymentUSDC transferWithAuth
SettlementSelf-hosted facilitator
Finality~2 seconds
Agent gas$0 (facilitator pays)
StatusLive on mainnet
Solana
SPL Transfer + Memo
PaymentUSDC SPL + memo
SettlementHelius webhook
Finality~400ms
Agent gas~$0.001
StatusComing soon

Endpoints

All /vpn/connect/* routes return HTTP 402 until USDC payment is settled. Free endpoints require no payment.

x402-Protected (payment required)
POST /vpn/connect/1day     $0.033 USDC
POST /vpn/connect/7days    $0.233 USDC
POST /vpn/connect/30days   $1.00  USDC

Body: { "sentinelAddr": "sent1...", "country": "DE" }   country optional — validated before payment
Without payment → 402 + PAYMENT-REQUIRED header
With payment    → 200 + provisioning result
Free endpoints (no payment)
GET /pricing              Tiers, network, asset info
GET /nodes                Plan nodes + live geo (country, city, protocol, byCountry)
GET /health               Server status + uptime
GET /agent/:sentinelAddr  Check subscription status
402 Response — PAYMENT-REQUIRED header (base64 JSON)
{
  "x402Version": 2,
  "accepts": [{
    "scheme": "exact",
    "network": "eip155:8453",
    "amount": "33000",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "payTo": "0x605C...85B",
    "maxTimeoutSeconds": 300
  }]
}

End-to-end: USDC to VPN tunnel

Everything in one script. Pay USDC on Base, get provisioned, connect to VPN, verify IP changed, disconnect. Zero P2P tokens needed.

e2e-vpn.mjs — complete agent script (copy-paste ready)
// npm install @x402/fetch @x402/evm blue-js-sdk viem

import { x402Client, wrapFetchWithPayment } from '@x402/fetch';
import { ExactEvmScheme } from '@x402/evm/exact/client';
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base } from 'viem/chains';
import {
  setup, createWallet, connect, disconnect,
  rpcQueryNodesForPlan, createRpcQueryClientWithFallback,
} from 'blue-js-sdk/ai-path';

// ── Step 0 (optional): pre-fetch V2Ray (no admin). connect({ protocol: 'v2ray' }) auto-fetches it anyway ──
await setup();

// ── Step 1: Create Sentinel wallet (one-time) ──
const wallet = await createWallet();
// SAVE wallet.mnemonic — this is the agent's VPN identity

// ── Step 2: Set up x402 payment client ──
const account = privateKeyToAccount(process.env.EVM_KEY);
const viemClient = createWalletClient({
  account, chain: base, transport: http('https://mainnet.base.org'),
});
const client = new x402Client();
client.register('eip155:8453', new ExactEvmScheme({
  address: account.address,
  signTypedData: (msg) => viemClient.signTypedData(msg),
}));
const paidFetch = wrapFetchWithPayment(fetch, client);

// ── Step 3: Pay USDC on Base via x402 ──
const res = await paidFetch('https://x402.sentinel.co/vpn/connect/30days', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ sentinelAddr: wallet.address, country: 'DE' }), // country optional
});

const provision = await res.json();
// provision.nodeAddress + provision.subscriptionId + provision.feeGranter → all you need

// ── Step 4: Resolve node — use provision.nodeAddress, fall back to live plan query ──
let nodeAddress = provision.nodeAddress;
if (!nodeAddress) {
  const rpc = await createRpcQueryClientWithFallback();
  const { items } = await rpcQueryNodesForPlan(rpc, provision.planId, { status: 1, limit: 50 });
  nodeAddress = items[0]?.address;
}

// ── Step 5: Connect to VPN (0 gas, 0 P2P) ──
// This connect() is cross-platform: protocol: 'v2ray' runs the SAME on Windows, macOS
// and Linux (the binary auto-downloads for each). The native CLI below is only an
// alternative for WireGuard nodes or Node-less environments — not a macOS/Linux requirement:
//   printf '%s\n\n' "$MNEMONIC" | sentinel-dvpncli keys add agent --keyring.backend test
//   sentinel-dvpncli tx session-start <nodeAddress> --subscription-id <id> --tx.fee-granter-addr <feeGranter> --tx.from-name agent --keyring.backend test
//   sessionId=$(sentinel-dvpncli query sessions --account-addr <sent1...> --subscription-id <id> --output-format json | jq '.result[-1].id')
//   sentinel-dvpncli connect <sessionId>   (needs wg-quick on PATH: install wireguard-tools + sudo)
const vpn = await connect({
  mnemonic: wallet.mnemonic,
  nodeAddress,
  subscriptionId: String(provision.subscriptionId),
  feeGranter: provision.feeGranter,
  protocol: 'v2ray',                          // zero admin, zero manual install (Windows)
});

console.log(`VPN active: ${vpn.ip} via ${vpn.protocol}`); // vpn.ip = exit IP, verified through the tunnel

// ── Step 6: Do private work ──
// v2ray runs a SOCKS5 proxy. For the whole device, pass systemProxy: true to connect() above.
// For per-request routing (below): use axios — native fetch() ignores SOCKS5 and exits on the real IP.
import { SocksProxyAgent } from 'socks-proxy-agent';
import axios from 'axios';
const proxy = new SocksProxyAgent(`socks5h://127.0.0.1:${vpn.socksPort}`);
const { data } = await axios.get('https://api.example.com/sensitive', {
  httpAgent: proxy, httpsAgent: proxy, adapter: 'http',
}); // traffic exits on vpn.ip

// ── Step 7: Disconnect (also uses fee grant) ──
await disconnect();
Provisioning response — full shape
{
  "provisioned": true,
  "sentinelAddr": "sent1abc...",
  "days": 30,
  "subscriptionId": 1192288,      ← pass to connect()
  "planId": 42,
  "feeGranter": "sent12e03...",  ← pass to connect()
  "nodeAddress": "sentnode1...",  ← node selected (matches country if requested) — pass to connect()
  "nodeCountry": "Germany",       ← verified location of nodeAddress
  "nodes": ["sentnode1a...", "sentnode1b..."],  ← full list — agent can choose
  "sentinelTxHash": "2C1CFE...",
  "expiresAt": "2026-05-14T...",
  "operatorAddress": "0xCC689D...",
  "instructions": "import { setup, connect } from 'blue-js-sdk/ai-path'; /* setup() is optional on Windows — connect({ protocol: 'v2ray' }) auto-fetches the V2Ray binary (no admin, ~70% of nodes) */ const vpn = await connect({ mnemonic, nodeAddress, subscriptionId, feeGranter, protocol: 'v2ray' }); /* returns { connected, ip, protocol, sessionId, nodeAddress, socksPort }. v2ray runs a SOCKS5 proxy: pass systemProxy: true to encrypt the whole device, or route per-request through vpn.socksPort (dynamic, never hardcode) with axios + SocksProxyAgent + adapter:'http' (native fetch() ignores SOCKS5 and exits on the real IP), or use protocol: 'wireguard' for a kernel full tunnel. */"
}

How zero-gas works

When x402 provisions an agent, it creates an AllowedMsgAllowance fee grant on the Sentinel chain. The operator pays gas for the agent's session operations.

What the grant covers

MsgStartSessionRequest (connect), MsgCancelSessionRequest (disconnect), MsgUpdateSessionRequest (keep-alive). The agent broadcasts these TXs with feeGranter set — chain deducts gas from operator.

Spend limit

5 P2P (5,000,000 udvpn) per agent, enough for ~25 session operations. If exhausted, the SDK throws FEE_GRANT_EXHAUSTED with nextAction: 'request_fee_grant_renewal'.

Expiration

Grant expires 24 hours after the purchased VPN period. If expired, the session ends naturally when the subscription allocation expires. No tokens are lost.

SDK pre-check

Before connecting, the SDK validates the fee grant via RPC (~250ms): checks existence, expiration, spend limit (≥20,000 udvpn), and allowed messages. Fails fast with typed errors if invalid.

Go deeper

The landing page covers the essentials. For production integration, error handling edge cases, and architectural details:

GUIDE.md — Pattern 6

Complete operator-provisioned mode documentation: fee grant pre-check (5-step validation), crash recovery, auto-reconnect dispatch, fee-granted disconnect. The primary reference for x402 integration.

E2E-FLOW.md

Every technical detail of the connection lifecycle: RPC protobuf queries, LCD failover, broadcastWithFeeGrant internals, credential persistence, session allocation tracking.

DECISION-TREE.md

Visual decision tree for connection mode selection. Covers direct pay, plan subscription, operator-provisioned (fee-granted), and error recovery paths.

FAILURES.md

92+ failure patterns with prevention rules. Includes fee grant failures (W4–W7), crash persistence rules, reconnect mode dispatch, and spend limit validation.

Deploy your own x402 VPN server

All operator-specific variables are in .env. Change these to deploy your own instance with your own wallet, plan, and pricing.

server/.env — all configurable variables
# ─── Base Chain (EVM) ───

# Your EVM wallet address — where USDC payments are settled
OPERATOR_ADDRESS=0xYourWalletAddress

# Base mainnet (eip155:8453) or Sepolia testnet (eip155:84532)
BASE_NETWORK=eip155:8453

# Self-hosted facilitator EVM private key
# This wallet needs ETH on Base for gas (~$0.001 per settlement)
# The facilitator verifies EIP-3009 signatures and settles USDC
FACILITATOR_PRIVATE_KEY=0xYourFacilitatorKey
FACILITATOR_PORT=4021

# ─── Sentinel Chain ───

# Operator mnemonic — this wallet must own the plan and have P2P for gas
# Used to: share subscriptions, create fee grants, manage subscription pool
SENTINEL_OPERATOR_MNEMONIC=your twelve word mnemonic phrase here

# Your Sentinel plan ID — create one at sentinel.co or via CLI
# The plan determines which nodes agents can connect to
SENTINEL_PLAN_ID=42

# RPC and LCD endpoints (defaults work for most setups)
SENTINEL_RPC_URL=https://rpc.sentinel.co:443
SENTINEL_LCD_URL=https://lcd.sentinel.co

# ─── Server ───
PORT=4020

OPERATOR_ADDRESS

EVM wallet on Base. This is where USDC payments from agents are settled by the facilitator. Must match the payTo field in x402 payment middleware. Fund with ETH for gas if also used as facilitator.

FACILITATOR_PRIVATE_KEY

EVM private key for the self-hosted facilitator. This wallet verifies EIP-3009 transferWithAuthorization signatures and settles USDC on-chain. Needs ~0.001 ETH per settlement on Base. Can be the same as OPERATOR_ADDRESS.

SENTINEL_OPERATOR_MNEMONIC

12-word BIP39 mnemonic for the Sentinel chain operator wallet. Must own the plan (SENTINEL_PLAN_ID) and hold P2P tokens for gas. Used to create MsgShareSubscription + MsgGrantAllowance for each agent. ~0.01 P2P per provisioning TX.

SENTINEL_PLAN_ID

Your Sentinel plan ID. Determines which VPN nodes agents can access. Create a plan via sentinelcli tx plan create or through the Plan Manager. Link nodes to the plan, then agents connect to those nodes.

sentinel.ts — tunable constants (in source code)
// Bytes to allocate per agent — how much data each agent can use
const SHARE_BYTES = 1_000_000_000;  // 1 GB per agent

// Fee grant budget per agent — covers ~25 session starts/stops
const FEE_GRANT_SPEND_LIMIT = 5_000_000;  // 5 P2P (udvpn)

// Allowed messages for fee grant — what the agent can broadcast
const FEE_GRANT_ALLOWED_MESSAGES = [
  '/sentinel.subscription.v3.MsgStartSessionRequest',
  '/sentinel.session.v3.MsgCancelSessionRequest',
  '/sentinel.session.v3.MsgUpdateSessionRequest',
  '/sentinel.node.v3.MsgStartSessionRequest',
];

// Subscription pool: chain limits 8 allocations per subscription.
// Server auto-creates new subscriptions when pool is exhausted.
// At 1000 agents = ~125 subscriptions. Managed automatically.