Live on Base Mainnet
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.
Currently verified on Windows only. Requires admin privileges for WireGuard. macOS and Linux support is built but untested.
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.
| Endpoint | Cost |
|---|---|
| /vpn/connect/1day | $0.033 |
| /vpn/connect/7days | $0.233 |
| /vpn/connect/30days | $1.00 |
The x402 path is the focus of this site: one paid HTTP request and the agent is provisioned — zero gas, zero P2P tokens. Separately, a fully autonomous agent can skip x402 entirely and pay nodes directly at their per-gigabyte or per-hour rates. Two distinct paths, same network.
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.
POST /vpn/connect/30daysGET /nodes)A separate path that never touches the x402 server. Agent holds P2P tokens, manages its own gas, and pays Sentinel nodes directly at their posted per-GB or per-hour rates. Full autonomy. No intermediary in the payment or the tunnel.
https://x402.sentinel.co with 726 active nodes in the plan and pricing from $0.033 (1 day) to $1.00 (30 days).
The Autonomous path is fully separate: the agent holds its own P2P and pays nodes directly at per-GB / per-hour rates — it never touches the x402 server.
Both paths use the same Sentinel network and the same WireGuard/V2Ray tunnels.
Live at https://x402.sentinel.co. Agent sends one HTTP request. Payment, settlement, subscription provisioning, and fee granting all happen automatically.
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).
@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.
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.
AutomaticServer 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.
A separate path for agents that want zero intermediaries. The agent holds its own P2P tokens, pays Sentinel nodes directly at their posted per-gigabyte or per-hour rates, and manages its own gas. The x402 server plays no part in this flow.
The agent market-buys P2P with USDT on MEXC spot by API and withdraws the tokens to its own Sentinel wallet. No human, no UI — the full recipe is below.
MEXC · P2PAgent creates a Sentinel wallet and holds the mnemonic itself. It pays its own gas (~0.01 P2P per transaction) from the P2P it bought in step zero.
AgentAgent queries the live Sentinel node list and picks by country, speed, and price. Every node posts its own per-gigabyte and per-hour rates — the agent pays exactly that, no plan, no subscription tier.
Agent → ChainAgent starts the session and pays the node in P2P at its posted rate, then handshakes directly over WireGuard/V2Ray. No middleman in the payment or the tunnel — the x402 server is never contacted.
Agent ↔ NodeNo human, no UI. The agent market-buys P2P with USDT on MEXC spot — the P2PUSDT pair is live (maker fee 0%, taker 0.05%, minimum market order 1 USDT) — then withdraws the tokens to its own Sentinel wallet. Three signed HTTP calls, no SDK required. Auth is an X-MEXC-APIKEY header plus an HMAC-SHA256 signature of the query string.
import { createHmac } from 'node:crypto';
const KEY = process.env.MEXC_API_KEY; // dashboard key with Spot Trade + Withdraw scopes
const SECRET = process.env.MEXC_API_SECRET;
async function mexc(method, path, params = {}) {
const qs = new URLSearchParams({ ...params, timestamp: Date.now() }).toString();
const sig = createHmac('sha256', SECRET).update(qs).digest('hex');
const res = await fetch(`https://api.mexc.com${path}?${qs}&signature=${sig}`, {
method, headers: { 'X-MEXC-APIKEY': KEY },
});
return res.json();
}
// 1. Market-buy P2P with USDT — spend exactly 10 USDT (min 1, max 100k per order)
await mexc('POST', '/api/v3/order', {
symbol: 'P2PUSDT', side: 'BUY', type: 'MARKET', quoteOrderQty: '10',
});
// 2. Read the filled P2P balance
const { balances } = await mexc('GET', '/api/v3/account');
const amount = balances.find(b => b.asset === 'P2P').free;
// 3. Withdraw to the agent's own Sentinel wallet (wallet.address = 'sent1...')
const { networkList } = (await mexc('GET', '/api/v3/capital/config/getall'))
.find(c => c.coin === 'P2P');
await mexc('POST', '/api/v3/capital/withdraw', {
coin: 'P2P', netWork: networkList[0].netWork,
address: wallet.address, amount,
});
// P2P lands on-chain in the agent's wallet — gas + direct node payments covered.
// At ~$0.00008/P2P, 10 USDT buys ~125,000 P2P: years of gas, weeks of bandwidth.
One-time human setup: create the MEXC API key with Spot Trade + Withdraw permissions and allowlist the agent's sent1... withdrawal address. From then on the agent is self-funding — it can top up its own P2P whenever the balance runs low.
Let the x402 server handle everything with @x402/fetch, or go fully autonomous with blue-js-sdk — both live now. Both give you private internet access through the same Sentinel network.
npm install @x402/fetch @x402/evm blue-js-sdk viem
// 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}`);
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';
// Fresh machine? setup() auto-downloads V2Ray (no admin) + checks WireGuard
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);
// 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...',
// }
// 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
});
// All traffic now routes through encrypted P2P tunnel
console.log(`VPN active: ${vpn.ip} via ${vpn.protocol}`);
// { connected: true, ip: '104.234.x.x', protocol: 'wireguard',
// sessionId: '39269345', nodeAddress: 'sentnode1...' }
// Disconnect also uses fee grant — 0 tokens needed
await disconnect();
// 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
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
}
npm install blue-js-sdk
import { createWallet, connectDirect } from 'blue-js-sdk';
// Agent already holds P2P tokens (bought on MEXC by API — see Autonomous Path above)
const { wallet } = await createWallet(process.env.SENTINEL_MNEMONIC);
// Pick any Sentinel node — pay directly in P2P, full autonomy
const vpn = await connectDirect({
wallet,
country: 'US',
protocol: 'wireguard',
});
// {
// connected: true,
// ip: '185.xxx.xxx.xxx',
// country: 'US',
// node: 'sentnode1...',
// sessionId: '12345678',
// protocol: 'wireguard'
// }
// The agent controls the full lifecycle:
//
// 1. Wallet: Agent creates & holds Sentinel keys (mnemonic)
// 2. Tokens: Agent holds P2P tokens (bought on MEXC by API)
// 3. Gas: Agent pays Sentinel gas (~0.01 P2P per transaction)
// 4. Node selection: Agent queries Sentinel nodes, picks by country/speed/price
// 5. Session: Agent starts/ends sessions, pays node at its per-GB / per-hour rate
// 6. Tunnel: Agent handshakes directly with VPN node (WireGuard/V2Ray)
//
// Zero middlemen. Zero trust required. Full autonomy. No x402 involved.
// All of this ships in blue-js-sdk today.
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
Both flows are designed so trust is unnecessary. Your keys, your tunnel, your traffic. The only difference is who manages the gas.
Autonomous: Sentinel mnemonic stays local. Managed: EIP-3009 signed locally — facilitator only receives the signature. In both flows, all private keys remain on the agent.
WireGuard/V2Ray handshake is direct between the agent and the VPN node in both flows. We never see the tunnel credentials, encryption keys, or traffic. Ever.
Managed Plan uses our own facilitator — no Coinbase, no third party. Verifies EIP-3009 signatures and settles USDC on Base. Fully auditable on-chain. Open source.
Autonomous: Agent pays nodes, no middleman at all. Managed: Trust us to provision the subscription, but the tunnel is still direct and we can't see traffic. Agent can switch flows at any time.
USDC payments settled via EIP-3009 transferWithAuthorization — no custom contract, no approve step. Native USDC on Base.
All /vpn/connect/* routes return HTTP 402 until USDC payment is settled. Free endpoints require no payment.
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
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
{
"x402Version": 2,
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"amount": "33000",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"payTo": "0x605C...85B",
"maxTimeoutSeconds": 300
}]
}
Everything in one script. Pay USDC on Base, get provisioned, connect to VPN, verify IP changed, disconnect. Zero P2P tokens needed.
// 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: Fresh machine? Auto-install tunnel binaries (V2Ray needs no admin) ──
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) ──
const vpn = await connect({
mnemonic: wallet.mnemonic,
nodeAddress,
subscriptionId: String(provision.subscriptionId),
feeGranter: provision.feeGranter,
});
console.log(`VPN active: ${vpn.ip} via ${vpn.protocol}`);
// All traffic now through encrypted P2P tunnel
// ── Step 6: Do private work ──
const data = await fetch('https://api.example.com/sensitive');
// ── Step 7: Disconnect (also uses fee grant) ──
await disconnect();
{
"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'; await setup(); /* installs V2Ray automatically if no tunnel binary is present */ await connect({ mnemonic, nodeAddress, subscriptionId, feeGranter })"
}
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.
MsgStartSessionRequest (connect), MsgCancelSessionRequest (disconnect), MsgUpdateSessionRequest (keep-alive). The agent broadcasts these TXs with feeGranter set — chain deducts gas from operator.
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'.
Grant expires 24 hours after the purchased VPN period. If expired, the session ends naturally when the subscription allocation expires. No tokens are lost.
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.
The landing page covers the essentials. For production integration, error handling edge cases, and architectural details:
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.
Every technical detail of the connection lifecycle: RPC protobuf queries, LCD failover, broadcastWithFeeGrant internals, credential persistence, session allocation tracking.
Visual decision tree for connection mode selection. Covers direct pay, plan subscription, operator-provisioned (fee-granted), and error recovery paths.
92+ failure patterns with prevention rules. Includes fee grant failures (W4–W7), crash persistence rules, reconnect mode dispatch, and spend limit validation.
All operator-specific variables are in .env. Change these to deploy your own instance with your own wallet, plan, and pricing.
# ─── 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
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.
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.
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.
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.
// 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.