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 soon
Linux soon

Currently verified on Windows only. Requires admin privileges for WireGuard. macOS and Linux support is built but untested.

agent dVPN — use cases 05
Agent privacy Mask the agent's IP — no request traces back to its operator. 01
Geo-restricted exchanges Trade on Hyperliquid and region-locked venues from anywhere. 02
Geo-locked APIs & data Fetch sources only served in certain countries. 03
Anonymity at scale Rotate exits across 700+ nodes 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

Choose your level of autonomy

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.

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 choice726 nodes in our plan (GET /nodes)
TrustMinimal — tunnel still direct
Best forQuick integration
Live Now

Autonomous — agent pays nodes directly

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.

  • Agent funds itself with P2P (see Autonomous Path below)
  • Agent creates Sentinel wallet, holds own keys
  • Agent queries nodes, picks by country/speed/price
  • Agent starts session, pays node in P2P per GB or per hour
  • Handshakes with VPN node (WireGuard/V2Ray)
  • Full control — no middleman, no plan needed
GasAgent pays (~0.01 P2P/tx)
Node choiceAny Sentinel node
PricingNode's posted rate (per GB / per hour)
TrustZero — agent controls everything
Current status: Both paths are live. The x402 Managed Plan is the primary integration path — running now at 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.

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

Fully autonomous — no x402 involved

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.

0

Agent funds itself with P2P

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 · P2P
1

Own wallet, own keys

Agent 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.

Agent
2

Pick any node on the network

Agent 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 → Chain
3

Pay the node, handshake directly

Agent 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 ↔ Node

Step zero in practice — agent buys P2P on MEXC, fully by API

No 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.

MEXC logo Buy P2P on MEXC Live spot market · P2P/USDT · mexc.com/exchange/P2P_USDT
MEXC Spot API v3 — market-buy P2P, withdraw to the agent's Sentinel wallet
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.

Two integration paths

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.

● 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';

// 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);
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: Connect to VPN — use provisioned credentials
// 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();
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
}

● Autonomous Flow — Live Now

Install — Autonomous flow (agent manages P2P + gas)
npm install blue-js-sdk
Connect to VPN — agent pays node directly
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'
// }
Autonomous flow — what the agent manages
// 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.

Both Flows

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

Trust nothing. Verify everything.

Both flows are designed so trust is unnecessary. Your keys, your tunnel, your traffic. The only difference is who manages the gas.

Agent keys never leave the agent

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.

Tunnel is always agent-to-node

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.

Self-hosted facilitator

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 = zero trust, Managed = minimal trust

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.

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: 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();
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'; await setup(); /* installs V2Ray automatically if no tunnel binary is present */ await connect({ mnemonic, nodeAddress, subscriptionId, feeGranter })"
}

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.